]> git.wh0rd.org - fontconfig.git/blame - src/fccache.c
#ifdef out old cache stuff, replace with first version of new mmapping
[fontconfig.git] / src / fccache.c
CommitLineData
24330d27 1/*
4bd4418a 2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
24330d27 3 *
46b51147 4 * Copyright © 2000 Keith Packard
24330d27
KP
5 *
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.
15 *
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.
23 */
24
212c9f43
PL
25#include <fcntl.h>
26#include <sys/mman.h>
27#include <sys/utsname.h>
24330d27
KP
28#include "fcint.h"
29
212c9f43 30#define PAGESIZE 8192
fa244f3d 31
212c9f43 32static FcBool force;
24330d27 33
ccb3e93b 34static FcChar8 *
212c9f43 35FcCacheReadString (int fd, FcChar8 *dest, int len)
24330d27 36{
212c9f43 37 FcChar8 c;
ccb3e93b 38 FcBool escape;
ccb3e93b
KP
39 int size;
40 int i;
24330d27 41
24330d27
KP
42 if (len == 0)
43 return FcFalse;
44
ccb3e93b
KP
45 size = len;
46 i = 0;
24330d27 47 escape = FcFalse;
212c9f43 48 while (read (fd, &c, 1) == 1)
24330d27
KP
49 {
50 if (!escape)
51 {
52 switch (c) {
53 case '"':
ccb3e93b
KP
54 c = '\0';
55 break;
24330d27
KP
56 case '\\':
57 escape = FcTrue;
58 continue;
59 }
60 }
ccb3e93b
KP
61 if (i == size)
62 {
212c9f43
PL
63 dest[i++] = 0;
64 return dest;
ccb3e93b 65 }
212c9f43 66 dest[i++] = c;
ccb3e93b 67 if (c == '\0')
212c9f43 68 return dest;
24330d27
KP
69 escape = FcFalse;
70 }
ccb3e93b 71 return 0;
24330d27
KP
72}
73
74static FcBool
212c9f43 75FcCacheWriteString (int fd, const FcChar8 *chars)
327a7fd4 76{
212c9f43 77 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
327a7fd4 78 return FcFalse;
327a7fd4
KP
79 return FcTrue;
80}
81
212c9f43 82#if 0
327a7fd4
KP
83/*
84 * Verify the saved timestamp for a file
85 */
86FcBool
a8386abc 87FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info)
327a7fd4
KP
88{
89 struct stat statb;
90
a8386abc 91 if (stat ((char *) file, &statb) < 0)
327a7fd4
KP
92 {
93 if (FcDebug () & FC_DBG_CACHE)
a8386abc 94 printf (" file %s missing\n", file);
327a7fd4
KP
95 return FcFalse;
96 }
97 if (statb.st_mtime != info->time)
98 {
99 if (FcDebug () & FC_DBG_CACHE)
100 printf (" timestamp mismatch (was %d is %d)\n",
101 (int) info->time, (int) statb.st_mtime);
102 return FcFalse;
103 }
104 return FcTrue;
105}
106
107void
108FcGlobalCacheReferenced (FcGlobalCache *cache,
109 FcGlobalCacheInfo *info)
110{
111 if (!info->referenced)
112 {
113 info->referenced = FcTrue;
114 cache->referenced++;
115 if (FcDebug () & FC_DBG_CACHE_REF)
116 printf ("Reference %d %s\n", cache->referenced, info->file);
117 }
118}
119
120/*
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
124 */
125
126typedef struct _FcFilePathInfo {
127 const FcChar8 *dir;
128 int dir_len;
129 const FcChar8 *base;
130 unsigned int base_hash;
131} FcFilePathInfo;
132
133static FcFilePathInfo
134FcFilePathInfoGet (const FcChar8 *path)
135{
136 FcFilePathInfo i;
137 FcChar8 *slash;
138
daeed6e0 139 slash = FcStrLastSlash (path);
327a7fd4
KP
140 if (slash)
141 {
142 i.dir = path;
143 i.dir_len = slash - path;
144 if (!i.dir_len)
145 i.dir_len = 1;
146 i.base = slash + 1;
147 }
148 else
149 {
150 i.dir = (const FcChar8 *) ".";
151 i.dir_len = 1;
152 i.base = path;
153 }
c8d5753c 154 i.base_hash = FcCacheHash (i.base, -1);
327a7fd4
KP
155 return i;
156}
157
327a7fd4
KP
158FcGlobalCache *
159FcGlobalCacheCreate (void)
24330d27 160{
327a7fd4
KP
161 FcGlobalCache *cache;
162 int h;
24330d27 163
327a7fd4 164 cache = malloc (sizeof (FcGlobalCache));
24330d27
KP
165 if (!cache)
166 return 0;
9dac3c59 167 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
327a7fd4 168 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27
KP
169 cache->ents[h] = 0;
170 cache->entries = 0;
171 cache->referenced = 0;
172 cache->updated = FcFalse;
bb356b68 173 cache->broken = FcFalse;
24330d27
KP
174 return cache;
175}
176
177void
327a7fd4 178FcGlobalCacheDestroy (FcGlobalCache *cache)
24330d27 179{
327a7fd4
KP
180 FcGlobalCacheDir *d, *next;
181 int h;
24330d27 182
327a7fd4 183 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27 184 {
327a7fd4 185 for (d = cache->ents[h]; d; d = next)
24330d27 186 {
327a7fd4
KP
187 next = d->next;
188 FcGlobalCacheDirDestroy (d);
24330d27
KP
189 }
190 }
9dac3c59 191 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
24330d27
KP
192 free (cache);
193}
194
327a7fd4
KP
195/*
196 * Cache file syntax is quite simple:
197 *
198 * "file_name" id time "font_name" \n
199 */
200
24330d27 201void
327a7fd4
KP
202FcGlobalCacheLoad (FcGlobalCache *cache,
203 const FcChar8 *cache_file)
24330d27 204{
327a7fd4
KP
205 FILE *f;
206 FcChar8 file_buf[8192], *file;
207 int id;
208 time_t time;
209 FcChar8 name_buf[8192], *name;
210 FcGlobalCacheInfo *info;
24330d27 211
ccb3e93b 212 f = fopen ((char *) cache_file, "r");
24330d27
KP
213 if (!f)
214 return;
215
216 cache->updated = FcFalse;
ccb3e93b
KP
217 file = 0;
218 name = 0;
327a7fd4
KP
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))))
24330d27 223 {
327a7fd4
KP
224 if (FcDebug () & FC_DBG_CACHEV)
225 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
226 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
c4ab52dc 227 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue);
327a7fd4
KP
228 else
229 info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
230 if (!info)
231 cache->broken = FcTrue;
232 else
233 cache->entries++;
234 if (FcDebug () & FC_DBG_CACHE_REF)
235 printf ("FcGlobalCacheLoad entry %d %s\n",
236 cache->entries, file);
ccb3e93b
KP
237 if (file != file_buf)
238 free (file);
239 if (name != name_buf)
240 free (name);
241 file = 0;
242 name = 0;
24330d27 243 }
ccb3e93b
KP
244 if (file && file != file_buf)
245 free (file);
246 if (name && name != name_buf)
247 free (name);
24330d27
KP
248 fclose (f);
249}
250
251FcBool
327a7fd4
KP
252FcGlobalCacheUpdate (FcGlobalCache *cache,
253 const FcChar8 *file,
254 int id,
255 const FcChar8 *name)
24330d27 256{
327a7fd4
KP
257 const FcChar8 *match;
258 struct stat statb;
259 FcGlobalCacheInfo *info;
24330d27
KP
260
261 match = file;
262
ccb3e93b 263 if (stat ((char *) file, &statb) < 0)
24330d27 264 return FcFalse;
327a7fd4
KP
265 if (S_ISDIR (statb.st_mode))
266 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
c4ab52dc 267 FcTrue, FcTrue);
327a7fd4
KP
268 else
269 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
270 name, FcTrue);
271 if (info)
24330d27 272 {
327a7fd4
KP
273 FcGlobalCacheReferenced (cache, info);
274 cache->updated = FcTrue;
24330d27 275 }
327a7fd4
KP
276 else
277 cache->broken = FcTrue;
278 return info != 0;
24330d27
KP
279}
280
281FcBool
327a7fd4
KP
282FcGlobalCacheSave (FcGlobalCache *cache,
283 const FcChar8 *cache_file)
24330d27 284{
327a7fd4
KP
285 FILE *f;
286 int dir_hash, file_hash;
287 FcGlobalCacheDir *dir;
288 FcGlobalCacheFile *file;
289 FcAtomic *atomic;
24330d27
KP
290
291 if (!cache->updated && cache->referenced == cache->entries)
292 return FcTrue;
293
327a7fd4
KP
294 if (cache->broken)
295 return FcFalse;
296
daeed6e0 297#if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
a391da8f
KP
298 /* Set-UID programs can't safely update the cache */
299 if (getuid () != geteuid ())
300 return FcFalse;
daeed6e0 301#endif
a391da8f 302
134f6011
KP
303 atomic = FcAtomicCreate (cache_file);
304 if (!atomic)
24330d27 305 goto bail0;
134f6011 306 if (!FcAtomicLock (atomic))
24330d27 307 goto bail1;
134f6011 308 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
24330d27
KP
309 if (!f)
310 goto bail2;
311
327a7fd4 312 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
24330d27 313 {
327a7fd4 314 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
24330d27 315 {
327a7fd4 316 if (!dir->info.referenced)
24330d27 317 continue;
327a7fd4 318 if (!FcCacheWriteString (f, dir->info.file))
24330d27 319 goto bail4;
fa244f3d 320 if (PUTC (' ', f) == EOF)
24330d27 321 goto bail4;
327a7fd4 322 if (!FcCacheWriteInt (f, 0))
24330d27 323 goto bail4;
fa244f3d 324 if (PUTC (' ', f) == EOF)
24330d27 325 goto bail4;
327a7fd4 326 if (!FcCacheWriteTime (f, dir->info.time))
24330d27 327 goto bail4;
fa244f3d 328 if (PUTC (' ', f) == EOF)
24330d27 329 goto bail4;
327a7fd4 330 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
24330d27 331 goto bail4;
fa244f3d 332 if (PUTC ('\n', f) == EOF)
24330d27 333 goto bail4;
327a7fd4
KP
334
335 for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
336 {
337 for (file = dir->ents[file_hash]; file; file = file->next)
338 {
339 if (!file->info.referenced)
340 continue;
341 if (!FcCacheWritePath (f, dir->info.file, file->info.file))
342 goto bail4;
fa244f3d 343 if (PUTC (' ', f) == EOF)
327a7fd4
KP
344 goto bail4;
345 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
346 goto bail4;
fa244f3d 347 if (PUTC (' ', f) == EOF)
327a7fd4
KP
348 goto bail4;
349 if (!FcCacheWriteTime (f, file->info.time))
350 goto bail4;
fa244f3d 351 if (PUTC (' ', f) == EOF)
327a7fd4
KP
352 goto bail4;
353 if (!FcCacheWriteString (f, file->name))
354 goto bail4;
fa244f3d 355 if (PUTC ('\n', f) == EOF)
327a7fd4
KP
356 goto bail4;
357 }
358 }
24330d27
KP
359 }
360 }
361
362 if (fclose (f) == EOF)
363 goto bail3;
364
134f6011 365 if (!FcAtomicReplaceOrig (atomic))
24330d27
KP
366 goto bail3;
367
134f6011
KP
368 FcAtomicUnlock (atomic);
369 FcAtomicDestroy (atomic);
370
24330d27
KP
371 cache->updated = FcFalse;
372 return FcTrue;
373
374bail4:
375 fclose (f);
376bail3:
134f6011 377 FcAtomicDeleteNew (atomic);
24330d27 378bail2:
134f6011 379 FcAtomicUnlock (atomic);
24330d27 380bail1:
134f6011 381 FcAtomicDestroy (atomic);
24330d27
KP
382bail0:
383 return FcFalse;
384}
212c9f43 385#endif
24330d27 386
212c9f43
PL
387/*
388 * Find the next presumably-mmapable offset after the current file
389 * pointer.
390 */
391int
392FcCacheNextOffset(int fd)
179c3995 393{
212c9f43
PL
394 off_t w;
395 w = lseek(fd, 0, SEEK_END);
179c3995 396
212c9f43
PL
397 if (w % PAGESIZE == 0)
398 return w;
399 else
400 return ((w / PAGESIZE)+1)*PAGESIZE;
179c3995
KP
401}
402
212c9f43
PL
403/* will go away once we use config->cache */
404#define CACHE_DEFAULT_TMPDIR "/tmp"
405#define CACHE_DEFAULT_NAME "/fontconfig-mmap"
406static char *
407FcCacheFilename(void)
24330d27 408{
212c9f43
PL
409 struct utsname b;
410 static char * name = 0;
24330d27 411
212c9f43
PL
412 if (name)
413 return name;
24330d27 414
212c9f43
PL
415 if (uname(&b) == -1)
416 name = CACHE_DEFAULT_NAME;
417 else
24330d27 418 {
212c9f43
PL
419 char * tmpname = getenv("TMPDIR");
420 char * logname = getenv("LOGNAME");
421 if (!tmpname)
422 tmpname = CACHE_DEFAULT_TMPDIR;
24330d27 423
212c9f43
PL
424 name = malloc(strlen(CACHE_DEFAULT_NAME) +
425 strlen(tmpname) +
426 (logname ? strlen(logname) : 0) + 5);
427 strcpy(name, tmpname);
428 strcat(name, CACHE_DEFAULT_NAME);
429 strcat(name, "-");
430 strcat(name, logname ? logname : "");
179c3995 431 }
212c9f43 432 return name;
24330d27 433}
cd2ec1a9 434
212c9f43
PL
435/*
436 * Wipe out static state.
437 */
cd2ec1a9
PL
438void
439FcCacheClearStatic()
440{
441 FcFontSetClearStatic();
442 FcPatternClearStatic();
443 FcValueListClearStatic();
444 FcObjectClearStatic();
445 FcMatrixClearStatic();
446 FcCharSetClearStatic();
447 FcLangSetClearStatic();
448}
449
212c9f43
PL
450/*
451 * Trigger the counting phase: this tells us how much to allocate.
452 */
cd2ec1a9
PL
453FcBool
454FcCachePrepareSerialize (FcConfig * config)
455{
456 int i;
457 for (i = FcSetSystem; i <= FcSetApplication; i++)
458 if (config->fonts[i] && !FcFontSetPrepareSerialize(config->fonts[i]))
459 return FcFalse;
460 return FcTrue;
461}
462
212c9f43 463/* allocate and populate static structures */
cd2ec1a9
PL
464FcBool
465FcCacheSerialize (FcConfig * config)
466{
467 int i;
468 for (i = FcSetSystem; i <= FcSetApplication; i++)
469 if (config->fonts[i] && !FcFontSetSerialize(config->fonts[i]))
470 return FcFalse;
471 return FcTrue;
472}
212c9f43
PL
473
474/* get the current arch name */
475/* caller is responsible for freeing returned pointer */
476static char *
477FcCacheGetCurrentArch (void)
478{
479 struct utsname b;
480 char * current_arch_machine_name;
481
482 if (uname(&b) == -1)
483 return FcFalse;
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;
488}
489
490/* return the address of the segment for the provided arch,
491 * or -1 if arch not found */
492static off_t
493FcCacheSkipToArch (int fd, const char * arch)
494{
495 char candidate_arch_machine_name[64], bytes_to_skip[7];
496 off_t current_arch_start = 0;
497
498 /* skip arches that are not the current arch */
499 while (1)
500 {
501 long bs;
502
503 lseek (fd, current_arch_start, SEEK_SET);
504 if (FcCacheReadString (fd, candidate_arch_machine_name,
505 sizeof (candidate_arch_machine_name)) == 0)
506 break;
507 if (FcCacheReadString (fd, bytes_to_skip, 7) == 0)
508 break;
509 bs = a64l(bytes_to_skip);
510 if (bs == 0)
511 break;
512
513 if (strcmp (candidate_arch_machine_name, arch)==0)
514 break;
515 current_arch_start += bs;
516 }
517
518 if (strcmp (candidate_arch_machine_name, arch)!=0)
519 return -1;
520
521 return current_arch_start;
522}
523
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
526 * file. */
527#define BUF_SIZE 8192
528
529static FcBool
530FcCacheMoveDown (int fd, off_t start)
531{
532 char * buf = malloc (BUF_SIZE);
533 char candidate_arch_machine_name[64], bytes_to_skip[7];
534 long bs; off_t pos;
535 int c, bytes_skipped;
536
537 if (!buf)
538 return FcFalse;
539
540 lseek (fd, start, SEEK_SET);
541 if (FcCacheReadString (fd, candidate_arch_machine_name,
542 sizeof (candidate_arch_machine_name)) == 0)
543 goto done;
544 if (FcCacheReadString (fd, bytes_to_skip, 7) == 0)
545 goto done;
546
547 bs = a64l(bytes_to_skip);
548 if (bs == 0)
549 goto done;
550
551 bytes_skipped = 0;
552 do
553 {
554 lseek (fd, start+bs+bytes_skipped, SEEK_SET);
555 if ((c = read (fd, buf, BUF_SIZE)) <= 0)
556 break;
557 lseek (fd, start+bytes_skipped, SEEK_SET);
558 if (write (fd, buf, c) < 0)
559 goto bail;
560 bytes_skipped += c;
561 }
562 while (c > 0);
563 lseek (fd, start+bytes_skipped, SEEK_SET);
564
565 done:
566 free (buf);
567 return FcTrue;
568
569 bail:
570 free (buf);
571 return FcFalse;
572}
573
574/* read serialized state from the cache file */
575FcBool
576FcCacheRead (FcConfig *config)
577{
578 int fd, i;
579 FcCache metadata;
580 char * current_arch_machine_name;
581 char candidate_arch_machine_name[64], bytes_in_block[7];
582 off_t current_arch_start = 0;
583
584 if (force)
585 return FcFalse;
586
587 fd = open(FcCacheFilename(), O_RDONLY);
588 if (fd == -1)
589 return FcFalse;
590
591 current_arch_machine_name = FcCacheGetCurrentArch();
592 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
593 if (current_arch_start < 0)
594 goto bail;
595
596 lseek (fd, current_arch_start, SEEK_SET);
597 if (FcCacheReadString (fd, candidate_arch_machine_name,
598 sizeof (candidate_arch_machine_name)) == 0)
599 goto bail;
600 if (FcCacheReadString (fd, bytes_in_block, 7) == 0)
601 goto bail;
602
603 // sanity check for endianness issues
604 read(fd, &metadata, sizeof(FcCache));
605 if (metadata.magic != FC_CACHE_MAGIC)
606 goto bail;
607
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;
617 close(fd);
618 free (current_arch_machine_name);
619 return FcTrue;
620
621 bail1:
622 for (i = FcSetSystem; i <= FcSetApplication; i++)
623 config->fonts[i] = 0;
624 close(fd);
625 bail:
626 free (current_arch_machine_name);
627 return FcFalse;
628}
629
630/* write serialized state to the cache file */
631FcBool
632FcCacheWrite (FcConfig * config)
633{
634 int fd;
635 FcCache metadata;
636 off_t current_arch_start = 0, truncate_to;
637 char * current_arch_machine_name, bytes_written[7] = "dedbef";
638
639 if (!FcCachePrepareSerialize (config))
640 return FcFalse;
641
642 if (!FcCacheSerialize (config))
643 return FcFalse;
644
645 fd = open(FcCacheFilename(), O_RDWR | O_CREAT, 0666);
646 if (fd == -1)
647 return FcFalse;
648
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);
653
654 if (!FcCacheMoveDown(fd, current_arch_start))
655 goto bail;
656
657 current_arch_start = lseek(fd, 0, SEEK_CUR);
658 if (ftruncate (fd, current_arch_start) == -1)
659 goto bail;
660
661 /* reserve space for arch, count & metadata */
662 if (!FcCacheWriteString (fd, current_arch_machine_name))
663 goto bail;
664 if (!FcCacheWriteString (fd, bytes_written))
665 goto bail;
666 memset (&metadata, 0, sizeof(FcCache));
667 metadata.magic = FC_CACHE_MAGIC;
668 write(fd, &metadata, sizeof(FcCache));
669
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;
679
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,
683 SEEK_SET);
684 strcpy (bytes_written, l64a(truncate_to));
685 if (!FcCacheWriteString (fd, bytes_written))
686 goto bail;
687
688 /* now rewrite metadata & truncate file */
689 if (write(fd, &metadata, sizeof(FcCache)) != sizeof (FcCache))
690 goto bail;
691 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
692 goto bail;
693
694 close(fd);
695 return FcTrue;
696
697 bail:
698 free (current_arch_machine_name);
699 return FcFalse;
700}
701
702/* if true, ignore the cache file */
703void
704FcCacheForce (FcBool f)
705{
706 force = f;
707}