]> git.wh0rd.org - fontconfig.git/blame - src/fccache.c
Emit and verify machine signature (sizeof (stuff) + endianness) in cache
[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 25#include <fcntl.h>
4262e0b3 26#include <dirent.h>
212c9f43
PL
27#include <sys/mman.h>
28#include <sys/utsname.h>
24330d27
KP
29#include "fcint.h"
30
2dbe7597
PL
31#define ENDIAN_TEST 0x12345678
32#define MACHINE_SIGNATURE_SIZE 9 + 5*19 + 1
33
34static char *
35FcCacheProduceMachineSignature (void);
36
1b7be377
PL
37/*
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.
42 */
43
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)
47#else
48#define GETC(f) getc(f)
49#define PUTC(c,f) putc(c,f)
50#endif
51
52#define FC_DBG_CACHE_REF 1024
53
212c9f43 54#define PAGESIZE 8192
fa244f3d 55
212c9f43 56static FcBool force;
24330d27 57
ccb3e93b 58static FcChar8 *
1b7be377
PL
59FcCacheReadString (FILE *f, FcChar8 *dest, int len)
60{
61 int c;
62 FcBool escape;
63 FcChar8 *d;
64 int size;
65 int i;
66
67 while ((c = GETC (f)) != EOF)
68 if (c == '"')
69 break;
70 if (c == EOF)
71 return FcFalse;
72 if (len == 0)
73 return FcFalse;
74
75 size = len;
76 i = 0;
77 d = dest;
78 escape = FcFalse;
79 while ((c = GETC (f)) != EOF)
80 {
81 if (!escape)
82 {
83 switch (c) {
84 case '"':
85 c = '\0';
86 break;
87 case '\\':
88 escape = FcTrue;
89 continue;
90 }
91 }
92 if (i == size)
93 {
94 FcChar8 *new = malloc (size * 2); /* freed in caller */
95 if (!new)
96 break;
97 memcpy (new, d, size);
98 size *= 2;
99 if (d != dest)
100 free (d);
101 d = new;
102 }
103 d[i++] = c;
104 if (c == '\0')
105 return d;
106 escape = FcFalse;
107 }
108 if (d != dest)
109 free (d);
110 return 0;
111}
112
113static FcBool
114FcCacheReadUlong (FILE *f, unsigned long *dest)
115{
116 unsigned long t;
117 int c;
118
119 while ((c = GETC (f)) != EOF)
120 {
121 if (!isspace (c))
122 break;
123 }
124 if (c == EOF)
125 return FcFalse;
126 t = 0;
127 for (;;)
128 {
129 if (c == EOF || isspace (c))
130 break;
131 if (!isdigit (c))
132 return FcFalse;
133 t = t * 10 + (c - '0');
134 c = GETC (f);
135 }
136 *dest = t;
137 return FcTrue;
138}
139
140static FcBool
141FcCacheReadInt (FILE *f, int *dest)
142{
143 unsigned long t;
144 FcBool ret;
145
146 ret = FcCacheReadUlong (f, &t);
147 if (ret)
148 *dest = (int) t;
149 return ret;
150}
151
152static FcBool
153FcCacheReadTime (FILE *f, time_t *dest)
154{
155 unsigned long t;
156 FcBool ret;
157
158 ret = FcCacheReadUlong (f, &t);
159 if (ret)
160 *dest = (time_t) t;
161 return ret;
162}
163
164static FcBool
165FcCacheWriteChars (FILE *f, const FcChar8 *chars)
166{
167 FcChar8 c;
168 while ((c = *chars++))
169 {
170 switch (c) {
171 case '"':
172 case '\\':
173 if (PUTC ('\\', f) == EOF)
174 return FcFalse;
175 /* fall through */
176 default:
177 if (PUTC (c, f) == EOF)
178 return FcFalse;
179 }
180 }
181 return FcTrue;
182}
183
184static FcBool
185FcCacheWriteString (FILE *f, const FcChar8 *string)
186{
187
188 if (PUTC ('"', f) == EOF)
189 return FcFalse;
190 if (!FcCacheWriteChars (f, string))
191 return FcFalse;
192 if (PUTC ('"', f) == EOF)
193 return FcFalse;
194 return FcTrue;
195}
196
197static FcBool
198FcCacheWritePath (FILE *f, const FcChar8 *dir, const FcChar8 *file)
199{
200 if (PUTC ('"', f) == EOF)
201 return FcFalse;
202 if (dir)
203 if (!FcCacheWriteChars (f, dir))
204 return FcFalse;
205#ifdef _WIN32
206 if (dir &&
207 dir[strlen((const char *) dir) - 1] != '/' &&
208 dir[strlen((const char *) dir) - 1] != '\\')
209 {
210 if (!FcCacheWriteChars (f, "\\"))
211 return FcFalse;
212 }
213#else
214 if (dir && dir[strlen((const char *) dir) - 1] != '/')
215 if (PUTC ('/', f) == EOF)
216 return FcFalse;
217#endif
218 if (!FcCacheWriteChars (f, file))
219 return FcFalse;
220 if (PUTC ('"', f) == EOF)
221 return FcFalse;
222 return FcTrue;
223}
224
225static FcBool
226FcCacheWriteUlong (FILE *f, unsigned long t)
227{
228 int pow;
229 unsigned long temp, digit;
230
231 temp = t;
232 pow = 1;
233 while (temp >= 10)
234 {
235 temp /= 10;
236 pow *= 10;
237 }
238 temp = t;
239 while (pow)
240 {
241 digit = temp / pow;
242 if (PUTC ((char) digit + '0', f) == EOF)
243 return FcFalse;
244 temp = temp - pow * digit;
245 pow = pow / 10;
246 }
247 return FcTrue;
248}
249
250static FcBool
251FcCacheWriteInt (FILE *f, int i)
252{
253 return FcCacheWriteUlong (f, (unsigned long) i);
254}
255
256static FcBool
257FcCacheWriteTime (FILE *f, time_t t)
258{
259 return FcCacheWriteUlong (f, (unsigned long) t);
260}
261
262static FcChar8 *
263FcCacheReadString2 (int fd, FcChar8 *dest, int len)
24330d27 264{
212c9f43 265 FcChar8 c;
ccb3e93b 266 FcBool escape;
ccb3e93b
KP
267 int size;
268 int i;
24330d27 269
24330d27
KP
270 if (len == 0)
271 return FcFalse;
272
ccb3e93b
KP
273 size = len;
274 i = 0;
24330d27 275 escape = FcFalse;
212c9f43 276 while (read (fd, &c, 1) == 1)
24330d27
KP
277 {
278 if (!escape)
279 {
280 switch (c) {
281 case '"':
ccb3e93b
KP
282 c = '\0';
283 break;
24330d27
KP
284 case '\\':
285 escape = FcTrue;
286 continue;
287 }
288 }
ccb3e93b
KP
289 if (i == size)
290 {
212c9f43
PL
291 dest[i++] = 0;
292 return dest;
ccb3e93b 293 }
212c9f43 294 dest[i++] = c;
ccb3e93b 295 if (c == '\0')
212c9f43 296 return dest;
24330d27
KP
297 escape = FcFalse;
298 }
ccb3e93b 299 return 0;
24330d27
KP
300}
301
302static FcBool
1b7be377 303FcCacheWriteString2 (int fd, const FcChar8 *chars)
327a7fd4 304{
212c9f43 305 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
327a7fd4 306 return FcFalse;
327a7fd4
KP
307 return FcTrue;
308}
309
1b7be377
PL
310static FcBool
311FcCacheFontSetAdd (FcFontSet *set,
312 FcStrSet *dirs,
313 const FcChar8 *dir,
314 int dir_len,
315 const FcChar8 *file,
316 const FcChar8 *name,
317 FcConfig *config)
318{
319 FcChar8 path_buf[8192], *path;
320 int len;
321 FcBool ret = FcFalse;
322 FcPattern *font;
323 FcPattern *frozen;
324
325 path = path_buf;
326 len = (dir_len + 1 + strlen ((const char *) file) + 1);
327 if (len > sizeof (path_buf))
328 {
329 path = malloc (len); /* freed down below */
330 if (!path)
331 return FcFalse;
332 }
333 strncpy ((char *) path, (const char *) dir, dir_len);
334#ifdef _WIN32
335 if (dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\' )
336 path[dir_len++] = '\\';
337#else
338 if (dir[dir_len - 1] != '/')
339 path[dir_len++] = '/';
340#endif
341 strcpy ((char *) path + dir_len, (const char *) file);
342 if (config && !FcConfigAcceptFilename (config, path))
343 ret = FcTrue;
344 else if (!FcStrCmp (name, FC_FONT_FILE_DIR))
345 {
346 if (FcDebug () & FC_DBG_CACHEV)
347 printf (" dir cache dir \"%s\"\n", path);
348 ret = FcStrSetAdd (dirs, path);
349 }
350 else if (!FcStrCmp (name, FC_FONT_FILE_INVALID))
351 {
352 ret = FcTrue;
353 }
354 else
355 {
356 font = FcNameParse (name);
357 if (font)
358 {
359 FcChar8 *family;
360
361 if (FcDebug () & FC_DBG_CACHEV)
362 printf (" dir cache file \"%s\"\n", file);
363 ret = FcPatternAddString (font, FC_FILE, path);
364 /*
365 * Make sure the pattern has the file name as well as
366 * already containing at least one family name.
367 */
368 if (ret &&
369 FcPatternGetString (font, FC_FAMILY, 0, &family) == FcResultMatch &&
370 (!config || FcConfigAcceptFont (config, font)))
371 {
372 frozen = FcPatternFreeze (font);
373 ret = (frozen != 0);
374 if (ret)
375 ret = FcFontSetAdd (set, frozen);
376 }
377 FcPatternDestroy (font);
378 }
379 }
380 if (path != path_buf) free (path);
381 return ret;
382
383}
384
385static unsigned int
386FcCacheHash (const FcChar8 *string, int len)
387{
388 unsigned int h = 0;
389 FcChar8 c;
390
391 while (len-- && (c = *string++))
392 h = (h << 1) ^ c;
393 return h;
394}
395
327a7fd4
KP
396/*
397 * Verify the saved timestamp for a file
398 */
399FcBool
a8386abc 400FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info)
327a7fd4
KP
401{
402 struct stat statb;
403
a8386abc 404 if (stat ((char *) file, &statb) < 0)
327a7fd4
KP
405 {
406 if (FcDebug () & FC_DBG_CACHE)
a8386abc 407 printf (" file %s missing\n", file);
327a7fd4
KP
408 return FcFalse;
409 }
410 if (statb.st_mtime != info->time)
411 {
412 if (FcDebug () & FC_DBG_CACHE)
413 printf (" timestamp mismatch (was %d is %d)\n",
414 (int) info->time, (int) statb.st_mtime);
415 return FcFalse;
416 }
417 return FcTrue;
418}
419
420void
421FcGlobalCacheReferenced (FcGlobalCache *cache,
422 FcGlobalCacheInfo *info)
423{
424 if (!info->referenced)
425 {
426 info->referenced = FcTrue;
427 cache->referenced++;
428 if (FcDebug () & FC_DBG_CACHE_REF)
429 printf ("Reference %d %s\n", cache->referenced, info->file);
430 }
431}
432
433/*
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
437 */
438
439typedef struct _FcFilePathInfo {
440 const FcChar8 *dir;
441 int dir_len;
442 const FcChar8 *base;
443 unsigned int base_hash;
444} FcFilePathInfo;
445
446static FcFilePathInfo
447FcFilePathInfoGet (const FcChar8 *path)
448{
449 FcFilePathInfo i;
450 FcChar8 *slash;
451
daeed6e0 452 slash = FcStrLastSlash (path);
327a7fd4
KP
453 if (slash)
454 {
455 i.dir = path;
456 i.dir_len = slash - path;
457 if (!i.dir_len)
458 i.dir_len = 1;
459 i.base = slash + 1;
460 }
461 else
462 {
463 i.dir = (const FcChar8 *) ".";
464 i.dir_len = 1;
465 i.base = path;
466 }
c8d5753c 467 i.base_hash = FcCacheHash (i.base, -1);
327a7fd4
KP
468 return i;
469}
470
1b7be377
PL
471FcGlobalCacheDir *
472FcGlobalCacheDirGet (FcGlobalCache *cache,
473 const FcChar8 *dir,
474 int len,
475 FcBool create_missing)
476{
477 unsigned int hash = FcCacheHash (dir, len);
478 FcGlobalCacheDir *d, **prev;
479
480 for (prev = &cache->ents[hash % FC_GLOBAL_CACHE_DIR_HASH_SIZE];
481 (d = *prev);
482 prev = &(*prev)->next)
483 {
484 if (d->info.hash == hash && d->len == len &&
485 !strncmp ((const char *) d->info.file,
486 (const char *) dir, len))
487 break;
488 }
489 if (!(d = *prev))
490 {
491 int i;
492 if (!create_missing)
493 return 0;
494 d = malloc (sizeof (FcGlobalCacheDir) + len + 1);
495 if (!d)
496 return 0;
497 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + len + 1);
498 d->next = *prev;
499 *prev = d;
500 d->info.hash = hash;
501 d->info.file = (FcChar8 *) (d + 1);
502 strncpy ((char *) d->info.file, (const char *) dir, len);
503 d->info.file[len] = '\0';
504 d->info.time = 0;
505 d->info.referenced = FcFalse;
506 d->len = len;
507 for (i = 0; i < FC_GLOBAL_CACHE_FILE_HASH_SIZE; i++)
508 d->ents[i] = 0;
509 d->subdirs = 0;
510 }
511 return d;
512}
513
514static FcGlobalCacheInfo *
515FcGlobalCacheDirAdd (FcGlobalCache *cache,
516 const FcChar8 *dir,
517 time_t time,
518 FcBool replace,
519 FcBool create_missing)
520{
521 FcGlobalCacheDir *d;
522 FcFilePathInfo i;
523 FcGlobalCacheSubdir *subdir;
524 FcGlobalCacheDir *parent;
525
526 i = FcFilePathInfoGet (dir);
527 parent = FcGlobalCacheDirGet (cache, i.dir, i.dir_len, create_missing);
528 /*
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.
536 */
537 if (!parent || (!create_missing &&
538 (!parent->info.referenced ||
539 (parent->info.time == 0))))
540 return 0;
541 /*
542 * Add this directory to the cache
543 */
544 d = FcGlobalCacheDirGet (cache, dir, strlen ((const char *) dir), FcTrue);
545 if (!d)
546 return 0;
547 d->info.time = time;
548 /*
549 * Add this directory to the subdirectory list of the parent
550 */
551 subdir = malloc (sizeof (FcGlobalCacheSubdir));
552 if (!subdir)
553 return 0;
554 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
555 subdir->ent = d;
556 subdir->next = parent->subdirs;
557 parent->subdirs = subdir;
558 return &d->info;
559}
560
561static void
562FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
563{
564 FcGlobalCacheFile *f, *next;
565 int h;
566 FcGlobalCacheSubdir *s, *nexts;
567
568 for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
569 for (f = d->ents[h]; f; f = next)
570 {
571 next = f->next;
572 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
573 strlen ((char *) f->info.file) + 1 +
574 strlen ((char *) f->name) + 1);
575 free (f);
576 }
577 for (s = d->subdirs; s; s = nexts)
578 {
579 nexts = s->next;
580 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
581 free (s);
582 }
583 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1);
584 free (d);
585}
586
587/*
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
591 */
592
593void
594FcGlobalCacheReferenceSubdir (FcGlobalCache *cache,
595 const FcChar8 *dir)
596{
597 FcGlobalCacheInfo *info;
598 info = FcGlobalCacheDirAdd (cache, dir, 0, FcFalse, FcFalse);
599 if (info && !info->referenced)
600 {
601 info->referenced = FcTrue;
602 cache->referenced++;
603 }
604}
605
606/*
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.
610 */
611FcBool
612FcGlobalCacheScanDir (FcFontSet *set,
613 FcStrSet *dirs,
614 FcGlobalCache *cache,
615 const FcChar8 *dir,
616 FcConfig *config)
617{
618 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, dir,
619 strlen ((const char *) dir),
620 FcFalse);
621 FcGlobalCacheFile *f;
622 int h;
623 int dir_len;
624 FcGlobalCacheSubdir *subdir;
625 FcBool any_in_cache = FcFalse;
626
627 if (FcDebug() & FC_DBG_CACHE)
628 printf ("FcGlobalCacheScanDir %s\n", dir);
629
630 if (!d)
631 {
632 if (FcDebug () & FC_DBG_CACHE)
633 printf ("\tNo dir cache entry\n");
634 return FcFalse;
635 }
636
637 /*
638 * See if the timestamp recorded in the global cache
639 * matches the directory time, if not, return False
640 */
641 if (!FcGlobalCacheCheckTime (d->info.file, &d->info))
642 {
643 if (FcDebug () & FC_DBG_CACHE)
644 printf ("\tdir cache entry time mismatch\n");
645 return FcFalse;
646 }
647
648 /*
649 * Add files from 'dir' to the fontset
650 */
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)
654 {
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))
660 {
661 cache->broken = FcTrue;
662 return FcFalse;
663 }
664 FcGlobalCacheReferenced (cache, &f->info);
665 }
666 /*
667 * Add directories in 'dir' to 'dirs'
668 */
669 for (subdir = d->subdirs; subdir; subdir = subdir->next)
670 {
671 FcFilePathInfo info = FcFilePathInfoGet (subdir->ent->info.file);
672
673 any_in_cache = FcTrue;
674 if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
675 info.base, FC_FONT_FILE_DIR, config))
676 {
677 cache->broken = FcTrue;
678 return FcFalse;
679 }
680 FcGlobalCacheReferenced (cache, &subdir->ent->info);
681 }
682
683 FcGlobalCacheReferenced (cache, &d->info);
684
685 /*
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
691 * presumably fixed.
692 */
693 return any_in_cache;
694}
695
696/*
697 * Locate the cache entry for a particular file
698 */
699FcGlobalCacheFile *
700FcGlobalCacheFileGet (FcGlobalCache *cache,
701 const FcChar8 *file,
702 int id,
703 int *count)
704{
705 FcFilePathInfo i = FcFilePathInfoGet (file);
706 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
707 i.dir_len, FcFalse);
708 FcGlobalCacheFile *f, *match = 0;
709 int max = -1;
710
711 if (!d)
712 return 0;
713 for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next)
714 {
715 if (f->info.hash == i.base_hash &&
716 !strcmp ((const char *) f->info.file, (const char *) i.base))
717 {
718 if (f->id == id)
719 match = f;
720 if (f->id > max)
721 max = f->id;
722 }
723 }
724 if (count)
725 *count = max + 1;
726 return match;
727}
728
729/*
730 * Add a file entry to the cache
731 */
732static FcGlobalCacheInfo *
733FcGlobalCacheFileAdd (FcGlobalCache *cache,
734 const FcChar8 *path,
735 int id,
736 time_t time,
737 const FcChar8 *name,
738 FcBool replace)
739{
740 FcFilePathInfo i = FcFilePathInfoGet (path);
741 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
742 i.dir_len, FcTrue);
743 FcGlobalCacheFile *f, **prev;
744 int size;
745
746 if (!d)
747 return 0;
748 for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE];
749 (f = *prev);
750 prev = &(*prev)->next)
751 {
752 if (f->info.hash == i.base_hash &&
753 f->id == id &&
754 !strcmp ((const char *) f->info.file, (const char *) i.base))
755 {
756 break;
757 }
758 }
759 if (*prev)
760 {
761 if (!replace)
762 return 0;
763
764 f = *prev;
765 if (f->info.referenced)
766 cache->referenced--;
767 *prev = f->next;
768 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
769 strlen ((char *) f->info.file) + 1 +
770 strlen ((char *) f->name) + 1);
771 free (f);
772 }
773 size = (sizeof (FcGlobalCacheFile) +
774 strlen ((char *) i.base) + 1 +
775 strlen ((char *) name) + 1);
776 f = malloc (size);
777 if (!f)
778 return 0;
779 FcMemAlloc (FC_MEM_CACHE, size);
780 f->next = *prev;
781 *prev = f;
782 f->info.hash = i.base_hash;
783 f->info.file = (FcChar8 *) (f + 1);
784 f->info.time = time;
785 f->info.referenced = FcFalse;
786 f->id = id;
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);
790 return &f->info;
791}
792
327a7fd4
KP
793FcGlobalCache *
794FcGlobalCacheCreate (void)
24330d27 795{
327a7fd4
KP
796 FcGlobalCache *cache;
797 int h;
24330d27 798
327a7fd4 799 cache = malloc (sizeof (FcGlobalCache));
24330d27
KP
800 if (!cache)
801 return 0;
9dac3c59 802 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
327a7fd4 803 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27
KP
804 cache->ents[h] = 0;
805 cache->entries = 0;
806 cache->referenced = 0;
807 cache->updated = FcFalse;
bb356b68 808 cache->broken = FcFalse;
24330d27
KP
809 return cache;
810}
811
812void
327a7fd4 813FcGlobalCacheDestroy (FcGlobalCache *cache)
24330d27 814{
327a7fd4
KP
815 FcGlobalCacheDir *d, *next;
816 int h;
24330d27 817
327a7fd4 818 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27 819 {
327a7fd4 820 for (d = cache->ents[h]; d; d = next)
24330d27 821 {
327a7fd4
KP
822 next = d->next;
823 FcGlobalCacheDirDestroy (d);
24330d27
KP
824 }
825 }
9dac3c59 826 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
24330d27
KP
827 free (cache);
828}
829
1b7be377
PL
830/*
831 * Cache file syntax is quite simple:
832 *
833 * "file_name" id time "font_name" \n
834 */
835
24330d27 836void
327a7fd4
KP
837FcGlobalCacheLoad (FcGlobalCache *cache,
838 const FcChar8 *cache_file)
24330d27 839{
327a7fd4
KP
840 FILE *f;
841 FcChar8 file_buf[8192], *file;
842 int id;
843 time_t time;
844 FcChar8 name_buf[8192], *name;
845 FcGlobalCacheInfo *info;
24330d27 846
ccb3e93b 847 f = fopen ((char *) cache_file, "r");
24330d27
KP
848 if (!f)
849 return;
850
851 cache->updated = FcFalse;
ccb3e93b
KP
852 file = 0;
853 name = 0;
327a7fd4
KP
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))))
24330d27 858 {
327a7fd4
KP
859 if (FcDebug () & FC_DBG_CACHEV)
860 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
861 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
c4ab52dc 862 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue);
327a7fd4
KP
863 else
864 info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
865 if (!info)
866 cache->broken = FcTrue;
867 else
868 cache->entries++;
869 if (FcDebug () & FC_DBG_CACHE_REF)
870 printf ("FcGlobalCacheLoad entry %d %s\n",
871 cache->entries, file);
ccb3e93b
KP
872 if (file != file_buf)
873 free (file);
874 if (name != name_buf)
875 free (name);
876 file = 0;
877 name = 0;
24330d27 878 }
ccb3e93b
KP
879 if (file && file != file_buf)
880 free (file);
881 if (name && name != name_buf)
882 free (name);
24330d27
KP
883 fclose (f);
884}
885
886FcBool
327a7fd4
KP
887FcGlobalCacheUpdate (FcGlobalCache *cache,
888 const FcChar8 *file,
889 int id,
890 const FcChar8 *name)
24330d27 891{
327a7fd4
KP
892 const FcChar8 *match;
893 struct stat statb;
894 FcGlobalCacheInfo *info;
24330d27
KP
895
896 match = file;
897
ccb3e93b 898 if (stat ((char *) file, &statb) < 0)
24330d27 899 return FcFalse;
327a7fd4
KP
900 if (S_ISDIR (statb.st_mode))
901 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
c4ab52dc 902 FcTrue, FcTrue);
327a7fd4
KP
903 else
904 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
905 name, FcTrue);
906 if (info)
24330d27 907 {
327a7fd4
KP
908 FcGlobalCacheReferenced (cache, info);
909 cache->updated = FcTrue;
24330d27 910 }
327a7fd4
KP
911 else
912 cache->broken = FcTrue;
913 return info != 0;
24330d27
KP
914}
915
916FcBool
327a7fd4
KP
917FcGlobalCacheSave (FcGlobalCache *cache,
918 const FcChar8 *cache_file)
24330d27 919{
327a7fd4
KP
920 FILE *f;
921 int dir_hash, file_hash;
922 FcGlobalCacheDir *dir;
923 FcGlobalCacheFile *file;
924 FcAtomic *atomic;
24330d27
KP
925
926 if (!cache->updated && cache->referenced == cache->entries)
927 return FcTrue;
928
327a7fd4
KP
929 if (cache->broken)
930 return FcFalse;
931
daeed6e0 932#if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
a391da8f
KP
933 /* Set-UID programs can't safely update the cache */
934 if (getuid () != geteuid ())
935 return FcFalse;
daeed6e0 936#endif
a391da8f 937
134f6011
KP
938 atomic = FcAtomicCreate (cache_file);
939 if (!atomic)
24330d27 940 goto bail0;
134f6011 941 if (!FcAtomicLock (atomic))
24330d27 942 goto bail1;
134f6011 943 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
24330d27
KP
944 if (!f)
945 goto bail2;
946
327a7fd4 947 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
24330d27 948 {
327a7fd4 949 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
24330d27 950 {
327a7fd4 951 if (!dir->info.referenced)
24330d27 952 continue;
327a7fd4 953 if (!FcCacheWriteString (f, dir->info.file))
24330d27 954 goto bail4;
fa244f3d 955 if (PUTC (' ', f) == EOF)
24330d27 956 goto bail4;
327a7fd4 957 if (!FcCacheWriteInt (f, 0))
24330d27 958 goto bail4;
fa244f3d 959 if (PUTC (' ', f) == EOF)
24330d27 960 goto bail4;
327a7fd4 961 if (!FcCacheWriteTime (f, dir->info.time))
24330d27 962 goto bail4;
fa244f3d 963 if (PUTC (' ', f) == EOF)
24330d27 964 goto bail4;
327a7fd4 965 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
24330d27 966 goto bail4;
fa244f3d 967 if (PUTC ('\n', f) == EOF)
24330d27 968 goto bail4;
327a7fd4
KP
969
970 for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
971 {
972 for (file = dir->ents[file_hash]; file; file = file->next)
973 {
974 if (!file->info.referenced)
975 continue;
976 if (!FcCacheWritePath (f, dir->info.file, file->info.file))
977 goto bail4;
fa244f3d 978 if (PUTC (' ', f) == EOF)
327a7fd4
KP
979 goto bail4;
980 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
981 goto bail4;
fa244f3d 982 if (PUTC (' ', f) == EOF)
327a7fd4
KP
983 goto bail4;
984 if (!FcCacheWriteTime (f, file->info.time))
985 goto bail4;
fa244f3d 986 if (PUTC (' ', f) == EOF)
327a7fd4
KP
987 goto bail4;
988 if (!FcCacheWriteString (f, file->name))
989 goto bail4;
fa244f3d 990 if (PUTC ('\n', f) == EOF)
327a7fd4
KP
991 goto bail4;
992 }
993 }
24330d27
KP
994 }
995 }
996
997 if (fclose (f) == EOF)
998 goto bail3;
999
134f6011 1000 if (!FcAtomicReplaceOrig (atomic))
24330d27
KP
1001 goto bail3;
1002
134f6011
KP
1003 FcAtomicUnlock (atomic);
1004 FcAtomicDestroy (atomic);
1005
24330d27
KP
1006 cache->updated = FcFalse;
1007 return FcTrue;
1008
1009bail4:
1010 fclose (f);
1011bail3:
134f6011 1012 FcAtomicDeleteNew (atomic);
24330d27 1013bail2:
134f6011 1014 FcAtomicUnlock (atomic);
24330d27 1015bail1:
134f6011 1016 FcAtomicDestroy (atomic);
24330d27
KP
1017bail0:
1018 return FcFalse;
1019}
1020
212c9f43
PL
1021/*
1022 * Find the next presumably-mmapable offset after the current file
1023 * pointer.
1024 */
4262e0b3
PL
1025static int
1026FcCacheNextOffset(off_t w)
179c3995 1027{
212c9f43
PL
1028 if (w % PAGESIZE == 0)
1029 return w;
1030 else
1031 return ((w / PAGESIZE)+1)*PAGESIZE;
179c3995
KP
1032}
1033
212c9f43
PL
1034/* return the address of the segment for the provided arch,
1035 * or -1 if arch not found */
1036static off_t
1037FcCacheSkipToArch (int fd, const char * arch)
1038{
2dbe7597
PL
1039 char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9];
1040 char * candidate_arch;
212c9f43
PL
1041 off_t current_arch_start = 0;
1042
1043 /* skip arches that are not the current arch */
1044 while (1)
1045 {
1046 long bs;
1047
1048 lseek (fd, current_arch_start, SEEK_SET);
2dbe7597
PL
1049 if (FcCacheReadString2 (fd, candidate_arch_machine_name_count,
1050 sizeof (candidate_arch_machine_name_count)) == 0)
212c9f43 1051 break;
2dbe7597
PL
1052 if (!strlen(candidate_arch_machine_name_count))
1053 return -1;
1054 bs = strtol(candidate_arch_machine_name_count, &candidate_arch, 16);
1055 candidate_arch++; /* skip leading space */
212c9f43 1056
2dbe7597 1057 if (strcmp (candidate_arch, arch)==0)
212c9f43
PL
1058 break;
1059 current_arch_start += bs;
1060 }
1061
2dbe7597 1062 if (strcmp (candidate_arch, arch)!=0)
212c9f43
PL
1063 return -1;
1064
1065 return current_arch_start;
1066}
1067
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
1070 * file. */
1071#define BUF_SIZE 8192
1072
1073static FcBool
1074FcCacheMoveDown (int fd, off_t start)
1075{
1076 char * buf = malloc (BUF_SIZE);
2dbe7597 1077 char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
4262e0b3 1078 long bs;
212c9f43
PL
1079 int c, bytes_skipped;
1080
1081 if (!buf)
1082 return FcFalse;
1083
1084 lseek (fd, start, SEEK_SET);
1b7be377 1085 if (FcCacheReadString2 (fd, candidate_arch_machine_name,
212c9f43
PL
1086 sizeof (candidate_arch_machine_name)) == 0)
1087 goto done;
2dbe7597 1088 if (!strlen(candidate_arch_machine_name))
212c9f43
PL
1089 goto done;
1090
2dbe7597 1091 bs = strtol(candidate_arch_machine_name, 0, 16);
212c9f43
PL
1092 if (bs == 0)
1093 goto done;
1094
1095 bytes_skipped = 0;
1096 do
1097 {
1098 lseek (fd, start+bs+bytes_skipped, SEEK_SET);
1099 if ((c = read (fd, buf, BUF_SIZE)) <= 0)
1100 break;
1101 lseek (fd, start+bytes_skipped, SEEK_SET);
1102 if (write (fd, buf, c) < 0)
1103 goto bail;
1104 bytes_skipped += c;
1105 }
1106 while (c > 0);
1107 lseek (fd, start+bytes_skipped, SEEK_SET);
1108
1109 done:
1110 free (buf);
1111 return FcTrue;
1112
1113 bail:
1114 free (buf);
1115 return FcFalse;
1116}
1117
1b7be377
PL
1118FcBool
1119FcDirCacheValid (const FcChar8 *dir)
1120{
1121 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1122 struct stat file_stat, dir_stat;
1123
1124 if (stat ((char *) dir, &dir_stat) < 0)
1125 {
1126 FcStrFree (cache_file);
1127 return FcFalse;
1128 }
1129 if (stat ((char *) cache_file, &file_stat) < 0)
1130 {
1131 FcStrFree (cache_file);
1132 return FcFalse;
1133 }
1134 FcStrFree (cache_file);
1135 /*
1136 * If the directory has been modified more recently than
1137 * the cache file, the cache is not valid
1138 */
1139 if (dir_stat.st_mtime - file_stat.st_mtime > 0)
1140 return FcFalse;
1141 return FcTrue;
1142}
1143
4262e0b3 1144static int
1b7be377
PL
1145FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache,
1146 FcStrList *list, FcFontSet * set)
4262e0b3
PL
1147{
1148 DIR *d;
1149 struct dirent *e;
1150 int ret = 0;
1151 FcChar8 *dir;
1152 FcChar8 *file, *base;
1153 FcStrSet *subdirs;
1154 FcStrList *sublist;
1155 struct stat statb;
1156
1157 /*
1158 * Now scan all of the directories into separate databases
1159 * and write out the results
1160 */
1161 while ((dir = FcStrListNext (list)))
1162 {
1163 /* freed below */
1164 file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
1165 if (!file)
1166 return FcFalse;
1167
1168 strcpy ((char *) file, (char *) dir);
1169 strcat ((char *) file, "/");
1170 base = file + strlen ((char *) file);
1171
1172 subdirs = FcStrSetCreate ();
1173 if (!subdirs)
1174 {
1175 fprintf (stderr, "Can't create directory set\n");
1176 ret++;
1177 free (file);
1178 continue;
1179 }
1180
1181 if (access ((char *) dir, X_OK) < 0)
1182 {
1183 switch (errno) {
1184 case ENOENT:
1185 case ENOTDIR:
1186 case EACCES:
1187 break;
1188 default:
1189 fprintf (stderr, "\"%s\": ", dir);
1190 perror ("");
1191 ret++;
1192 }
1193 FcStrSetDestroy (subdirs);
1194 free (file);
1195 continue;
1196 }
1197 if (stat ((char *) dir, &statb) == -1)
1198 {
1199 fprintf (stderr, "\"%s\": ", dir);
1200 perror ("");
1201 FcStrSetDestroy (subdirs);
1202 ret++;
1203 free (file);
1204 continue;
1205 }
1206 if (!S_ISDIR (statb.st_mode))
1207 {
1208 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
1209 FcStrSetDestroy (subdirs);
1210 free (file);
1211 continue;
1212 }
1213 d = opendir ((char *) dir);
1214 if (!d)
1215 {
1216 FcStrSetDestroy (subdirs);
1217 free (file);
1218 continue;
1219 }
1220 while ((e = readdir (d)))
1221 {
1222 if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN)
1223 {
1224 strcpy ((char *) base, (char *) e->d_name);
1225 if (FcFileIsDir (file) && !FcStrSetAdd (subdirs, file))
1226 ret++;
1227 }
1228 }
1229 closedir (d);
2dbe7597 1230 if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, dir))
4262e0b3 1231 {
1b7be377
PL
1232 if (FcDebug () & FC_DBG_FONTSET)
1233 printf ("scan dir %s\n", dir);
1234 FcDirScanConfig (set, subdirs, cache,
1235 config->blanks, dir, FcFalse, config);
4262e0b3
PL
1236 }
1237 sublist = FcStrListCreate (subdirs);
1238 FcStrSetDestroy (subdirs);
1239 if (!sublist)
1240 {
1241 fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
1242 ret++;
1243 free (file);
1244 continue;
1245 }
1b7be377 1246 ret += FcCacheReadDirs (config, cache, sublist, set);
4262e0b3
PL
1247 free (file);
1248 }
1249 FcStrListDone (list);
1250 return ret;
1251}
1252
1253FcFontSet *
1b7be377 1254FcCacheRead (FcConfig *config, FcGlobalCache * cache)
4262e0b3
PL
1255{
1256 FcFontSet * s = FcFontSetCreate();
1257 if (!s)
1258 return 0;
1259
1260 if (force)
1261 goto bail;
1262
1b7be377 1263 if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s))
4262e0b3
PL
1264 goto bail;
1265
1266 return s;
1267
1268 bail:
1269 FcFontSetDestroy (s);
1270 return 0;
1271}
1272
212c9f43
PL
1273/* read serialized state from the cache file */
1274FcBool
4262e0b3 1275FcDirCacheRead (FcFontSet * set, const FcChar8 *dir)
212c9f43 1276{
4262e0b3
PL
1277 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1278 int fd;
212c9f43 1279 FcCache metadata;
4262e0b3 1280 void * current_dir_block;
212c9f43 1281 char * current_arch_machine_name;
2dbe7597 1282 char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
212c9f43
PL
1283 off_t current_arch_start = 0;
1284
1285 if (force)
4262e0b3
PL
1286 goto bail;
1287 if (!cache_file)
1288 goto bail;
212c9f43 1289
2dbe7597 1290 current_arch_machine_name = FcCacheProduceMachineSignature();
4262e0b3 1291 fd = open(cache_file, O_RDONLY);
212c9f43 1292 if (fd == -1)
2dbe7597 1293 goto bail;
212c9f43 1294
212c9f43
PL
1295 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
1296 if (current_arch_start < 0)
4262e0b3 1297 goto bail1;
212c9f43
PL
1298
1299 lseek (fd, current_arch_start, SEEK_SET);
1b7be377 1300 if (FcCacheReadString2 (fd, candidate_arch_machine_name,
212c9f43 1301 sizeof (candidate_arch_machine_name)) == 0)
4262e0b3 1302 goto bail1;
212c9f43
PL
1303
1304 // sanity check for endianness issues
1305 read(fd, &metadata, sizeof(FcCache));
1306 if (metadata.magic != FC_CACHE_MAGIC)
4262e0b3 1307 goto bail1;
212c9f43 1308
2dbe7597
PL
1309 if (!metadata.count)
1310 goto bail1;
1311
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)
1316 perror("");
1317
1318 if (!FcFontSetUnserialize (metadata, set, current_dir_block))
1319 goto bail1;
4262e0b3 1320
212c9f43 1321 close(fd);
4262e0b3 1322 free (cache_file);
212c9f43
PL
1323 return FcTrue;
1324
1325 bail1:
212c9f43 1326 close(fd);
4262e0b3
PL
1327 bail:
1328 free (cache_file);
212c9f43
PL
1329 return FcFalse;
1330}
1331
1332/* write serialized state to the cache file */
1333FcBool
4262e0b3 1334FcDirCacheWrite (int bank, FcFontSet *set, const FcChar8 *dir)
212c9f43 1335{
4262e0b3
PL
1336 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1337 int fd, bytes_to_write, metadata_bytes;
212c9f43
PL
1338 FcCache metadata;
1339 off_t current_arch_start = 0, truncate_to;
2dbe7597 1340 char * current_arch_machine_name, * header;
4262e0b3 1341 void * current_dir_block, *final_dir_block;
212c9f43 1342
4262e0b3
PL
1343 if (!cache_file)
1344 goto bail;
1345
1346 FcFontSetNewBank();
1347 bytes_to_write = FcFontSetNeededBytes (set);
1348 metadata_bytes = FcCacheNextOffset (sizeof (FcCache));
212c9f43 1349
4262e0b3
PL
1350 if (!bytes_to_write)
1351 {
1352 unlink (cache_file);
1353 free (cache_file);
1354 return FcTrue;
1355 }
1356
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)
1362 goto bail;
1363 final_dir_block = FcFontSetDistributeBytes (&metadata, current_dir_block);
2dbe7597
PL
1364
1365 if ((char *)current_dir_block + bytes_to_write != final_dir_block)
1366 goto bail;
4262e0b3
PL
1367
1368 if (!FcFontSetSerialize (bank, set))
2dbe7597 1369 goto bail;
212c9f43 1370
4262e0b3
PL
1371 if (FcDebug () & FC_DBG_CACHE)
1372 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
1373
1374 fd = open(cache_file, O_RDWR | O_CREAT, 0666);
212c9f43 1375 if (fd == -1)
2dbe7597 1376 goto bail;
212c9f43 1377
2dbe7597 1378 current_arch_machine_name = FcCacheProduceMachineSignature ();
212c9f43
PL
1379 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
1380 if (current_arch_start < 0)
4262e0b3 1381 current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END));
212c9f43
PL
1382
1383 if (!FcCacheMoveDown(fd, current_arch_start))
4262e0b3 1384 goto bail1;
212c9f43
PL
1385
1386 current_arch_start = lseek(fd, 0, SEEK_CUR);
1387 if (ftruncate (fd, current_arch_start) == -1)
4262e0b3 1388 goto bail1;
212c9f43 1389
212c9f43 1390 /* now write the address of the next offset */
2dbe7597
PL
1391 truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + metadata_bytes) + bytes_to_write) - current_arch_start;
1392
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))
4262e0b3 1397 goto bail1;
212c9f43 1398
4262e0b3
PL
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);
1403
2dbe7597 1404 /* this actually serves to pad out the cache file, if needed */
212c9f43 1405 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
4262e0b3 1406 goto bail1;
212c9f43
PL
1407
1408 close(fd);
1409 return FcTrue;
1410
4262e0b3
PL
1411 bail1:
1412 free (current_dir_block);
212c9f43 1413 free (current_arch_machine_name);
4262e0b3
PL
1414 bail:
1415 unlink (cache_file);
1416 free (cache_file);
212c9f43
PL
1417 return FcFalse;
1418}
1419
2dbe7597
PL
1420static char *
1421FcCacheProduceMachineSignature ()
1422{
1423 static char buf[MACHINE_SIGNATURE_SIZE];
1424 int magic = ENDIAN_TEST;
1425 char * m = (char *)&magic;
1426
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],
1431 sizeof (char),
1432 sizeof (char *),
1433 sizeof (int),
1434 sizeof (FcPattern),
1435 sizeof (FcPatternEltPtr),
1436 sizeof (struct _FcPatternElt *),
1437 sizeof (FcPatternElt),
1438 sizeof (FcObjectPtr),
1439 sizeof (FcValueListPtr),
1440 sizeof (FcValue),
1441 sizeof (FcValueBinding),
1442 sizeof (struct _FcValueList *),
1443 sizeof (FcCharSet),
1444 sizeof (FcCharLeaf **),
1445 sizeof (FcChar16 *),
1446 sizeof (FcChar16),
1447 sizeof (FcCharLeaf),
1448 sizeof (FcChar32),
1449 sizeof (FcCache));
1450
1451 return buf;
1452}
1453
212c9f43
PL
1454/* if true, ignore the cache file */
1455void
1456FcCacheForce (FcBool f)
1457{
1458 force = f;
1459}
4262e0b3
PL
1460
1461static int banks_ptr = 0, banks_alloc = 0;
1462static int * bankId = 0;
1463
1464int
1465FcCacheBankCount (void)
1466{
1467 return banks_ptr;
1468}
1469
1470FcBool
1471FcCacheHaveBank (int bank)
1472{
1473 int i;
1474
1475 if (bank < FC_BANK_FIRST)
1476 return FcTrue;
1477
1478 for (i = 0; i < banks_ptr; i++)
1479 if (bankId[i] == bank)
1480 return FcTrue;
1481
1482 return FcFalse;
1483}
1484
1485int
1486FcCacheBankToIndex (int bank)
1487{
1488 static int lastBank = FC_BANK_DYNAMIC, lastIndex = -1;
1489 int i;
1490 int * b;
1491
1492 if (bank == lastBank)
1493 return lastIndex;
1494
1495 for (i = 0; i < banks_ptr; i++)
1496 if (bankId[i] == bank)
1497 return i;
1498
2dbe7597 1499 if (banks_ptr >= banks_alloc)
4262e0b3 1500 {
2dbe7597 1501 b = realloc (bankId, (banks_alloc + 4) * sizeof(int));
4262e0b3
PL
1502 if (!b)
1503 return -1;
1504
1505 bankId = b;
1506 banks_alloc += 4;
1507 }
1508
1509 i = banks_ptr++;
1510 bankId[i] = bank;
1511 return i;
1512}