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>
31 #define ENDIAN_TEST 0x12345678
32 #define MACHINE_SIGNATURE_SIZE 9 + 5*19 + 1
35 FcCacheProduceMachineSignature (void);
38 * POSIX has broken stdio so that getc must do thread-safe locking,
39 * this is a serious performance problem for applications doing large
40 * amounts of IO with getc (as is done here). If available, use
41 * the getc_unlocked varient instead.
44 #if defined(getc_unlocked) || defined(_IO_getc_unlocked)
45 #define GETC(f) getc_unlocked(f)
46 #define PUTC(c,f) putc_unlocked(c,f)
48 #define GETC(f) getc(f)
49 #define PUTC(c,f) putc(c,f)
52 #define FC_DBG_CACHE_REF 1024
59 FcCacheReadString (FILE *f
, FcChar8
*dest
, int len
)
67 while ((c
= GETC (f
)) != EOF
)
79 while ((c
= GETC (f
)) != EOF
)
94 FcChar8
*new = malloc (size
* 2); /* freed in caller */
97 memcpy (new, d
, size
);
114 FcCacheReadUlong (FILE *f
, unsigned long *dest
)
119 while ((c
= GETC (f
)) != EOF
)
129 if (c
== EOF
|| isspace (c
))
133 t
= t
* 10 + (c
- '0');
141 FcCacheReadInt (FILE *f
, int *dest
)
146 ret
= FcCacheReadUlong (f
, &t
);
153 FcCacheReadTime (FILE *f
, time_t *dest
)
158 ret
= FcCacheReadUlong (f
, &t
);
165 FcCacheWriteChars (FILE *f
, const FcChar8
*chars
)
168 while ((c
= *chars
++))
173 if (PUTC ('\\', f
) == EOF
)
177 if (PUTC (c
, f
) == EOF
)
185 FcCacheWriteString (FILE *f
, const FcChar8
*string
)
188 if (PUTC ('"', f
) == EOF
)
190 if (!FcCacheWriteChars (f
, string
))
192 if (PUTC ('"', f
) == EOF
)
198 FcCacheWritePath (FILE *f
, const FcChar8
*dir
, const FcChar8
*file
)
200 if (PUTC ('"', f
) == EOF
)
203 if (!FcCacheWriteChars (f
, dir
))
207 dir
[strlen((const char *) dir
) - 1] != '/' &&
208 dir
[strlen((const char *) dir
) - 1] != '\\')
210 if (!FcCacheWriteChars (f
, "\\"))
214 if (dir
&& dir
[strlen((const char *) dir
) - 1] != '/')
215 if (PUTC ('/', f
) == EOF
)
218 if (!FcCacheWriteChars (f
, file
))
220 if (PUTC ('"', f
) == EOF
)
226 FcCacheWriteUlong (FILE *f
, unsigned long t
)
229 unsigned long temp
, digit
;
242 if (PUTC ((char) digit
+ '0', f
) == EOF
)
244 temp
= temp
- pow
* digit
;
251 FcCacheWriteInt (FILE *f
, int i
)
253 return FcCacheWriteUlong (f
, (unsigned long) i
);
257 FcCacheWriteTime (FILE *f
, time_t t
)
259 return FcCacheWriteUlong (f
, (unsigned long) t
);
263 FcCacheReadString2 (int fd
, FcChar8
*dest
, int len
)
276 while (read (fd
, &c
, 1) == 1)
303 FcCacheWriteString2 (int fd
, const FcChar8
*chars
)
305 if (write (fd
, chars
, strlen(chars
)+1) != strlen(chars
)+1)
311 FcCacheFontSetAdd (FcFontSet
*set
,
319 FcChar8 path_buf
[8192], *path
;
321 FcBool ret
= FcFalse
;
326 len
= (dir_len
+ 1 + strlen ((const char *) file
) + 1);
327 if (len
> sizeof (path_buf
))
329 path
= malloc (len
); /* freed down below */
333 strncpy ((char *) path
, (const char *) dir
, dir_len
);
335 if (dir
[dir_len
- 1] != '/' && dir
[dir_len
- 1] != '\\' )
336 path
[dir_len
++] = '\\';
338 if (dir
[dir_len
- 1] != '/')
339 path
[dir_len
++] = '/';
341 strcpy ((char *) path
+ dir_len
, (const char *) file
);
342 if (config
&& !FcConfigAcceptFilename (config
, path
))
344 else if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
346 if (FcDebug () & FC_DBG_CACHEV
)
347 printf (" dir cache dir \"%s\"\n", path
);
348 ret
= FcStrSetAdd (dirs
, path
);
350 else if (!FcStrCmp (name
, FC_FONT_FILE_INVALID
))
356 font
= FcNameParse (name
);
361 if (FcDebug () & FC_DBG_CACHEV
)
362 printf (" dir cache file \"%s\"\n", file
);
363 ret
= FcPatternAddString (font
, FC_FILE
, path
);
365 * Make sure the pattern has the file name as well as
366 * already containing at least one family name.
369 FcPatternGetString (font
, FC_FAMILY
, 0, &family
) == FcResultMatch
&&
370 (!config
|| FcConfigAcceptFont (config
, font
)))
372 frozen
= FcPatternFreeze (font
);
375 ret
= FcFontSetAdd (set
, frozen
);
377 FcPatternDestroy (font
);
380 if (path
!= path_buf
) free (path
);
386 FcCacheHash (const FcChar8
*string
, int len
)
391 while (len
-- && (c
= *string
++))
397 * Verify the saved timestamp for a file
400 FcGlobalCacheCheckTime (const FcChar8
*file
, FcGlobalCacheInfo
*info
)
404 if (stat ((char *) file
, &statb
) < 0)
406 if (FcDebug () & FC_DBG_CACHE
)
407 printf (" file %s missing\n", file
);
410 if (statb
.st_mtime
!= info
->time
)
412 if (FcDebug () & FC_DBG_CACHE
)
413 printf (" timestamp mismatch (was %d is %d)\n",
414 (int) info
->time
, (int) statb
.st_mtime
);
421 FcGlobalCacheReferenced (FcGlobalCache
*cache
,
422 FcGlobalCacheInfo
*info
)
424 if (!info
->referenced
)
426 info
->referenced
= FcTrue
;
428 if (FcDebug () & FC_DBG_CACHE_REF
)
429 printf ("Reference %d %s\n", cache
->referenced
, info
->file
);
434 * Break a path into dir/base elements and compute the base hash
435 * and the dir length. This is shared between the functions
436 * which walk the file caches
439 typedef struct _FcFilePathInfo
{
443 unsigned int base_hash
;
446 static FcFilePathInfo
447 FcFilePathInfoGet (const FcChar8
*path
)
452 slash
= FcStrLastSlash (path
);
456 i
.dir_len
= slash
- path
;
463 i
.dir
= (const FcChar8
*) ".";
467 i
.base_hash
= FcCacheHash (i
.base
, -1);
472 FcGlobalCacheDirGet (FcGlobalCache
*cache
,
475 FcBool create_missing
)
477 unsigned int hash
= FcCacheHash (dir
, len
);
478 FcGlobalCacheDir
*d
, **prev
;
480 for (prev
= &cache
->ents
[hash
% FC_GLOBAL_CACHE_DIR_HASH_SIZE
];
482 prev
= &(*prev
)->next
)
484 if (d
->info
.hash
== hash
&& d
->len
== len
&&
485 !strncmp ((const char *) d
->info
.file
,
486 (const char *) dir
, len
))
494 d
= malloc (sizeof (FcGlobalCacheDir
) + len
+ 1);
497 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCacheDir
) + len
+ 1);
501 d
->info
.file
= (FcChar8
*) (d
+ 1);
502 strncpy ((char *) d
->info
.file
, (const char *) dir
, len
);
503 d
->info
.file
[len
] = '\0';
505 d
->info
.referenced
= FcFalse
;
507 for (i
= 0; i
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; i
++)
514 static FcGlobalCacheInfo
*
515 FcGlobalCacheDirAdd (FcGlobalCache
*cache
,
519 FcBool create_missing
)
523 FcGlobalCacheSubdir
*subdir
;
524 FcGlobalCacheDir
*parent
;
526 i
= FcFilePathInfoGet (dir
);
527 parent
= FcGlobalCacheDirGet (cache
, i
.dir
, i
.dir_len
, create_missing
);
529 * Tricky here -- directories containing fonts.cache-1 files
530 * need entries only when the parent doesn't have a cache file.
531 * That is, when the parent already exists in the cache, is
532 * referenced and has a "real" timestamp. The time of 0 is
533 * special and marks directories which got stuck in the
534 * global cache for this very reason. Yes, it could
535 * use a separate boolean field, and probably should.
537 if (!parent
|| (!create_missing
&&
538 (!parent
->info
.referenced
||
539 (parent
->info
.time
== 0))))
542 * Add this directory to the cache
544 d
= FcGlobalCacheDirGet (cache
, dir
, strlen ((const char *) dir
), FcTrue
);
549 * Add this directory to the subdirectory list of the parent
551 subdir
= malloc (sizeof (FcGlobalCacheSubdir
));
554 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCacheSubdir
));
556 subdir
->next
= parent
->subdirs
;
557 parent
->subdirs
= subdir
;
562 FcGlobalCacheDirDestroy (FcGlobalCacheDir
*d
)
564 FcGlobalCacheFile
*f
, *next
;
566 FcGlobalCacheSubdir
*s
, *nexts
;
568 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
569 for (f
= d
->ents
[h
]; f
; f
= next
)
572 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheFile
) +
573 strlen ((char *) f
->info
.file
) + 1 +
574 strlen ((char *) f
->name
) + 1);
577 for (s
= d
->subdirs
; s
; s
= nexts
)
580 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheSubdir
));
583 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheDir
) + d
->len
+ 1);
588 * If the parent is in the global cache and referenced, add
589 * an entry for 'dir' to the global cache. This is used
590 * for directories with fonts.cache files
594 FcGlobalCacheReferenceSubdir (FcGlobalCache
*cache
,
597 FcGlobalCacheInfo
*info
;
598 info
= FcGlobalCacheDirAdd (cache
, dir
, 0, FcFalse
, FcFalse
);
599 if (info
&& !info
->referenced
)
601 info
->referenced
= FcTrue
;
607 * Check to see if the global cache contains valid data for 'dir'.
608 * If so, scan the global cache for files and directories in 'dir'.
609 * else, return False.
612 FcGlobalCacheScanDir (FcFontSet
*set
,
614 FcGlobalCache
*cache
,
618 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, dir
,
619 strlen ((const char *) dir
),
621 FcGlobalCacheFile
*f
;
624 FcGlobalCacheSubdir
*subdir
;
625 FcBool any_in_cache
= FcFalse
;
627 if (FcDebug() & FC_DBG_CACHE
)
628 printf ("FcGlobalCacheScanDir %s\n", dir
);
632 if (FcDebug () & FC_DBG_CACHE
)
633 printf ("\tNo dir cache entry\n");
638 * See if the timestamp recorded in the global cache
639 * matches the directory time, if not, return False
641 if (!FcGlobalCacheCheckTime (d
->info
.file
, &d
->info
))
643 if (FcDebug () & FC_DBG_CACHE
)
644 printf ("\tdir cache entry time mismatch\n");
649 * Add files from 'dir' to the fontset
651 dir_len
= strlen ((const char *) dir
);
652 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
653 for (f
= d
->ents
[h
]; f
; f
= f
->next
)
655 if (FcDebug() & FC_DBG_CACHEV
)
656 printf ("FcGlobalCacheScanDir add file %s\n", f
->info
.file
);
657 any_in_cache
= FcTrue
;
658 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
659 f
->info
.file
, f
->name
, config
))
661 cache
->broken
= FcTrue
;
664 FcGlobalCacheReferenced (cache
, &f
->info
);
667 * Add directories in 'dir' to 'dirs'
669 for (subdir
= d
->subdirs
; subdir
; subdir
= subdir
->next
)
671 FcFilePathInfo info
= FcFilePathInfoGet (subdir
->ent
->info
.file
);
673 any_in_cache
= FcTrue
;
674 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
675 info
.base
, FC_FONT_FILE_DIR
, config
))
677 cache
->broken
= FcTrue
;
680 FcGlobalCacheReferenced (cache
, &subdir
->ent
->info
);
683 FcGlobalCacheReferenced (cache
, &d
->info
);
686 * To recover from a bug in previous versions of fontconfig,
687 * return FcFalse if no entries in the cache were found
688 * for this directory. This will cause any empty directories
689 * to get rescanned every time fontconfig is initialized. This
690 * might get removed at some point when the older cache files are
697 * Locate the cache entry for a particular file
700 FcGlobalCacheFileGet (FcGlobalCache
*cache
,
705 FcFilePathInfo i
= FcFilePathInfoGet (file
);
706 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
708 FcGlobalCacheFile
*f
, *match
= 0;
713 for (f
= d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
]; f
; f
= f
->next
)
715 if (f
->info
.hash
== i
.base_hash
&&
716 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
730 * Add a file entry to the cache
732 static FcGlobalCacheInfo
*
733 FcGlobalCacheFileAdd (FcGlobalCache
*cache
,
740 FcFilePathInfo i
= FcFilePathInfoGet (path
);
741 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
743 FcGlobalCacheFile
*f
, **prev
;
748 for (prev
= &d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
];
750 prev
= &(*prev
)->next
)
752 if (f
->info
.hash
== i
.base_hash
&&
754 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
765 if (f
->info
.referenced
)
768 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCacheFile
) +
769 strlen ((char *) f
->info
.file
) + 1 +
770 strlen ((char *) f
->name
) + 1);
773 size
= (sizeof (FcGlobalCacheFile
) +
774 strlen ((char *) i
.base
) + 1 +
775 strlen ((char *) name
) + 1);
779 FcMemAlloc (FC_MEM_CACHE
, size
);
782 f
->info
.hash
= i
.base_hash
;
783 f
->info
.file
= (FcChar8
*) (f
+ 1);
785 f
->info
.referenced
= FcFalse
;
787 f
->name
= f
->info
.file
+ strlen ((char *) i
.base
) + 1;
788 strcpy ((char *) f
->info
.file
, (const char *) i
.base
);
789 strcpy ((char *) f
->name
, (const char *) name
);
794 FcGlobalCacheCreate (void)
796 FcGlobalCache
*cache
;
799 cache
= malloc (sizeof (FcGlobalCache
));
802 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
803 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
806 cache
->referenced
= 0;
807 cache
->updated
= FcFalse
;
808 cache
->broken
= FcFalse
;
813 FcGlobalCacheDestroy (FcGlobalCache
*cache
)
815 FcGlobalCacheDir
*d
, *next
;
818 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
820 for (d
= cache
->ents
[h
]; d
; d
= next
)
823 FcGlobalCacheDirDestroy (d
);
826 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
831 * Cache file syntax is quite simple:
833 * "file_name" id time "font_name" \n
837 FcGlobalCacheLoad (FcGlobalCache
*cache
,
838 const FcChar8
*cache_file
)
841 FcChar8 file_buf
[8192], *file
;
844 FcChar8 name_buf
[8192], *name
;
845 FcGlobalCacheInfo
*info
;
847 f
= fopen ((char *) cache_file
, "r");
851 cache
->updated
= FcFalse
;
854 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
855 FcCacheReadInt (f
, &id
) &&
856 FcCacheReadTime (f
, &time
) &&
857 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
859 if (FcDebug () & FC_DBG_CACHEV
)
860 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file
, name
);
861 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
862 info
= FcGlobalCacheDirAdd (cache
, file
, time
, FcFalse
, FcTrue
);
864 info
= FcGlobalCacheFileAdd (cache
, file
, id
, time
, name
, FcFalse
);
866 cache
->broken
= FcTrue
;
869 if (FcDebug () & FC_DBG_CACHE_REF
)
870 printf ("FcGlobalCacheLoad entry %d %s\n",
871 cache
->entries
, file
);
872 if (file
!= file_buf
)
874 if (name
!= name_buf
)
879 if (file
&& file
!= file_buf
)
881 if (name
&& name
!= name_buf
)
887 FcGlobalCacheUpdate (FcGlobalCache
*cache
,
892 const FcChar8
*match
;
894 FcGlobalCacheInfo
*info
;
898 if (stat ((char *) file
, &statb
) < 0)
900 if (S_ISDIR (statb
.st_mode
))
901 info
= FcGlobalCacheDirAdd (cache
, file
, statb
.st_mtime
,
904 info
= FcGlobalCacheFileAdd (cache
, file
, id
, statb
.st_mtime
,
908 FcGlobalCacheReferenced (cache
, info
);
909 cache
->updated
= FcTrue
;
912 cache
->broken
= FcTrue
;
917 FcGlobalCacheSave (FcGlobalCache
*cache
,
918 const FcChar8
*cache_file
)
921 int dir_hash
, file_hash
;
922 FcGlobalCacheDir
*dir
;
923 FcGlobalCacheFile
*file
;
926 if (!cache
->updated
&& cache
->referenced
== cache
->entries
)
932 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
933 /* Set-UID programs can't safely update the cache */
934 if (getuid () != geteuid ())
938 atomic
= FcAtomicCreate (cache_file
);
941 if (!FcAtomicLock (atomic
))
943 f
= fopen ((char *) FcAtomicNewFile(atomic
), "w");
947 for (dir_hash
= 0; dir_hash
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; dir_hash
++)
949 for (dir
= cache
->ents
[dir_hash
]; dir
; dir
= dir
->next
)
951 if (!dir
->info
.referenced
)
953 if (!FcCacheWriteString (f
, dir
->info
.file
))
955 if (PUTC (' ', f
) == EOF
)
957 if (!FcCacheWriteInt (f
, 0))
959 if (PUTC (' ', f
) == EOF
)
961 if (!FcCacheWriteTime (f
, dir
->info
.time
))
963 if (PUTC (' ', f
) == EOF
)
965 if (!FcCacheWriteString (f
, (FcChar8
*) FC_FONT_FILE_DIR
))
967 if (PUTC ('\n', f
) == EOF
)
970 for (file_hash
= 0; file_hash
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; file_hash
++)
972 for (file
= dir
->ents
[file_hash
]; file
; file
= file
->next
)
974 if (!file
->info
.referenced
)
976 if (!FcCacheWritePath (f
, dir
->info
.file
, file
->info
.file
))
978 if (PUTC (' ', f
) == EOF
)
980 if (!FcCacheWriteInt (f
, file
->id
< 0 ? 0 : file
->id
))
982 if (PUTC (' ', f
) == EOF
)
984 if (!FcCacheWriteTime (f
, file
->info
.time
))
986 if (PUTC (' ', f
) == EOF
)
988 if (!FcCacheWriteString (f
, file
->name
))
990 if (PUTC ('\n', f
) == EOF
)
997 if (fclose (f
) == EOF
)
1000 if (!FcAtomicReplaceOrig (atomic
))
1003 FcAtomicUnlock (atomic
);
1004 FcAtomicDestroy (atomic
);
1006 cache
->updated
= FcFalse
;
1012 FcAtomicDeleteNew (atomic
);
1014 FcAtomicUnlock (atomic
);
1016 FcAtomicDestroy (atomic
);
1022 * Find the next presumably-mmapable offset after the current file
1026 FcCacheNextOffset(off_t w
)
1028 if (w
% PAGESIZE
== 0)
1031 return ((w
/ PAGESIZE
)+1)*PAGESIZE
;
1034 /* return the address of the segment for the provided arch,
1035 * or -1 if arch not found */
1037 FcCacheSkipToArch (int fd
, const char * arch
)
1039 char candidate_arch_machine_name_count
[MACHINE_SIGNATURE_SIZE
+ 9];
1040 char * candidate_arch
;
1041 off_t current_arch_start
= 0;
1043 /* skip arches that are not the current arch */
1048 lseek (fd
, current_arch_start
, SEEK_SET
);
1049 if (FcCacheReadString2 (fd
, candidate_arch_machine_name_count
,
1050 sizeof (candidate_arch_machine_name_count
)) == 0)
1052 if (!strlen(candidate_arch_machine_name_count
))
1054 bs
= strtol(candidate_arch_machine_name_count
, &candidate_arch
, 16);
1055 candidate_arch
++; /* skip leading space */
1057 if (strcmp (candidate_arch
, arch
)==0)
1059 current_arch_start
+= bs
;
1062 if (strcmp (candidate_arch
, arch
)!=0)
1065 return current_arch_start
;
1068 /* Cuts out the segment at the file pointer (moves everything else
1069 * down to cover it), and leaves the file pointer at the end of the
1071 #define BUF_SIZE 8192
1074 FcCacheMoveDown (int fd
, off_t start
)
1076 char * buf
= malloc (BUF_SIZE
);
1077 char candidate_arch_machine_name
[MACHINE_SIGNATURE_SIZE
+ 9];
1079 int c
, bytes_skipped
;
1084 lseek (fd
, start
, SEEK_SET
);
1085 if (FcCacheReadString2 (fd
, candidate_arch_machine_name
,
1086 sizeof (candidate_arch_machine_name
)) == 0)
1088 if (!strlen(candidate_arch_machine_name
))
1091 bs
= strtol(candidate_arch_machine_name
, 0, 16);
1098 lseek (fd
, start
+bs
+bytes_skipped
, SEEK_SET
);
1099 if ((c
= read (fd
, buf
, BUF_SIZE
)) <= 0)
1101 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
1102 if (write (fd
, buf
, c
) < 0)
1107 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
1119 FcDirCacheValid (const FcChar8
*dir
)
1121 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1122 struct stat file_stat
, dir_stat
;
1124 if (stat ((char *) dir
, &dir_stat
) < 0)
1126 FcStrFree (cache_file
);
1129 if (stat ((char *) cache_file
, &file_stat
) < 0)
1131 FcStrFree (cache_file
);
1134 FcStrFree (cache_file
);
1136 * If the directory has been modified more recently than
1137 * the cache file, the cache is not valid
1139 if (dir_stat
.st_mtime
- file_stat
.st_mtime
> 0)
1145 FcCacheReadDirs (FcConfig
* config
, FcGlobalCache
* cache
,
1146 FcStrList
*list
, FcFontSet
* set
)
1152 FcChar8
*file
, *base
;
1158 * Now scan all of the directories into separate databases
1159 * and write out the results
1161 while ((dir
= FcStrListNext (list
)))
1164 file
= (FcChar8
*) malloc (strlen ((char *) dir
) + 1 + FC_MAX_FILE_LEN
+ 1);
1168 strcpy ((char *) file
, (char *) dir
);
1169 strcat ((char *) file
, "/");
1170 base
= file
+ strlen ((char *) file
);
1172 subdirs
= FcStrSetCreate ();
1175 fprintf (stderr
, "Can't create directory set\n");
1181 if (access ((char *) dir
, X_OK
) < 0)
1189 fprintf (stderr
, "\"%s\": ", dir
);
1193 FcStrSetDestroy (subdirs
);
1197 if (stat ((char *) dir
, &statb
) == -1)
1199 fprintf (stderr
, "\"%s\": ", dir
);
1201 FcStrSetDestroy (subdirs
);
1206 if (!S_ISDIR (statb
.st_mode
))
1208 fprintf (stderr
, "\"%s\": not a directory, skipping\n", dir
);
1209 FcStrSetDestroy (subdirs
);
1213 d
= opendir ((char *) dir
);
1216 FcStrSetDestroy (subdirs
);
1220 while ((e
= readdir (d
)))
1222 if (e
->d_name
[0] != '.' && strlen (e
->d_name
) < FC_MAX_FILE_LEN
)
1224 strcpy ((char *) base
, (char *) e
->d_name
);
1225 if (FcFileIsDir (file
) && !FcStrSetAdd (subdirs
, file
))
1230 if (!FcDirCacheValid (dir
) || !FcDirCacheRead (set
, dir
))
1232 if (FcDebug () & FC_DBG_FONTSET
)
1233 printf ("scan dir %s\n", dir
);
1234 FcDirScanConfig (set
, subdirs
, cache
,
1235 config
->blanks
, dir
, FcFalse
, config
);
1237 sublist
= FcStrListCreate (subdirs
);
1238 FcStrSetDestroy (subdirs
);
1241 fprintf (stderr
, "Can't create subdir list in \"%s\"\n", dir
);
1246 ret
+= FcCacheReadDirs (config
, cache
, sublist
, set
);
1249 FcStrListDone (list
);
1254 FcCacheRead (FcConfig
*config
, FcGlobalCache
* cache
)
1256 FcFontSet
* s
= FcFontSetCreate();
1263 if (FcCacheReadDirs (config
, cache
, FcConfigGetConfigDirs (config
), s
))
1269 FcFontSetDestroy (s
);
1273 /* read serialized state from the cache file */
1275 FcDirCacheRead (FcFontSet
* set
, const FcChar8
*dir
)
1277 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1280 void * current_dir_block
;
1281 char * current_arch_machine_name
;
1282 char candidate_arch_machine_name
[9+MACHINE_SIGNATURE_SIZE
];
1283 off_t current_arch_start
= 0;
1290 current_arch_machine_name
= FcCacheProduceMachineSignature();
1291 fd
= open(cache_file
, O_RDONLY
);
1295 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
1296 if (current_arch_start
< 0)
1299 lseek (fd
, current_arch_start
, SEEK_SET
);
1300 if (FcCacheReadString2 (fd
, candidate_arch_machine_name
,
1301 sizeof (candidate_arch_machine_name
)) == 0)
1304 // sanity check for endianness issues
1305 read(fd
, &metadata
, sizeof(FcCache
));
1306 if (metadata
.magic
!= FC_CACHE_MAGIC
)
1309 if (!metadata
.count
)
1312 off_t pos
= FcCacheNextOffset (lseek(fd
, 0, SEEK_CUR
));
1313 current_dir_block
= mmap (0, metadata
.count
,
1314 PROT_READ
, MAP_SHARED
, fd
, pos
);
1315 if (current_dir_block
== MAP_FAILED
)
1318 if (!FcFontSetUnserialize (metadata
, set
, current_dir_block
))
1332 /* write serialized state to the cache file */
1334 FcDirCacheWrite (int bank
, FcFontSet
*set
, const FcChar8
*dir
)
1336 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
1337 int fd
, bytes_to_write
, metadata_bytes
;
1339 off_t current_arch_start
= 0, truncate_to
;
1340 char * current_arch_machine_name
, * header
;
1341 void * current_dir_block
, *final_dir_block
;
1347 bytes_to_write
= FcFontSetNeededBytes (set
);
1348 metadata_bytes
= FcCacheNextOffset (sizeof (FcCache
));
1350 if (!bytes_to_write
)
1352 unlink (cache_file
);
1357 current_dir_block
= malloc (bytes_to_write
);
1358 memset (&metadata
, 0, sizeof(FcCache
));
1359 metadata
.count
= bytes_to_write
;
1360 metadata
.bank
= bank
;
1361 if (!current_dir_block
)
1363 final_dir_block
= FcFontSetDistributeBytes (&metadata
, current_dir_block
);
1365 if ((char *)current_dir_block
+ bytes_to_write
!= final_dir_block
)
1368 if (!FcFontSetSerialize (bank
, set
))
1371 if (FcDebug () & FC_DBG_CACHE
)
1372 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file
);
1374 fd
= open(cache_file
, O_RDWR
| O_CREAT
, 0666);
1378 current_arch_machine_name
= FcCacheProduceMachineSignature ();
1379 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
1380 if (current_arch_start
< 0)
1381 current_arch_start
= FcCacheNextOffset (lseek(fd
, 0, SEEK_END
));
1383 if (!FcCacheMoveDown(fd
, current_arch_start
))
1386 current_arch_start
= lseek(fd
, 0, SEEK_CUR
);
1387 if (ftruncate (fd
, current_arch_start
) == -1)
1390 /* now write the address of the next offset */
1391 truncate_to
= FcCacheNextOffset (FcCacheNextOffset (current_arch_start
+ metadata_bytes
) + bytes_to_write
) - current_arch_start
;
1393 header
= malloc (10 + strlen (current_arch_machine_name
));
1394 sprintf (header
, "%8x ", (int)truncate_to
);
1395 strcat (header
, current_arch_machine_name
);
1396 if (!FcCacheWriteString2 (fd
, header
))
1399 metadata
.magic
= FC_CACHE_MAGIC
;
1400 write (fd
, &metadata
, sizeof(FcCache
));
1401 lseek (fd
, FcCacheNextOffset (lseek(fd
, 0, SEEK_END
)), SEEK_SET
);
1402 write (fd
, current_dir_block
, bytes_to_write
);
1404 /* this actually serves to pad out the cache file, if needed */
1405 if (ftruncate (fd
, current_arch_start
+ truncate_to
) == -1)
1412 free (current_dir_block
);
1413 free (current_arch_machine_name
);
1415 unlink (cache_file
);
1421 FcCacheProduceMachineSignature ()
1423 static char buf
[MACHINE_SIGNATURE_SIZE
];
1424 int magic
= ENDIAN_TEST
;
1425 char * m
= (char *)&magic
;
1427 sprintf (buf
, "%2x%2x%2x%2x "
1428 "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x "
1429 "%4x %4x %4x %4x %4x %4x %4x\n",
1430 m
[0], m
[1], m
[2], m
[3],
1435 sizeof (FcPatternEltPtr
),
1436 sizeof (struct _FcPatternElt
*),
1437 sizeof (FcPatternElt
),
1438 sizeof (FcObjectPtr
),
1439 sizeof (FcValueListPtr
),
1441 sizeof (FcValueBinding
),
1442 sizeof (struct _FcValueList
*),
1444 sizeof (FcCharLeaf
**),
1445 sizeof (FcChar16
*),
1447 sizeof (FcCharLeaf
),
1454 /* if true, ignore the cache file */
1456 FcCacheForce (FcBool f
)
1461 static int banks_ptr
= 0, banks_alloc
= 0;
1462 static int * bankId
= 0;
1465 FcCacheBankCount (void)
1471 FcCacheHaveBank (int bank
)
1475 if (bank
< FC_BANK_FIRST
)
1478 for (i
= 0; i
< banks_ptr
; i
++)
1479 if (bankId
[i
] == bank
)
1486 FcCacheBankToIndex (int bank
)
1488 static int lastBank
= FC_BANK_DYNAMIC
, lastIndex
= -1;
1492 if (bank
== lastBank
)
1495 for (i
= 0; i
< banks_ptr
; i
++)
1496 if (bankId
[i
] == bank
)
1499 if (banks_ptr
>= banks_alloc
)
1501 b
= realloc (bankId
, (banks_alloc
+ 4) * sizeof(int));