2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
4 * Copyright © 2000 Keith Packard
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 #include <sys/utsname.h>
32 * POSIX has broken stdio so that getc must do thread-safe locking,
33 * this is a serious performance problem for applications doing large
34 * amounts of IO with getc (as is done here). If available, use
35 * the getc_unlocked varient instead.
38 #if defined(getc_unlocked) || defined(_IO_getc_unlocked)
39 #define GETC(f) getc_unlocked(f)
40 #define PUTC(c,f) putc_unlocked(c,f)
42 #define GETC(f) getc(f)
43 #define PUTC(c,f) putc(c,f)
46 #define FC_DBG_CACHE_REF 1024
53 FcCacheReadString (FILE *f
, FcChar8
*dest
, int len
)
61 while ((c
= GETC (f
)) != EOF
)
73 while ((c
= GETC (f
)) != EOF
)
88 FcChar8
*new = malloc (size
* 2); /* freed in caller */
91 memcpy (new, d
, size
);
108 FcCacheReadUlong (FILE *f
, unsigned long *dest
)
113 while ((c
= GETC (f
)) != EOF
)
123 if (c
== EOF
|| isspace (c
))
127 t
= t
* 10 + (c
- '0');
135 FcCacheReadInt (FILE *f
, int *dest
)
140 ret
= FcCacheReadUlong (f
, &t
);
147 FcCacheReadTime (FILE *f
, time_t *dest
)
152 ret
= FcCacheReadUlong (f
, &t
);
159 FcCacheWriteChars (FILE *f
, const FcChar8
*chars
)
162 while ((c
= *chars
++))
167 if (PUTC ('\\', f
) == EOF
)
171 if (PUTC (c
, f
) == EOF
)
179 FcCacheWriteString (FILE *f
, const FcChar8
*string
)
182 if (PUTC ('"', f
) == EOF
)
184 if (!FcCacheWriteChars (f
, string
))
186 if (PUTC ('"', f
) == EOF
)
192 FcCacheWritePath (FILE *f
, const FcChar8
*dir
, const FcChar8
*file
)
194 if (PUTC ('"', f
) == EOF
)
197 if (!FcCacheWriteChars (f
, dir
))
201 dir
[strlen((const char *) dir
) - 1] != '/' &&
202 dir
[strlen((const char *) dir
) - 1] != '\\')
204 if (!FcCacheWriteChars (f
, "\\"))
208 if (dir
&& dir
[strlen((const char *) dir
) - 1] != '/')
209 if (PUTC ('/', f
) == EOF
)
212 if (!FcCacheWriteChars (f
, file
))
214 if (PUTC ('"', f
) == EOF
)
220 FcCacheWriteUlong (FILE *f
, unsigned long t
)
223 unsigned long temp
, digit
;
236 if (PUTC ((char) digit
+ '0', f
) == EOF
)
238 temp
= temp
- pow
* digit
;
245 FcCacheWriteInt (FILE *f
, int i
)
247 return FcCacheWriteUlong (f
, (unsigned long) i
);
251 FcCacheWriteTime (FILE *f
, time_t t
)
253 return FcCacheWriteUlong (f
, (unsigned long) t
);
257 FcCacheReadString2 (int fd
, FcChar8
*dest
, int len
)
270 while (read (fd
, &c
, 1) == 1)
297 FcCacheWriteString2 (int fd
, const FcChar8
*chars
)
299 if (write (fd
, chars
, strlen(chars
)+1) != strlen(chars
)+1)
305 FcCacheFontSetAdd (FcFontSet
*set
,
313 FcChar8 path_buf
[8192], *path
;
315 FcBool ret
= FcFalse
;
320 len
= (dir_len
+ 1 + strlen ((const char *) file
) + 1);
321 if (len
> sizeof (path_buf
))
323 path
= malloc (len
); /* freed down below */
327 strncpy ((char *) path
, (const char *) dir
, dir_len
);
329 if (dir
[dir_len
- 1] != '/' && dir
[dir_len
- 1] != '\\' )
330 path
[dir_len
++] = '\\';
332 if (dir
[dir_len
- 1] != '/')
333 path
[dir_len
++] = '/';
335 strcpy ((char *) path
+ dir_len
, (const char *) file
);
336 if (config
&& !FcConfigAcceptFilename (config
, path
))
338 else if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
340 if (FcDebug () & FC_DBG_CACHEV
)
341 printf (" dir cache dir \"%s\"\n", path
);
342 ret
= FcStrSetAdd (dirs
, path
);
344 else if (!FcStrCmp (name
, FC_FONT_FILE_INVALID
))
350 font
= FcNameParse (name
);
355 if (FcDebug () & FC_DBG_CACHEV
)
356 printf (" dir cache file \"%s\"\n", file
);
357 ret
= FcPatternAddString (font
, FC_FILE
, path
);
359 * Make sure the pattern has the file name as well as
360 * already containing at least one family name.
363 FcPatternGetString (font
, FC_FAMILY
, 0, &family
) == FcResultMatch
&&
364 (!config
|| FcConfigAcceptFont (config
, font
)))
366 frozen
= FcPatternFreeze (font
);
369 ret
= FcFontSetAdd (set
, frozen
);
371 FcPatternDestroy (font
);
374 if (path
!= path_buf
) free (path
);
380 FcCacheHash (const FcChar8
*string
, int len
)
385 while (len
-- && (c
= *string
++))
391 * Verify the saved timestamp for a file
394 FcGlobalCacheCheckTime (const FcChar8
*file
, FcGlobalCacheInfo
*info
)
398 if (stat ((char *) file
, &statb
) < 0)
400 if (FcDebug () & FC_DBG_CACHE
)
401 printf (" file %s missing\n", file
);
404 if (statb
.st_mtime
!= info
->time
)
406 if (FcDebug () & FC_DBG_CACHE
)
407 printf (" timestamp mismatch (was %d is %d)\n",
408 (int) info
->time
, (int) statb
.st_mtime
);
415 FcGlobalCacheReferenced (FcGlobalCache
*cache
,
416 FcGlobalCacheInfo
*info
)
418 if (!info
->referenced
)
420 info
->referenced
= FcTrue
;
422 if (FcDebug () & FC_DBG_CACHE_REF
)
423 printf ("Reference %d %s\n", cache
->referenced
, info
->file
);
428 * Break a path into dir/base elements and compute the base hash
429 * and the dir length. This is shared between the functions
430 * which walk the file caches
433 typedef struct _FcFilePathInfo
{
437 unsigned int base_hash
;
440 static FcFilePathInfo
441 FcFilePathInfoGet (const FcChar8
*path
)
446 slash
= FcStrLastSlash (path
);
450 i
.dir_len
= slash
- path
;
457 i
.dir
= (const FcChar8
*) ".";
461 i
.base_hash
= FcCacheHash (i
.base
, -1);
466 FcGlobalCacheDirGet (FcGlobalCache
*cache
,
469 FcBool create_missing
)
471 unsigned int hash
= FcCacheHash (dir
, len
);
472 FcGlobalCacheDir
*d
, **prev
;
474 for (prev
= &cache
->ents
[hash
% FC_GLOBAL_CACHE_DIR_HASH_SIZE
];
476 prev
= &(*prev
)->next
)
478 if (d
->info
.hash
== hash
&& d
->len
== len
&&
479 !strncmp ((const char *) d
->info
.file
,
480 (const char *) dir
, len
))
488 d
= malloc (sizeof (FcGlobalCacheDir
) + len
+ 1);
491 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCacheDir
) + len
+ 1);
495 d
->info
.file
= (FcChar8
*) (d
+ 1);
496 strncpy ((char *) d
->info
.file
, (const char *) dir
, len
);
497 d
->info
.file
[len
] = '\0';
499 d
->info
.referenced
= FcFalse
;
501 for (i
= 0; i
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; i
++)
508 static FcGlobalCacheInfo
*
509 FcGlobalCacheDirAdd (FcGlobalCache
*cache
,
513 FcBool create_missing
)
517 FcGlobalCacheSubdir
*subdir
;
518 FcGlobalCacheDir
*parent
;
520 i
= FcFilePathInfoGet (dir
);
521 parent
= FcGlobalCacheDirGet (cache
, i
.dir
, i
.dir_len
, create_missing
);
523 * Tricky here -- directories containing fonts.cache-1 files
524 * need entries only when the parent doesn't have a cache file.
525 * That is, when the parent already exists in the cache, is
526 * referenced and has a "real" timestamp. The time of 0 is
527 * special and marks directories which got stuck in the
528 * global cache for this very reason. Yes, it could
529 * use a separate boolean field, and probably should.
531 if (!parent
|| (!create_missing
&&
532 (!parent
->info
.referenced
||
533 (parent
->info
.time
== 0))))
536 * Add this directory to the cache
538 d
= FcGlobalCacheDirGet (cache
, dir
, strlen ((const char *) dir
), FcTrue
);
543 * Add this directory to the subdirectory list of the parent
545 subdir
= malloc (sizeof (FcGlobalCacheSubdir
));
548 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCacheSubdir
));
550 subdir
->next
= parent
->subdirs
;
551 parent
->subdirs
= subdir
;
556 FcGlobalCacheDirDestroy (FcGlobalCacheDir
*d
)
558 FcGlobalCacheFile
*f
, *next
;
560 FcGlobalCacheSubdir
*s
, *nexts
;
562 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
563 for (f
= d
->ents
[h
]; f
; f
= next
)
566 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheFile
) +
567 strlen ((char *) f
->info
.file
) + 1 +
568 strlen ((char *) f
->name
) + 1);
571 for (s
= d
->subdirs
; s
; s
= nexts
)
574 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheSubdir
));
577 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheDir
) + d
->len
+ 1);
582 * If the parent is in the global cache and referenced, add
583 * an entry for 'dir' to the global cache. This is used
584 * for directories with fonts.cache files
588 FcGlobalCacheReferenceSubdir (FcGlobalCache
*cache
,
591 FcGlobalCacheInfo
*info
;
592 info
= FcGlobalCacheDirAdd (cache
, dir
, 0, FcFalse
, FcFalse
);
593 if (info
&& !info
->referenced
)
595 info
->referenced
= FcTrue
;
601 * Check to see if the global cache contains valid data for 'dir'.
602 * If so, scan the global cache for files and directories in 'dir'.
603 * else, return False.
606 FcGlobalCacheScanDir (FcFontSet
*set
,
608 FcGlobalCache
*cache
,
612 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, dir
,
613 strlen ((const char *) dir
),
615 FcGlobalCacheFile
*f
;
618 FcGlobalCacheSubdir
*subdir
;
619 FcBool any_in_cache
= FcFalse
;
621 if (FcDebug() & FC_DBG_CACHE
)
622 printf ("FcGlobalCacheScanDir %s\n", dir
);
626 if (FcDebug () & FC_DBG_CACHE
)
627 printf ("\tNo dir cache entry\n");
632 * See if the timestamp recorded in the global cache
633 * matches the directory time, if not, return False
635 if (!FcGlobalCacheCheckTime (d
->info
.file
, &d
->info
))
637 if (FcDebug () & FC_DBG_CACHE
)
638 printf ("\tdir cache entry time mismatch\n");
643 * Add files from 'dir' to the fontset
645 dir_len
= strlen ((const char *) dir
);
646 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
647 for (f
= d
->ents
[h
]; f
; f
= f
->next
)
649 if (FcDebug() & FC_DBG_CACHEV
)
650 printf ("FcGlobalCacheScanDir add file %s\n", f
->info
.file
);
651 any_in_cache
= FcTrue
;
652 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
653 f
->info
.file
, f
->name
, config
))
655 cache
->broken
= FcTrue
;
658 FcGlobalCacheReferenced (cache
, &f
->info
);
661 * Add directories in 'dir' to 'dirs'
663 for (subdir
= d
->subdirs
; subdir
; subdir
= subdir
->next
)
665 FcFilePathInfo info
= FcFilePathInfoGet (subdir
->ent
->info
.file
);
667 any_in_cache
= FcTrue
;
668 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
669 info
.base
, FC_FONT_FILE_DIR
, config
))
671 cache
->broken
= FcTrue
;
674 FcGlobalCacheReferenced (cache
, &subdir
->ent
->info
);
677 FcGlobalCacheReferenced (cache
, &d
->info
);
680 * To recover from a bug in previous versions of fontconfig,
681 * return FcFalse if no entries in the cache were found
682 * for this directory. This will cause any empty directories
683 * to get rescanned every time fontconfig is initialized. This
684 * might get removed at some point when the older cache files are
691 * Locate the cache entry for a particular file
694 FcGlobalCacheFileGet (FcGlobalCache
*cache
,
699 FcFilePathInfo i
= FcFilePathInfoGet (file
);
700 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
702 FcGlobalCacheFile
*f
, *match
= 0;
707 for (f
= d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
]; f
; f
= f
->next
)
709 if (f
->info
.hash
== i
.base_hash
&&
710 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
724 * Add a file entry to the cache
726 static FcGlobalCacheInfo
*
727 FcGlobalCacheFileAdd (FcGlobalCache
*cache
,
734 FcFilePathInfo i
= FcFilePathInfoGet (path
);
735 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
737 FcGlobalCacheFile
*f
, **prev
;
742 for (prev
= &d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
];
744 prev
= &(*prev
)->next
)
746 if (f
->info
.hash
== i
.base_hash
&&
748 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
759 if (f
->info
.referenced
)
762 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheFile
) +
763 strlen ((char *) f
->info
.file
) + 1 +
764 strlen ((char *) f
->name
) + 1);
767 size
= (sizeof (FcGlobalCacheFile
) +
768 strlen ((char *) i
.base
) + 1 +
769 strlen ((char *) name
) + 1);
773 FcMemAlloc (FC_MEM_CACHE
, size
);
776 f
->info
.hash
= i
.base_hash
;
777 f
->info
.file
= (FcChar8
*) (f
+ 1);
779 f
->info
.referenced
= FcFalse
;
781 f
->name
= f
->info
.file
+ strlen ((char *) i
.base
) + 1;
782 strcpy ((char *) f
->info
.file
, (const char *) i
.base
);
783 strcpy ((char *) f
->name
, (const char *) name
);
788 FcGlobalCacheCreate (void)
790 FcGlobalCache
*cache
;
793 cache
= malloc (sizeof (FcGlobalCache
));
796 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
797 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
800 cache
->referenced
= 0;
801 cache
->updated
= FcFalse
;
802 cache
->broken
= FcFalse
;
807 FcGlobalCacheDestroy (FcGlobalCache
*cache
)
809 FcGlobalCacheDir
*d
, *next
;
812 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
814 for (d
= cache
->ents
[h
]; d
; d
= next
)
817 FcGlobalCacheDirDestroy (d
);
820 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
825 * Cache file syntax is quite simple:
827 * "file_name" id time "font_name" \n
831 FcGlobalCacheLoad (FcGlobalCache
*cache
,
832 const FcChar8
*cache_file
)
835 FcChar8 file_buf
[8192], *file
;
838 FcChar8 name_buf
[8192], *name
;
839 FcGlobalCacheInfo
*info
;
841 f
= fopen ((char *) cache_file
, "r");
845 cache
->updated
= FcFalse
;
848 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
849 FcCacheReadInt (f
, &id
) &&
850 FcCacheReadTime (f
, &time
) &&
851 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
853 if (FcDebug () & FC_DBG_CACHEV
)
854 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file
, name
);
855 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
856 info
= FcGlobalCacheDirAdd (cache
, file
, time
, FcFalse
, FcTrue
);
858 info
= FcGlobalCacheFileAdd (cache
, file
, id
, time
, name
, FcFalse
);
860 cache
->broken
= FcTrue
;
863 if (FcDebug () & FC_DBG_CACHE_REF
)
864 printf ("FcGlobalCacheLoad entry %d %s\n",
865 cache
->entries
, file
);
866 if (file
!= file_buf
)
868 if (name
!= name_buf
)
873 if (file
&& file
!= file_buf
)
875 if (name
&& name
!= name_buf
)
881 FcGlobalCacheUpdate (FcGlobalCache
*cache
,
886 const FcChar8
*match
;
888 FcGlobalCacheInfo
*info
;
892 if (stat ((char *) file
, &statb
) < 0)
894 if (S_ISDIR (statb
.st_mode
))
895 info
= FcGlobalCacheDirAdd (cache
, file
, statb
.st_mtime
,
898 info
= FcGlobalCacheFileAdd (cache
, file
, id
, statb
.st_mtime
,
902 FcGlobalCacheReferenced (cache
, info
);
903 cache
->updated
= FcTrue
;
906 cache
->broken
= FcTrue
;
911 FcGlobalCacheSave (FcGlobalCache
*cache
,
912 const FcChar8
*cache_file
)
915 int dir_hash
, file_hash
;
916 FcGlobalCacheDir
*dir
;
917 FcGlobalCacheFile
*file
;
920 if (!cache
->updated
&& cache
->referenced
== cache
->entries
)
926 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
927 /* Set-UID programs can't safely update the cache */
928 if (getuid () != geteuid ())
932 atomic
= FcAtomicCreate (cache_file
);
935 if (!FcAtomicLock (atomic
))
937 f
= fopen ((char *) FcAtomicNewFile(atomic
), "w");
941 for (dir_hash
= 0; dir_hash
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; dir_hash
++)
943 for (dir
= cache
->ents
[dir_hash
]; dir
; dir
= dir
->next
)
945 if (!dir
->info
.referenced
)
947 if (!FcCacheWriteString (f
, dir
->info
.file
))
949 if (PUTC (' ', f
) == EOF
)
951 if (!FcCacheWriteInt (f
, 0))
953 if (PUTC (' ', f
) == EOF
)
955 if (!FcCacheWriteTime (f
, dir
->info
.time
))
957 if (PUTC (' ', f
) == EOF
)
959 if (!FcCacheWriteString (f
, (FcChar8
*) FC_FONT_FILE_DIR
))
961 if (PUTC ('\n', f
) == EOF
)
964 for (file_hash
= 0; file_hash
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; file_hash
++)
966 for (file
= dir
->ents
[file_hash
]; file
; file
= file
->next
)
968 if (!file
->info
.referenced
)
970 if (!FcCacheWritePath (f
, dir
->info
.file
, file
->info
.file
))
972 if (PUTC (' ', f
) == EOF
)
974 if (!FcCacheWriteInt (f
, file
->id
< 0 ? 0 : file
->id
))
976 if (PUTC (' ', f
) == EOF
)
978 if (!FcCacheWriteTime (f
, file
->info
.time
))
980 if (PUTC (' ', f
) == EOF
)
982 if (!FcCacheWriteString (f
, file
->name
))
984 if (PUTC ('\n', f
) == EOF
)
991 if (fclose (f
) == EOF
)
994 if (!FcAtomicReplaceOrig (atomic
))
997 FcAtomicUnlock (atomic
);
998 FcAtomicDestroy (atomic
);
1000 cache
->updated
= FcFalse
;
1006 FcAtomicDeleteNew (atomic
);
1008 FcAtomicUnlock (atomic
);
1010 FcAtomicDestroy (atomic
);
1016 * Find the next presumably-mmapable offset after the current file
1020 FcCacheNextOffset(off_t w
)
1022 if (w
% PAGESIZE
== 0)
1025 return ((w
/ PAGESIZE
)+1)*PAGESIZE
;
1028 /* get the current arch name */
1029 /* caller is responsible for freeing returned pointer */
1031 FcCacheGetCurrentArch (void)
1034 char * current_arch_machine_name
;
1036 if (uname(&b
) == -1)
1038 current_arch_machine_name
= strdup(b
.machine
);
1039 /* if (getenv ("FAKE_ARCH")) // testing purposes
1040 current_arch_machine_name = strdup(getenv("FAKE_ARCH")); */
1041 return current_arch_machine_name
;
1044 /* return the address of the segment for the provided arch,
1045 * or -1 if arch not found */
1047 FcCacheSkipToArch (int fd
, const char * arch
)
1049 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
1050 off_t current_arch_start
= 0;
1052 /* skip arches that are not the current arch */
1057 lseek (fd
, current_arch_start
, SEEK_SET
);
1058 if (FcCacheReadString2 (fd
, candidate_arch_machine_name
,
1059 sizeof (candidate_arch_machine_name
)) == 0)
1061 if (FcCacheReadString2 (fd
, bytes_to_skip
, 7) == 0)
1063 bs
= a64l(bytes_to_skip
);
1067 if (strcmp (candidate_arch_machine_name
, arch
)==0)
1069 current_arch_start
+= bs
;
1072 if (strcmp (candidate_arch_machine_name
, arch
)!=0)
1075 return current_arch_start
;
1078 /* Cuts out the segment at the file pointer (moves everything else
1079 * down to cover it), and leaves the file pointer at the end of the
1081 #define BUF_SIZE 8192
1084 FcCacheMoveDown (int fd
, off_t start
)
1086 char * buf
= malloc (BUF_SIZE
);
1087 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
1089 int c
, bytes_skipped
;
1094 lseek (fd
, start
, SEEK_SET
);
1095 if (FcCacheReadString2 (fd
, candidate_arch_machine_name
,
1096 sizeof (candidate_arch_machine_name
)) == 0)
1098 if (FcCacheReadString2 (fd
, bytes_to_skip
, 7) == 0)
1101 bs
= a64l(bytes_to_skip
);
1108 lseek (fd
, start
+bs
+bytes_skipped
, SEEK_SET
);
1109 if ((c
= read (fd
, buf
, BUF_SIZE
)) <= 0)
1111 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
1112 if (write (fd
, buf
, c
) < 0)
1117 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
1129 FcDirCacheValid (const FcChar8
*dir
)
1131 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1132 struct stat file_stat
, dir_stat
;
1134 if (stat ((char *) dir
, &dir_stat
) < 0)
1136 FcStrFree (cache_file
);
1139 if (stat ((char *) cache_file
, &file_stat
) < 0)
1141 FcStrFree (cache_file
);
1144 FcStrFree (cache_file
);
1146 * If the directory has been modified more recently than
1147 * the cache file, the cache is not valid
1149 if (dir_stat
.st_mtime
- file_stat
.st_mtime
> 0)
1155 FcCacheReadDirs (FcConfig
* config
, FcGlobalCache
* cache
,
1156 FcStrList
*list
, FcFontSet
* set
)
1162 FcChar8
*file
, *base
;
1168 * Now scan all of the directories into separate databases
1169 * and write out the results
1171 while ((dir
= FcStrListNext (list
)))
1174 file
= (FcChar8
*) malloc (strlen ((char *) dir
) + 1 + FC_MAX_FILE_LEN
+ 1);
1178 strcpy ((char *) file
, (char *) dir
);
1179 strcat ((char *) file
, "/");
1180 base
= file
+ strlen ((char *) file
);
1182 subdirs
= FcStrSetCreate ();
1185 fprintf (stderr
, "Can't create directory set\n");
1191 if (access ((char *) dir
, X_OK
) < 0)
1199 fprintf (stderr
, "\"%s\": ", dir
);
1203 FcStrSetDestroy (subdirs
);
1207 if (stat ((char *) dir
, &statb
) == -1)
1209 fprintf (stderr
, "\"%s\": ", dir
);
1211 FcStrSetDestroy (subdirs
);
1216 if (!S_ISDIR (statb
.st_mode
))
1218 fprintf (stderr
, "\"%s\": not a directory, skipping\n", dir
);
1219 FcStrSetDestroy (subdirs
);
1223 d
= opendir ((char *) dir
);
1226 FcStrSetDestroy (subdirs
);
1230 while ((e
= readdir (d
)))
1232 if (e
->d_name
[0] != '.' && strlen (e
->d_name
) < FC_MAX_FILE_LEN
)
1234 strcpy ((char *) base
, (char *) e
->d_name
);
1235 if (FcFileIsDir (file
) && !FcStrSetAdd (subdirs
, file
))
1240 if (FcDirCacheValid (dir
))
1242 FcDirCacheRead (set
, dir
);
1246 if (FcDebug () & FC_DBG_FONTSET
)
1247 printf ("scan dir %s\n", dir
);
1248 FcDirScanConfig (set
, subdirs
, cache
,
1249 config
->blanks
, dir
, FcFalse
, config
);
1251 sublist
= FcStrListCreate (subdirs
);
1252 FcStrSetDestroy (subdirs
);
1255 fprintf (stderr
, "Can't create subdir list in \"%s\"\n", dir
);
1260 ret
+= FcCacheReadDirs (config
, cache
, sublist
, set
);
1263 FcStrListDone (list
);
1268 FcCacheRead (FcConfig
*config
, FcGlobalCache
* cache
)
1270 FcFontSet
* s
= FcFontSetCreate();
1277 if (FcCacheReadDirs (config
, cache
, FcConfigGetConfigDirs (config
), s
))
1283 FcFontSetDestroy (s
);
1287 /* read serialized state from the cache file */
1289 FcDirCacheRead (FcFontSet
* set
, const FcChar8
*dir
)
1291 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1294 void * current_dir_block
;
1295 char * current_arch_machine_name
;
1296 char candidate_arch_machine_name
[64], bytes_in_block
[7];
1297 off_t current_arch_start
= 0;
1304 current_arch_machine_name
= FcCacheGetCurrentArch();
1305 fd
= open(cache_file
, O_RDONLY
);
1309 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
1310 if (current_arch_start
< 0)
1313 lseek (fd
, current_arch_start
, SEEK_SET
);
1314 if (FcCacheReadString2 (fd
, candidate_arch_machine_name
,
1315 sizeof (candidate_arch_machine_name
)) == 0)
1317 if (FcCacheReadString2 (fd
, bytes_in_block
, 7) == 0)
1320 // sanity check for endianness issues
1321 read(fd
, &metadata
, sizeof(FcCache
));
1322 if (metadata
.magic
!= FC_CACHE_MAGIC
)
1327 off_t pos
= FcCacheNextOffset (lseek(fd
, 0, SEEK_CUR
));
1328 current_dir_block
= mmap (0, metadata
.count
,
1329 PROT_READ
, MAP_SHARED
, fd
, pos
);
1330 if (current_dir_block
== MAP_FAILED
)
1333 if (!FcFontSetUnserialize (metadata
, set
, current_dir_block
))
1338 free (current_arch_machine_name
);
1345 free (current_arch_machine_name
);
1351 /* write serialized state to the cache file */
1353 FcDirCacheWrite (int bank
, FcFontSet
*set
, const FcChar8
*dir
)
1355 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1356 int fd
, bytes_to_write
, metadata_bytes
;
1358 off_t current_arch_start
= 0, truncate_to
;
1359 char * current_arch_machine_name
, bytes_written
[7] = "dedbef";
1360 void * current_dir_block
, *final_dir_block
;
1366 bytes_to_write
= FcFontSetNeededBytes (set
);
1367 metadata_bytes
= FcCacheNextOffset (sizeof (FcCache
));
1369 if (!bytes_to_write
)
1371 unlink (cache_file
);
1376 current_dir_block
= malloc (bytes_to_write
);
1377 memset (&metadata
, 0, sizeof(FcCache
));
1378 metadata
.count
= bytes_to_write
;
1379 metadata
.bank
= bank
;
1380 if (!current_dir_block
)
1382 final_dir_block
= FcFontSetDistributeBytes (&metadata
, current_dir_block
);
1384 if (!FcFontSetSerialize (bank
, set
))
1387 if (FcDebug () & FC_DBG_CACHE
)
1388 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file
);
1390 fd
= open(cache_file
, O_RDWR
| O_CREAT
, 0666);
1394 current_arch_machine_name
= FcCacheGetCurrentArch();
1395 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
1396 if (current_arch_start
< 0)
1397 current_arch_start
= FcCacheNextOffset (lseek(fd
, 0, SEEK_END
));
1399 if (!FcCacheMoveDown(fd
, current_arch_start
))
1402 current_arch_start
= lseek(fd
, 0, SEEK_CUR
);
1403 if (ftruncate (fd
, current_arch_start
) == -1)
1406 /* reserve space for arch, count & metadata */
1407 if (!FcCacheWriteString2 (fd
, current_arch_machine_name
))
1410 /* now write the address of the next offset */
1411 truncate_to
= FcCacheNextOffset(current_arch_start
+ bytes_to_write
+ metadata_bytes
) -
1413 strcpy (bytes_written
, l64a(truncate_to
));
1414 if (!FcCacheWriteString2 (fd
, bytes_written
))
1417 metadata
.magic
= FC_CACHE_MAGIC
;
1418 write (fd
, &metadata
, sizeof(FcCache
));
1419 lseek (fd
, FcCacheNextOffset (lseek(fd
, 0, SEEK_END
)), SEEK_SET
);
1420 write (fd
, current_dir_block
, bytes_to_write
);
1422 /* this actually serves to pad out the cache file */
1423 if (ftruncate (fd
, current_arch_start
+ truncate_to
) == -1)
1430 free (current_dir_block
);
1431 free (current_arch_machine_name
);
1433 unlink (cache_file
);
1438 /* if true, ignore the cache file */
1440 FcCacheForce (FcBool f
)
1445 static int banks_ptr
= 0, banks_alloc
= 0;
1446 static int * bankId
= 0;
1449 FcCacheBankCount (void)
1455 FcCacheHaveBank (int bank
)
1459 if (bank
< FC_BANK_FIRST
)
1462 for (i
= 0; i
< banks_ptr
; i
++)
1463 if (bankId
[i
] == bank
)
1470 FcCacheBankToIndex (int bank
)
1472 static int lastBank
= FC_BANK_DYNAMIC
, lastIndex
= -1;
1476 if (bank
== lastBank
)
1479 for (i
= 0; i
< banks_ptr
; i
++)
1480 if (bankId
[i
] == bank
)
1483 if (banks_ptr
<= banks_alloc
)
1485 b
= realloc (bankId
, banks_alloc
+ 4);