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.
27 #include <sys/utsname.h>
35 FcCacheReadString (int fd
, FcChar8
*dest
, int len
)
48 while (read (fd
, &c
, 1) == 1)
75 FcCacheWriteString (int fd
, const FcChar8
*chars
)
77 if (write (fd
, chars
, strlen(chars
)+1) != strlen(chars
)+1)
84 * Verify the saved timestamp for a file
87 FcGlobalCacheCheckTime (const FcChar8
*file
, FcGlobalCacheInfo
*info
)
91 if (stat ((char *) file
, &statb
) < 0)
93 if (FcDebug () & FC_DBG_CACHE
)
94 printf (" file %s missing\n", file
);
97 if (statb
.st_mtime
!= info
->time
)
99 if (FcDebug () & FC_DBG_CACHE
)
100 printf (" timestamp mismatch (was %d is %d)\n",
101 (int) info
->time
, (int) statb
.st_mtime
);
108 FcGlobalCacheReferenced (FcGlobalCache
*cache
,
109 FcGlobalCacheInfo
*info
)
111 if (!info
->referenced
)
113 info
->referenced
= FcTrue
;
115 if (FcDebug () & FC_DBG_CACHE_REF
)
116 printf ("Reference %d %s\n", cache
->referenced
, info
->file
);
121 * Break a path into dir/base elements and compute the base hash
122 * and the dir length. This is shared between the functions
123 * which walk the file caches
126 typedef struct _FcFilePathInfo
{
130 unsigned int base_hash
;
133 static FcFilePathInfo
134 FcFilePathInfoGet (const FcChar8
*path
)
139 slash
= FcStrLastSlash (path
);
143 i
.dir_len
= slash
- path
;
150 i
.dir
= (const FcChar8
*) ".";
154 i
.base_hash
= FcCacheHash (i
.base
, -1);
159 FcGlobalCacheCreate (void)
161 FcGlobalCache
*cache
;
164 cache
= malloc (sizeof (FcGlobalCache
));
167 FcMemAlloc (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
168 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
171 cache
->referenced
= 0;
172 cache
->updated
= FcFalse
;
173 cache
->broken
= FcFalse
;
178 FcGlobalCacheDestroy (FcGlobalCache
*cache
)
180 FcGlobalCacheDir
*d
, *next
;
183 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
185 for (d
= cache
->ents
[h
]; d
; d
= next
)
188 FcGlobalCacheDirDestroy (d
);
191 FcMemFree (FC_MEM_CACHE
, sizeof (FcGlobalCache
));
196 * Cache file syntax is quite simple:
198 * "file_name" id time "font_name" \n
202 FcGlobalCacheLoad (FcGlobalCache
*cache
,
203 const FcChar8
*cache_file
)
206 FcChar8 file_buf
[8192], *file
;
209 FcChar8 name_buf
[8192], *name
;
210 FcGlobalCacheInfo
*info
;
212 f
= fopen ((char *) cache_file
, "r");
216 cache
->updated
= FcFalse
;
219 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
220 FcCacheReadInt (f
, &id
) &&
221 FcCacheReadTime (f
, &time
) &&
222 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
224 if (FcDebug () & FC_DBG_CACHEV
)
225 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file
, name
);
226 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
227 info
= FcGlobalCacheDirAdd (cache
, file
, time
, FcFalse
, FcTrue
);
229 info
= FcGlobalCacheFileAdd (cache
, file
, id
, time
, name
, FcFalse
);
231 cache
->broken
= FcTrue
;
234 if (FcDebug () & FC_DBG_CACHE_REF
)
235 printf ("FcGlobalCacheLoad entry %d %s\n",
236 cache
->entries
, file
);
237 if (file
!= file_buf
)
239 if (name
!= name_buf
)
244 if (file
&& file
!= file_buf
)
246 if (name
&& name
!= name_buf
)
252 FcGlobalCacheUpdate (FcGlobalCache
*cache
,
257 const FcChar8
*match
;
259 FcGlobalCacheInfo
*info
;
263 if (stat ((char *) file
, &statb
) < 0)
265 if (S_ISDIR (statb
.st_mode
))
266 info
= FcGlobalCacheDirAdd (cache
, file
, statb
.st_mtime
,
269 info
= FcGlobalCacheFileAdd (cache
, file
, id
, statb
.st_mtime
,
273 FcGlobalCacheReferenced (cache
, info
);
274 cache
->updated
= FcTrue
;
277 cache
->broken
= FcTrue
;
282 FcGlobalCacheSave (FcGlobalCache
*cache
,
283 const FcChar8
*cache_file
)
286 int dir_hash
, file_hash
;
287 FcGlobalCacheDir
*dir
;
288 FcGlobalCacheFile
*file
;
291 if (!cache
->updated
&& cache
->referenced
== cache
->entries
)
297 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
298 /* Set-UID programs can't safely update the cache */
299 if (getuid () != geteuid ())
303 atomic
= FcAtomicCreate (cache_file
);
306 if (!FcAtomicLock (atomic
))
308 f
= fopen ((char *) FcAtomicNewFile(atomic
), "w");
312 for (dir_hash
= 0; dir_hash
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; dir_hash
++)
314 for (dir
= cache
->ents
[dir_hash
]; dir
; dir
= dir
->next
)
316 if (!dir
->info
.referenced
)
318 if (!FcCacheWriteString (f
, dir
->info
.file
))
320 if (PUTC (' ', f
) == EOF
)
322 if (!FcCacheWriteInt (f
, 0))
324 if (PUTC (' ', f
) == EOF
)
326 if (!FcCacheWriteTime (f
, dir
->info
.time
))
328 if (PUTC (' ', f
) == EOF
)
330 if (!FcCacheWriteString (f
, (FcChar8
*) FC_FONT_FILE_DIR
))
332 if (PUTC ('\n', f
) == EOF
)
335 for (file_hash
= 0; file_hash
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; file_hash
++)
337 for (file
= dir
->ents
[file_hash
]; file
; file
= file
->next
)
339 if (!file
->info
.referenced
)
341 if (!FcCacheWritePath (f
, dir
->info
.file
, file
->info
.file
))
343 if (PUTC (' ', f
) == EOF
)
345 if (!FcCacheWriteInt (f
, file
->id
< 0 ? 0 : file
->id
))
347 if (PUTC (' ', f
) == EOF
)
349 if (!FcCacheWriteTime (f
, file
->info
.time
))
351 if (PUTC (' ', f
) == EOF
)
353 if (!FcCacheWriteString (f
, file
->name
))
355 if (PUTC ('\n', f
) == EOF
)
362 if (fclose (f
) == EOF
)
365 if (!FcAtomicReplaceOrig (atomic
))
368 FcAtomicUnlock (atomic
);
369 FcAtomicDestroy (atomic
);
371 cache
->updated
= FcFalse
;
377 FcAtomicDeleteNew (atomic
);
379 FcAtomicUnlock (atomic
);
381 FcAtomicDestroy (atomic
);
388 * Find the next presumably-mmapable offset after the current file
392 FcCacheNextOffset(int fd
)
395 w
= lseek(fd
, 0, SEEK_END
);
397 if (w
% PAGESIZE
== 0)
400 return ((w
/ PAGESIZE
)+1)*PAGESIZE
;
403 /* will go away once we use config->cache */
404 #define CACHE_DEFAULT_TMPDIR "/tmp"
405 #define CACHE_DEFAULT_NAME "/fontconfig-mmap"
407 FcCacheFilename(void)
410 static char * name
= 0;
416 name
= CACHE_DEFAULT_NAME
;
419 char * tmpname
= getenv("TMPDIR");
420 char * logname
= getenv("LOGNAME");
422 tmpname
= CACHE_DEFAULT_TMPDIR
;
424 name
= malloc(strlen(CACHE_DEFAULT_NAME
) +
426 (logname
? strlen(logname
) : 0) + 5);
427 strcpy(name
, tmpname
);
428 strcat(name
, CACHE_DEFAULT_NAME
);
430 strcat(name
, logname
? logname
: "");
436 * Wipe out static state.
441 FcFontSetClearStatic();
442 FcPatternClearStatic();
443 FcValueListClearStatic();
444 FcObjectClearStatic();
445 FcMatrixClearStatic();
446 FcCharSetClearStatic();
447 FcLangSetClearStatic();
451 * Trigger the counting phase: this tells us how much to allocate.
454 FcCachePrepareSerialize (FcConfig
* config
)
457 for (i
= FcSetSystem
; i
<= FcSetApplication
; i
++)
458 if (config
->fonts
[i
] && !FcFontSetPrepareSerialize(config
->fonts
[i
]))
463 /* allocate and populate static structures */
465 FcCacheSerialize (FcConfig
* config
)
468 for (i
= FcSetSystem
; i
<= FcSetApplication
; i
++)
469 if (config
->fonts
[i
] && !FcFontSetSerialize(config
->fonts
[i
]))
474 /* get the current arch name */
475 /* caller is responsible for freeing returned pointer */
477 FcCacheGetCurrentArch (void)
480 char * current_arch_machine_name
;
484 current_arch_machine_name
= strdup(b
.machine
);
485 /* if (getenv ("FAKE_ARCH")) // testing purposes
486 current_arch_machine_name = strdup(getenv("FAKE_ARCH")); */
487 return current_arch_machine_name
;
490 /* return the address of the segment for the provided arch,
491 * or -1 if arch not found */
493 FcCacheSkipToArch (int fd
, const char * arch
)
495 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
496 off_t current_arch_start
= 0;
498 /* skip arches that are not the current arch */
503 lseek (fd
, current_arch_start
, SEEK_SET
);
504 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
505 sizeof (candidate_arch_machine_name
)) == 0)
507 if (FcCacheReadString (fd
, bytes_to_skip
, 7) == 0)
509 bs
= a64l(bytes_to_skip
);
513 if (strcmp (candidate_arch_machine_name
, arch
)==0)
515 current_arch_start
+= bs
;
518 if (strcmp (candidate_arch_machine_name
, arch
)!=0)
521 return current_arch_start
;
524 /* Cuts out the segment at the file pointer (moves everything else
525 * down to cover it), and leaves the file pointer at the end of the
527 #define BUF_SIZE 8192
530 FcCacheMoveDown (int fd
, off_t start
)
532 char * buf
= malloc (BUF_SIZE
);
533 char candidate_arch_machine_name
[64], bytes_to_skip
[7];
535 int c
, bytes_skipped
;
540 lseek (fd
, start
, SEEK_SET
);
541 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
542 sizeof (candidate_arch_machine_name
)) == 0)
544 if (FcCacheReadString (fd
, bytes_to_skip
, 7) == 0)
547 bs
= a64l(bytes_to_skip
);
554 lseek (fd
, start
+bs
+bytes_skipped
, SEEK_SET
);
555 if ((c
= read (fd
, buf
, BUF_SIZE
)) <= 0)
557 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
558 if (write (fd
, buf
, c
) < 0)
563 lseek (fd
, start
+bytes_skipped
, SEEK_SET
);
574 /* read serialized state from the cache file */
576 FcCacheRead (FcConfig
*config
)
580 char * current_arch_machine_name
;
581 char candidate_arch_machine_name
[64], bytes_in_block
[7];
582 off_t current_arch_start
= 0;
587 fd
= open(FcCacheFilename(), O_RDONLY
);
591 current_arch_machine_name
= FcCacheGetCurrentArch();
592 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
593 if (current_arch_start
< 0)
596 lseek (fd
, current_arch_start
, SEEK_SET
);
597 if (FcCacheReadString (fd
, candidate_arch_machine_name
,
598 sizeof (candidate_arch_machine_name
)) == 0)
600 if (FcCacheReadString (fd
, bytes_in_block
, 7) == 0)
603 // sanity check for endianness issues
604 read(fd
, &metadata
, sizeof(FcCache
));
605 if (metadata
.magic
!= FC_CACHE_MAGIC
)
608 if (!FcObjectRead(fd
, metadata
)) goto bail1
;
609 if (!FcStrSetRead(fd
, metadata
)) goto bail1
;
610 if (!FcCharSetRead(fd
, metadata
)) goto bail1
;
611 if (!FcMatrixRead(fd
, metadata
)) goto bail1
;
612 if (!FcLangSetRead(fd
, metadata
)) goto bail1
;
613 if (!FcValueListRead(fd
, metadata
)) goto bail1
;
614 if (!FcPatternEltRead(fd
, metadata
)) goto bail1
;
615 if (!FcPatternRead(fd
, metadata
)) goto bail1
;
616 if (!FcFontSetRead(fd
, config
, metadata
)) goto bail1
;
618 free (current_arch_machine_name
);
622 for (i
= FcSetSystem
; i
<= FcSetApplication
; i
++)
623 config
->fonts
[i
] = 0;
626 free (current_arch_machine_name
);
630 /* write serialized state to the cache file */
632 FcCacheWrite (FcConfig
* config
)
636 off_t current_arch_start
= 0, truncate_to
;
637 char * current_arch_machine_name
, bytes_written
[7] = "dedbef";
639 if (!FcCachePrepareSerialize (config
))
642 if (!FcCacheSerialize (config
))
645 fd
= open(FcCacheFilename(), O_RDWR
| O_CREAT
, 0666);
649 current_arch_machine_name
= FcCacheGetCurrentArch();
650 current_arch_start
= FcCacheSkipToArch(fd
, current_arch_machine_name
);
651 if (current_arch_start
< 0)
652 current_arch_start
= FcCacheNextOffset (fd
);
654 if (!FcCacheMoveDown(fd
, current_arch_start
))
657 current_arch_start
= lseek(fd
, 0, SEEK_CUR
);
658 if (ftruncate (fd
, current_arch_start
) == -1)
661 /* reserve space for arch, count & metadata */
662 if (!FcCacheWriteString (fd
, current_arch_machine_name
))
664 if (!FcCacheWriteString (fd
, bytes_written
))
666 memset (&metadata
, 0, sizeof(FcCache
));
667 metadata
.magic
= FC_CACHE_MAGIC
;
668 write(fd
, &metadata
, sizeof(FcCache
));
670 if (!FcFontSetWrite(fd
, config
, &metadata
)) goto bail
;
671 if (!FcPatternWrite(fd
, &metadata
)) goto bail
;
672 if (!FcPatternEltWrite(fd
, &metadata
)) goto bail
;
673 if (!FcValueListWrite(fd
, &metadata
)) goto bail
;
674 if (!FcLangSetWrite(fd
, &metadata
)) goto bail
;
675 if (!FcCharSetWrite(fd
, &metadata
)) goto bail
;
676 if (!FcMatrixWrite(fd
, &metadata
)) goto bail
;
677 if (!FcStrSetWrite(fd
, &metadata
)) goto bail
;
678 if (!FcObjectWrite(fd
, &metadata
)) goto bail
;
680 /* now write the address of the next offset */
681 truncate_to
= FcCacheNextOffset(fd
) - current_arch_start
;
682 lseek(fd
, current_arch_start
+ strlen(current_arch_machine_name
)+1,
684 strcpy (bytes_written
, l64a(truncate_to
));
685 if (!FcCacheWriteString (fd
, bytes_written
))
688 /* now rewrite metadata & truncate file */
689 if (write(fd
, &metadata
, sizeof(FcCache
)) != sizeof (FcCache
))
691 if (ftruncate (fd
, current_arch_start
+ truncate_to
) == -1)
698 free (current_arch_machine_name
);
702 /* if true, ignore the cache file */
704 FcCacheForce (FcBool f
)