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>
36 FcCacheReadString (int fd
, FcChar8
*dest
, int len
)
49 while (read (fd
, &c
, 1) == 1)
76 FcCacheWriteString (int fd
, const FcChar8
*chars
)
78 if (write (fd
, chars
, strlen(chars
)+1) != strlen(chars
)+1)
85 * Verify the saved timestamp for a file
88 FcGlobalCacheCheckTime (const FcChar8
*file
, FcGlobalCacheInfo
*info
)
92 if (stat ((char *) file
, &statb
) < 0)
94 if (FcDebug () & FC_DBG_CACHE
)
95 printf (" file %s missing\n", file
);
98 if (statb
.st_mtime
!= info
->time
)
100 if (FcDebug () & FC_DBG_CACHE
)
101 printf (" timestamp mismatch (was %d is %d)\n",
102 (int) info
->time
, (int) statb
.st_mtime
);
109 FcGlobalCacheReferenced (FcGlobalCache
*cache
,
110 FcGlobalCacheInfo
*info
)
112 if (!info
->referenced
)
114 info
->referenced
= FcTrue
;
116 if (FcDebug () & FC_DBG_CACHE_REF
)
117 printf ("Reference %d %s\n", cache
->referenced
, info
->file
);
122 * Break a path into dir/base elements and compute the base hash
123 * and the dir length. This is shared between the functions
124 * which walk the file caches
127 typedef struct _FcFilePathInfo
{
131 unsigned int base_hash
;
134 static FcFilePathInfo
135 FcFilePathInfoGet (const FcChar8
*path
)
140 slash
= FcStrLastSlash (path
);
144 i
.dir_len
= slash
- path
;
151 i
.dir
= (const FcChar8
*) ".";
155 i
.base_hash
= FcCacheHash (i
.base
, -1);
160 FcGlobalCacheCreate (void)
162 FcGlobalCache
*cache
;
165 cache
= malloc (sizeof (FcGlobalCache
));
168 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
169 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
172 cache
->referenced
= 0;
173 cache
->updated
= FcFalse
;
174 cache
->broken
= FcFalse
;
179 FcGlobalCacheDestroy (FcGlobalCache
*cache
)
181 FcGlobalCacheDir
*d
, *next
;
184 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
186 for (d
= cache
->ents
[h
]; d
; d
= next
)
189 FcGlobalCacheDirDestroy (d
);
192 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
197 FcGlobalCacheLoad (FcGlobalCache
*cache
,
198 const FcChar8
*cache_file
)
201 FcChar8 file_buf
[8192], *file
;
204 FcChar8 name_buf
[8192], *name
;
205 FcGlobalCacheInfo
*info
;
207 f
= fopen ((char *) cache_file
, "r");
211 cache
->updated
= FcFalse
;
214 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
215 FcCacheReadInt (f
, &id
) &&
216 FcCacheReadTime (f
, &time
) &&
217 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
219 if (FcDebug () & FC_DBG_CACHEV
)
220 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file
, name
);
221 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
222 info
= FcGlobalCacheDirAdd (cache
, file
, time
, FcFalse
, FcTrue
);
224 info
= FcGlobalCacheFileAdd (cache
, file
, id
, time
, name
, FcFalse
);
226 cache
->broken
= FcTrue
;
229 if (FcDebug () & FC_DBG_CACHE_REF
)
230 printf ("FcGlobalCacheLoad entry %d %s\n",
231 cache
->entries
, file
);
232 if (file
!= file_buf
)
234 if (name
!= name_buf
)
239 if (file
&& file
!= file_buf
)
241 if (name
&& name
!= name_buf
)
247 FcGlobalCacheUpdate (FcGlobalCache
*cache
,
252 const FcChar8
*match
;
254 FcGlobalCacheInfo
*info
;
258 if (stat ((char *) file
, &statb
) < 0)
260 if (S_ISDIR (statb
.st_mode
))
261 info
= FcGlobalCacheDirAdd (cache
, file
, statb
.st_mtime
,
264 info
= FcGlobalCacheFileAdd (cache
, file
, id
, statb
.st_mtime
,
268 FcGlobalCacheReferenced (cache
, info
);
269 cache
->updated
= FcTrue
;
272 cache
->broken
= FcTrue
;
277 FcGlobalCacheSave (FcGlobalCache
*cache
,
278 const FcChar8
*cache_file
)
281 int dir_hash
, file_hash
;
282 FcGlobalCacheDir
*dir
;
283 FcGlobalCacheFile
*file
;
286 if (!cache
->updated
&& cache
->referenced
== cache
->entries
)
292 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
293 /* Set-UID programs can't safely update the cache */
294 if (getuid () != geteuid ())
298 atomic
= FcAtomicCreate (cache_file
);
301 if (!FcAtomicLock (atomic
))
303 f
= fopen ((char *) FcAtomicNewFile(atomic
), "w");
307 for (dir_hash
= 0; dir_hash
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; dir_hash
++)
309 for (dir
= cache
->ents
[dir_hash
]; dir
; dir
= dir
->next
)
311 if (!dir
->info
.referenced
)
313 if (!FcCacheWriteString (f
, dir
->info
.file
))
315 if (PUTC (' ', f
) == EOF
)
317 if (!FcCacheWriteInt (f
, 0))
319 if (PUTC (' ', f
) == EOF
)
321 if (!FcCacheWriteTime (f
, dir
->info
.time
))
323 if (PUTC (' ', f
) == EOF
)
325 if (!FcCacheWriteString (f
, (FcChar8
*) FC_FONT_FILE_DIR
))
327 if (PUTC ('\n', f
) == EOF
)
330 for (file_hash
= 0; file_hash
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; file_hash
++)
332 for (file
= dir
->ents
[file_hash
]; file
; file
= file
->next
)
334 if (!file
->info
.referenced
)
336 if (!FcCacheWritePath (f
, dir
->info
.file
, file
->info
.file
))
338 if (PUTC (' ', f
) == EOF
)
340 if (!FcCacheWriteInt (f
, file
->id
< 0 ? 0 : file
->id
))
342 if (PUTC (' ', f
) == EOF
)
344 if (!FcCacheWriteTime (f
, file
->info
.time
))
346 if (PUTC (' ', f
) == EOF
)
348 if (!FcCacheWriteString (f
, file
->name
))
350 if (PUTC ('\n', f
) == EOF
)
357 if (fclose (f
) == EOF
)
360 if (!FcAtomicReplaceOrig (atomic
))
363 FcAtomicUnlock (atomic
);
364 FcAtomicDestroy (atomic
);
366 cache
->updated
= FcFalse
;
372 FcAtomicDeleteNew (atomic
);
374 FcAtomicUnlock (atomic
);
376 FcAtomicDestroy (atomic
);
383 * Find the next presumably-mmapable offset after the current file
387 FcCacheNextOffset(off_t w
)
389 if (w
% PAGESIZE
== 0)
392 return ((w
/ PAGESIZE
)+1)*PAGESIZE
;
395 /* get the current arch name */
396 /* caller is responsible for freeing returned pointer */
398 FcCacheGetCurrentArch (void)
401 char * current_arch_machine_name
;
405 current_arch_machine_name
= strdup(b
.machine
);
406 /* if (getenv ("FAKE_ARCH")) // testing purposes
407 current_arch_machine_name = strdup(getenv("FAKE_ARCH")); */
408 return current_arch_machine_name
;
411 /* return the address of the segment for the provided arch,
412 * or -1 if arch not found */
414 FcCacheSkipToArch (int fd
, const char * arch
)
416 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
417 off_t current_arch_start
= 0;
419 /* skip arches that are not the current arch */
424 lseek (fd
, current_arch_start
, SEEK_SET
);
425 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
426 sizeof (candidate_arch_machine_name
)) == 0)
428 if (FcCacheReadString (fd
, bytes_to_skip
, 7) == 0)
430 bs
= a64l(bytes_to_skip
);
434 if (strcmp (candidate_arch_machine_name
, arch
)==0)
436 current_arch_start
+= bs
;
439 if (strcmp (candidate_arch_machine_name
, arch
)!=0)
442 return current_arch_start
;
445 /* Cuts out the segment at the file pointer (moves everything else
446 * down to cover it), and leaves the file pointer at the end of the
448 #define BUF_SIZE 8192
451 FcCacheMoveDown (int fd
, off_t start
)
453 char * buf
= malloc (BUF_SIZE
);
454 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
456 int c
, bytes_skipped
;
461 lseek (fd
, start
, SEEK_SET
);
462 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
463 sizeof (candidate_arch_machine_name
)) == 0)
465 if (FcCacheReadString (fd
, bytes_to_skip
, 7) == 0)
468 bs
= a64l(bytes_to_skip
);
475 lseek (fd
, start
+bs
+bytes_skipped
, SEEK_SET
);
476 if ((c
= read (fd
, buf
, BUF_SIZE
)) <= 0)
478 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
479 if (write (fd
, buf
, c
) < 0)
484 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
496 FcCacheReadDirs (FcStrList
*list
, FcFontSet
* set
)
502 FcChar8
*file
, *base
;
508 * Now scan all of the directories into separate databases
509 * and write out the results
511 while ((dir
= FcStrListNext (list
)))
514 file
= (FcChar8
*) malloc (strlen ((char *) dir
) + 1 + FC_MAX_FILE_LEN
+ 1);
518 strcpy ((char *) file
, (char *) dir
);
519 strcat ((char *) file
, "/");
520 base
= file
+ strlen ((char *) file
);
522 subdirs
= FcStrSetCreate ();
525 fprintf (stderr
, "Can't create directory set\n");
531 if (access ((char *) dir
, X_OK
) < 0)
539 fprintf (stderr
, "\"%s\": ", dir
);
543 FcStrSetDestroy (subdirs
);
547 if (stat ((char *) dir
, &statb
) == -1)
549 fprintf (stderr
, "\"%s\": ", dir
);
551 FcStrSetDestroy (subdirs
);
556 if (!S_ISDIR (statb
.st_mode
))
558 fprintf (stderr
, "\"%s\": not a directory, skipping\n", dir
);
559 FcStrSetDestroy (subdirs
);
563 d
= opendir ((char *) dir
);
566 FcStrSetDestroy (subdirs
);
570 while ((e
= readdir (d
)))
572 if (e
->d_name
[0] != '.' && strlen (e
->d_name
) < FC_MAX_FILE_LEN
)
574 strcpy ((char *) base
, (char *) e
->d_name
);
575 if (FcFileIsDir (file
) && !FcStrSetAdd (subdirs
, file
))
580 if (1 || FcDirCacheValid (dir
))
582 FcDirCacheRead (set
, dir
);
587 #if 0 // (implement per-dir loading)
589 printf ("caching, %d fonts, %d dirs\n",
590 set
->nfont
, nsubdirs (subdirs
));
592 if (!FcDirSave (set
, dir
))
594 fprintf (stderr
, "Can't save cache in \"%s\"\n", dir
);
599 sublist
= FcStrListCreate (subdirs
);
600 FcStrSetDestroy (subdirs
);
603 fprintf (stderr
, "Can't create subdir list in \"%s\"\n", dir
);
608 ret
+= FcCacheReadDirs (sublist
, set
);
611 FcStrListDone (list
);
616 FcCacheRead (FcConfig
*config
)
618 FcFontSet
* s
= FcFontSetCreate();
625 if (FcCacheReadDirs (FcConfigGetConfigDirs (config
), s
))
631 FcFontSetDestroy (s
);
635 /* read serialized state from the cache file */
637 FcDirCacheRead (FcFontSet
* set
, const FcChar8
*dir
)
639 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
642 void * current_dir_block
;
643 char * current_arch_machine_name
;
644 char candidate_arch_machine_name
[64], bytes_in_block
[7];
645 off_t current_arch_start
= 0;
652 current_arch_machine_name
= FcCacheGetCurrentArch();
653 fd
= open(cache_file
, O_RDONLY
);
657 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
658 if (current_arch_start
< 0)
661 lseek (fd
, current_arch_start
, SEEK_SET
);
662 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
663 sizeof (candidate_arch_machine_name
)) == 0)
665 if (FcCacheReadString (fd
, bytes_in_block
, 7) == 0)
668 // sanity check for endianness issues
669 read(fd
, &metadata
, sizeof(FcCache
));
670 if (metadata
.magic
!= FC_CACHE_MAGIC
)
675 off_t pos
= FcCacheNextOffset (lseek(fd
, 0, SEEK_CUR
));
676 current_dir_block
= mmap (0, metadata
.count
,
677 PROT_READ
, MAP_SHARED
, fd
, pos
);
678 if (current_dir_block
== MAP_FAILED
)
681 if (!FcFontSetUnserialize (metadata
, set
, current_dir_block
))
686 free (current_arch_machine_name
);
693 free (current_arch_machine_name
);
699 /* write serialized state to the cache file */
701 FcDirCacheWrite (int bank
, FcFontSet
*set
, const FcChar8
*dir
)
703 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
704 int fd
, bytes_to_write
, metadata_bytes
;
706 off_t current_arch_start
= 0, truncate_to
;
707 char * current_arch_machine_name
, bytes_written
[7] = "dedbef";
708 void * current_dir_block
, *final_dir_block
;
714 bytes_to_write
= FcFontSetNeededBytes (set
);
715 metadata_bytes
= FcCacheNextOffset (sizeof (FcCache
));
724 current_dir_block
= malloc (bytes_to_write
);
725 memset (&metadata
, 0, sizeof(FcCache
));
726 metadata
.count
= bytes_to_write
;
727 metadata
.bank
= bank
;
728 if (!current_dir_block
)
730 final_dir_block
= FcFontSetDistributeBytes (&metadata
, current_dir_block
);
732 if (!FcFontSetSerialize (bank
, set
))
735 if (FcDebug () & FC_DBG_CACHE
)
736 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file
);
738 fd
= open(cache_file
, O_RDWR
| O_CREAT
, 0666);
742 current_arch_machine_name
= FcCacheGetCurrentArch();
743 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
744 if (current_arch_start
< 0)
745 current_arch_start
= FcCacheNextOffset (lseek(fd
, 0, SEEK_END
));
747 if (!FcCacheMoveDown(fd
, current_arch_start
))
750 current_arch_start
= lseek(fd
, 0, SEEK_CUR
);
751 if (ftruncate (fd
, current_arch_start
) == -1)
754 /* reserve space for arch, count & metadata */
755 if (!FcCacheWriteString (fd
, current_arch_machine_name
))
758 /* now write the address of the next offset */
759 truncate_to
= FcCacheNextOffset(current_arch_start
+ bytes_to_write
+ metadata_bytes
) -
761 strcpy (bytes_written
, l64a(truncate_to
));
762 if (!FcCacheWriteString (fd
, bytes_written
))
765 metadata
.magic
= FC_CACHE_MAGIC
;
766 write (fd
, &metadata
, sizeof(FcCache
));
767 lseek (fd
, FcCacheNextOffset (lseek(fd
, 0, SEEK_END
)), SEEK_SET
);
768 write (fd
, current_dir_block
, bytes_to_write
);
770 /* this actually serves to pad out the cache file */
771 if (ftruncate (fd
, current_arch_start
+ truncate_to
) == -1)
778 free (current_dir_block
);
779 free (current_arch_machine_name
);
786 /* if true, ignore the cache file */
788 FcCacheForce (FcBool f
)
793 static int banks_ptr
= 0, banks_alloc
= 0;
794 static int * bankId
= 0;
797 FcCacheBankCount (void)
803 FcCacheHaveBank (int bank
)
807 if (bank
< FC_BANK_FIRST
)
810 for (i
= 0; i
< banks_ptr
; i
++)
811 if (bankId
[i
] == bank
)
818 FcCacheBankToIndex (int bank
)
820 static int lastBank
= FC_BANK_DYNAMIC
, lastIndex
= -1;
824 if (bank
== lastBank
)
827 for (i
= 0; i
< banks_ptr
; i
++)
828 if (bankId
[i
] == bank
)
831 if (banks_ptr
<= banks_alloc
)
833 b
= realloc (bankId
, banks_alloc
+ 4);