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)