]> git.wh0rd.org - fontconfig.git/blame - src/fccache.c
Overhaul the serialization system to create one mmapable file per directory
[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
212c9f43 31#define PAGESIZE 8192
fa244f3d 32
212c9f43 33static FcBool force;
24330d27 34
ccb3e93b 35static FcChar8 *
212c9f43 36FcCacheReadString (int fd, FcChar8 *dest, int len)
24330d27 37{
212c9f43 38 FcChar8 c;
ccb3e93b 39 FcBool escape;
ccb3e93b
KP
40 int size;
41 int i;
24330d27 42
24330d27
KP
43 if (len == 0)
44 return FcFalse;
45
ccb3e93b
KP
46 size = len;
47 i = 0;
24330d27 48 escape = FcFalse;
212c9f43 49 while (read (fd, &c, 1) == 1)
24330d27
KP
50 {
51 if (!escape)
52 {
53 switch (c) {
54 case '"':
ccb3e93b
KP
55 c = '\0';
56 break;
24330d27
KP
57 case '\\':
58 escape = FcTrue;
59 continue;
60 }
61 }
ccb3e93b
KP
62 if (i == size)
63 {
212c9f43
PL
64 dest[i++] = 0;
65 return dest;
ccb3e93b 66 }
212c9f43 67 dest[i++] = c;
ccb3e93b 68 if (c == '\0')
212c9f43 69 return dest;
24330d27
KP
70 escape = FcFalse;
71 }
ccb3e93b 72 return 0;
24330d27
KP
73}
74
75static FcBool
212c9f43 76FcCacheWriteString (int fd, const FcChar8 *chars)
327a7fd4 77{
212c9f43 78 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
327a7fd4 79 return FcFalse;
327a7fd4
KP
80 return FcTrue;
81}
82
212c9f43 83#if 0
327a7fd4
KP
84/*
85 * Verify the saved timestamp for a file
86 */
87FcBool
a8386abc 88FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info)
327a7fd4
KP
89{
90 struct stat statb;
91
a8386abc 92 if (stat ((char *) file, &statb) < 0)
327a7fd4
KP
93 {
94 if (FcDebug () & FC_DBG_CACHE)
a8386abc 95 printf (" file %s missing\n", file);
327a7fd4
KP
96 return FcFalse;
97 }
98 if (statb.st_mtime != info->time)
99 {
100 if (FcDebug () & FC_DBG_CACHE)
101 printf (" timestamp mismatch (was %d is %d)\n",
102 (int) info->time, (int) statb.st_mtime);
103 return FcFalse;
104 }
105 return FcTrue;
106}
107
108void
109FcGlobalCacheReferenced (FcGlobalCache *cache,
110 FcGlobalCacheInfo *info)
111{
112 if (!info->referenced)
113 {
114 info->referenced = FcTrue;
115 cache->referenced++;
116 if (FcDebug () & FC_DBG_CACHE_REF)
117 printf ("Reference %d %s\n", cache->referenced, info->file);
118 }
119}
120
121/*
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
125 */
126
127typedef struct _FcFilePathInfo {
128 const FcChar8 *dir;
129 int dir_len;
130 const FcChar8 *base;
131 unsigned int base_hash;
132} FcFilePathInfo;
133
134static FcFilePathInfo
135FcFilePathInfoGet (const FcChar8 *path)
136{
137 FcFilePathInfo i;
138 FcChar8 *slash;
139
daeed6e0 140 slash = FcStrLastSlash (path);
327a7fd4
KP
141 if (slash)
142 {
143 i.dir = path;
144 i.dir_len = slash - path;
145 if (!i.dir_len)
146 i.dir_len = 1;
147 i.base = slash + 1;
148 }
149 else
150 {
151 i.dir = (const FcChar8 *) ".";
152 i.dir_len = 1;
153 i.base = path;
154 }
c8d5753c 155 i.base_hash = FcCacheHash (i.base, -1);
327a7fd4
KP
156 return i;
157}
158
327a7fd4
KP
159FcGlobalCache *
160FcGlobalCacheCreate (void)
24330d27 161{
327a7fd4
KP
162 FcGlobalCache *cache;
163 int h;
24330d27 164
327a7fd4 165 cache = malloc (sizeof (FcGlobalCache));
24330d27
KP
166 if (!cache)
167 return 0;
9dac3c59 168 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
327a7fd4 169 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27
KP
170 cache->ents[h] = 0;
171 cache->entries = 0;
172 cache->referenced = 0;
173 cache->updated = FcFalse;
bb356b68 174 cache->broken = FcFalse;
24330d27
KP
175 return cache;
176}
177
178void
327a7fd4 179FcGlobalCacheDestroy (FcGlobalCache *cache)
24330d27 180{
327a7fd4
KP
181 FcGlobalCacheDir *d, *next;
182 int h;
24330d27 183
327a7fd4 184 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
24330d27 185 {
327a7fd4 186 for (d = cache->ents[h]; d; d = next)
24330d27 187 {
327a7fd4
KP
188 next = d->next;
189 FcGlobalCacheDirDestroy (d);
24330d27
KP
190 }
191 }
9dac3c59 192 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
24330d27
KP
193 free (cache);
194}
195
196void
327a7fd4
KP
197FcGlobalCacheLoad (FcGlobalCache *cache,
198 const FcChar8 *cache_file)
24330d27 199{
327a7fd4
KP
200 FILE *f;
201 FcChar8 file_buf[8192], *file;
202 int id;
203 time_t time;
204 FcChar8 name_buf[8192], *name;
205 FcGlobalCacheInfo *info;
24330d27 206
ccb3e93b 207 f = fopen ((char *) cache_file, "r");
24330d27
KP
208 if (!f)
209 return;
210
211 cache->updated = FcFalse;
ccb3e93b
KP
212 file = 0;
213 name = 0;
327a7fd4
KP
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))))
24330d27 218 {
327a7fd4
KP
219 if (FcDebug () & FC_DBG_CACHEV)
220 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
221 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
c4ab52dc 222 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue);
327a7fd4
KP
223 else
224 info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
225 if (!info)
226 cache->broken = FcTrue;
227 else
228 cache->entries++;
229 if (FcDebug () & FC_DBG_CACHE_REF)
230 printf ("FcGlobalCacheLoad entry %d %s\n",
231 cache->entries, file);
ccb3e93b
KP
232 if (file != file_buf)
233 free (file);
234 if (name != name_buf)
235 free (name);
236 file = 0;
237 name = 0;
24330d27 238 }
ccb3e93b
KP
239 if (file && file != file_buf)
240 free (file);
241 if (name && name != name_buf)
242 free (name);
24330d27
KP
243 fclose (f);
244}
245
246FcBool
327a7fd4
KP
247FcGlobalCacheUpdate (FcGlobalCache *cache,
248 const FcChar8 *file,
249 int id,
250 const FcChar8 *name)
24330d27 251{
327a7fd4
KP
252 const FcChar8 *match;
253 struct stat statb;
254 FcGlobalCacheInfo *info;
24330d27
KP
255
256 match = file;
257
ccb3e93b 258 if (stat ((char *) file, &statb) < 0)
24330d27 259 return FcFalse;
327a7fd4
KP
260 if (S_ISDIR (statb.st_mode))
261 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
c4ab52dc 262 FcTrue, FcTrue);
327a7fd4
KP
263 else
264 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
265 name, FcTrue);
266 if (info)
24330d27 267 {
327a7fd4
KP
268 FcGlobalCacheReferenced (cache, info);
269 cache->updated = FcTrue;
24330d27 270 }
327a7fd4
KP
271 else
272 cache->broken = FcTrue;
273 return info != 0;
24330d27
KP
274}
275
276FcBool
327a7fd4
KP
277FcGlobalCacheSave (FcGlobalCache *cache,
278 const FcChar8 *cache_file)
24330d27 279{
327a7fd4
KP
280 FILE *f;
281 int dir_hash, file_hash;
282 FcGlobalCacheDir *dir;
283 FcGlobalCacheFile *file;
284 FcAtomic *atomic;
24330d27
KP
285
286 if (!cache->updated && cache->referenced == cache->entries)
287 return FcTrue;
288
327a7fd4
KP
289 if (cache->broken)
290 return FcFalse;
291
daeed6e0 292#if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
a391da8f
KP
293 /* Set-UID programs can't safely update the cache */
294 if (getuid () != geteuid ())
295 return FcFalse;
daeed6e0 296#endif
a391da8f 297
134f6011
KP
298 atomic = FcAtomicCreate (cache_file);
299 if (!atomic)
24330d27 300 goto bail0;
134f6011 301 if (!FcAtomicLock (atomic))
24330d27 302 goto bail1;
134f6011 303 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
24330d27
KP
304 if (!f)
305 goto bail2;
306
327a7fd4 307 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
24330d27 308 {
327a7fd4 309 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
24330d27 310 {
327a7fd4 311 if (!dir->info.referenced)
24330d27 312 continue;
327a7fd4 313 if (!FcCacheWriteString (f, dir->info.file))
24330d27 314 goto bail4;
fa244f3d 315 if (PUTC (' ', f) == EOF)
24330d27 316 goto bail4;
327a7fd4 317 if (!FcCacheWriteInt (f, 0))
24330d27 318 goto bail4;
fa244f3d 319 if (PUTC (' ', f) == EOF)
24330d27 320 goto bail4;
327a7fd4 321 if (!FcCacheWriteTime (f, dir->info.time))
24330d27 322 goto bail4;
fa244f3d 323 if (PUTC (' ', f) == EOF)
24330d27 324 goto bail4;
327a7fd4 325 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
24330d27 326 goto bail4;
fa244f3d 327 if (PUTC ('\n', f) == EOF)
24330d27 328 goto bail4;
327a7fd4
KP
329
330 for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
331 {
332 for (file = dir->ents[file_hash]; file; file = file->next)
333 {
334 if (!file->info.referenced)
335 continue;
336 if (!FcCacheWritePath (f, dir->info.file, file->info.file))
337 goto bail4;
fa244f3d 338 if (PUTC (' ', f) == EOF)
327a7fd4
KP
339 goto bail4;
340 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
341 goto bail4;
fa244f3d 342 if (PUTC (' ', f) == EOF)
327a7fd4
KP
343 goto bail4;
344 if (!FcCacheWriteTime (f, file->info.time))
345 goto bail4;
fa244f3d 346 if (PUTC (' ', f) == EOF)
327a7fd4
KP
347 goto bail4;
348 if (!FcCacheWriteString (f, file->name))
349 goto bail4;
fa244f3d 350 if (PUTC ('\n', f) == EOF)
327a7fd4
KP
351 goto bail4;
352 }
353 }
24330d27
KP
354 }
355 }
356
357 if (fclose (f) == EOF)
358 goto bail3;
359
134f6011 360 if (!FcAtomicReplaceOrig (atomic))
24330d27
KP
361 goto bail3;
362
134f6011
KP
363 FcAtomicUnlock (atomic);
364 FcAtomicDestroy (atomic);
365
24330d27
KP
366 cache->updated = FcFalse;
367 return FcTrue;
368
369bail4:
370 fclose (f);
371bail3:
134f6011 372 FcAtomicDeleteNew (atomic);
24330d27 373bail2:
134f6011 374 FcAtomicUnlock (atomic);
24330d27 375bail1:
134f6011 376 FcAtomicDestroy (atomic);
24330d27
KP
377bail0:
378 return FcFalse;
379}
212c9f43 380#endif
24330d27 381
212c9f43
PL
382/*
383 * Find the next presumably-mmapable offset after the current file
384 * pointer.
385 */
4262e0b3
PL
386static int
387FcCacheNextOffset(off_t w)
179c3995 388{
212c9f43
PL
389 if (w % PAGESIZE == 0)
390 return w;
391 else
392 return ((w / PAGESIZE)+1)*PAGESIZE;
179c3995
KP
393}
394
212c9f43
PL
395/* get the current arch name */
396/* caller is responsible for freeing returned pointer */
397static char *
398FcCacheGetCurrentArch (void)
399{
400 struct utsname b;
401 char * current_arch_machine_name;
402
403 if (uname(&b) == -1)
404 return FcFalse;
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;
409}
410
411/* return the address of the segment for the provided arch,
412 * or -1 if arch not found */
413static off_t
414FcCacheSkipToArch (int fd, const char * arch)
415{
416 char candidate_arch_machine_name[64], bytes_to_skip[7];
417 off_t current_arch_start = 0;
418
419 /* skip arches that are not the current arch */
420 while (1)
421 {
422 long bs;
423
424 lseek (fd, current_arch_start, SEEK_SET);
425 if (FcCacheReadString (fd, candidate_arch_machine_name,
426 sizeof (candidate_arch_machine_name)) == 0)
427 break;
428 if (FcCacheReadString (fd, bytes_to_skip, 7) == 0)
429 break;
430 bs = a64l(bytes_to_skip);
431 if (bs == 0)
432 break;
433
434 if (strcmp (candidate_arch_machine_name, arch)==0)
435 break;
436 current_arch_start += bs;
437 }
438
439 if (strcmp (candidate_arch_machine_name, arch)!=0)
440 return -1;
441
442 return current_arch_start;
443}
444
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
447 * file. */
448#define BUF_SIZE 8192
449
450static FcBool
451FcCacheMoveDown (int fd, off_t start)
452{
453 char * buf = malloc (BUF_SIZE);
454 char candidate_arch_machine_name[64], bytes_to_skip[7];
4262e0b3 455 long bs;
212c9f43
PL
456 int c, bytes_skipped;
457
458 if (!buf)
459 return FcFalse;
460
461 lseek (fd, start, SEEK_SET);
462 if (FcCacheReadString (fd, candidate_arch_machine_name,
463 sizeof (candidate_arch_machine_name)) == 0)
464 goto done;
465 if (FcCacheReadString (fd, bytes_to_skip, 7) == 0)
466 goto done;
467
468 bs = a64l(bytes_to_skip);
469 if (bs == 0)
470 goto done;
471
472 bytes_skipped = 0;
473 do
474 {
475 lseek (fd, start+bs+bytes_skipped, SEEK_SET);
476 if ((c = read (fd, buf, BUF_SIZE)) <= 0)
477 break;
478 lseek (fd, start+bytes_skipped, SEEK_SET);
479 if (write (fd, buf, c) < 0)
480 goto bail;
481 bytes_skipped += c;
482 }
483 while (c > 0);
484 lseek (fd, start+bytes_skipped, SEEK_SET);
485
486 done:
487 free (buf);
488 return FcTrue;
489
490 bail:
491 free (buf);
492 return FcFalse;
493}
494
4262e0b3
PL
495static int
496FcCacheReadDirs (FcStrList *list, FcFontSet * set)
497{
498 DIR *d;
499 struct dirent *e;
500 int ret = 0;
501 FcChar8 *dir;
502 FcChar8 *file, *base;
503 FcStrSet *subdirs;
504 FcStrList *sublist;
505 struct stat statb;
506
507 /*
508 * Now scan all of the directories into separate databases
509 * and write out the results
510 */
511 while ((dir = FcStrListNext (list)))
512 {
513 /* freed below */
514 file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
515 if (!file)
516 return FcFalse;
517
518 strcpy ((char *) file, (char *) dir);
519 strcat ((char *) file, "/");
520 base = file + strlen ((char *) file);
521
522 subdirs = FcStrSetCreate ();
523 if (!subdirs)
524 {
525 fprintf (stderr, "Can't create directory set\n");
526 ret++;
527 free (file);
528 continue;
529 }
530
531 if (access ((char *) dir, X_OK) < 0)
532 {
533 switch (errno) {
534 case ENOENT:
535 case ENOTDIR:
536 case EACCES:
537 break;
538 default:
539 fprintf (stderr, "\"%s\": ", dir);
540 perror ("");
541 ret++;
542 }
543 FcStrSetDestroy (subdirs);
544 free (file);
545 continue;
546 }
547 if (stat ((char *) dir, &statb) == -1)
548 {
549 fprintf (stderr, "\"%s\": ", dir);
550 perror ("");
551 FcStrSetDestroy (subdirs);
552 ret++;
553 free (file);
554 continue;
555 }
556 if (!S_ISDIR (statb.st_mode))
557 {
558 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
559 FcStrSetDestroy (subdirs);
560 free (file);
561 continue;
562 }
563 d = opendir ((char *) dir);
564 if (!d)
565 {
566 FcStrSetDestroy (subdirs);
567 free (file);
568 continue;
569 }
570 while ((e = readdir (d)))
571 {
572 if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN)
573 {
574 strcpy ((char *) base, (char *) e->d_name);
575 if (FcFileIsDir (file) && !FcStrSetAdd (subdirs, file))
576 ret++;
577 }
578 }
579 closedir (d);
580 if (1 || FcDirCacheValid (dir))
581 {
582 FcDirCacheRead (set, dir);
583 }
584 else
585 {
586 ret++;
587#if 0 // (implement per-dir loading)
588 if (verbose)
589 printf ("caching, %d fonts, %d dirs\n",
590 set->nfont, nsubdirs (subdirs));
591
592 if (!FcDirSave (set, dir))
593 {
594 fprintf (stderr, "Can't save cache in \"%s\"\n", dir);
595 ret++;
596 }
597#endif
598 }
599 sublist = FcStrListCreate (subdirs);
600 FcStrSetDestroy (subdirs);
601 if (!sublist)
602 {
603 fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
604 ret++;
605 free (file);
606 continue;
607 }
608 ret += FcCacheReadDirs (sublist, set);
609 free (file);
610 }
611 FcStrListDone (list);
612 return ret;
613}
614
615FcFontSet *
616FcCacheRead (FcConfig *config)
617{
618 FcFontSet * s = FcFontSetCreate();
619 if (!s)
620 return 0;
621
622 if (force)
623 goto bail;
624
625 if (FcCacheReadDirs (FcConfigGetConfigDirs (config), s))
626 goto bail;
627
628 return s;
629
630 bail:
631 FcFontSetDestroy (s);
632 return 0;
633}
634
212c9f43
PL
635/* read serialized state from the cache file */
636FcBool
4262e0b3 637FcDirCacheRead (FcFontSet * set, const FcChar8 *dir)
212c9f43 638{
4262e0b3
PL
639 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
640 int fd;
212c9f43 641 FcCache metadata;
4262e0b3 642 void * current_dir_block;
212c9f43
PL
643 char * current_arch_machine_name;
644 char candidate_arch_machine_name[64], bytes_in_block[7];
645 off_t current_arch_start = 0;
646
647 if (force)
4262e0b3
PL
648 goto bail;
649 if (!cache_file)
650 goto bail;
212c9f43 651
4262e0b3
PL
652 current_arch_machine_name = FcCacheGetCurrentArch();
653 fd = open(cache_file, O_RDONLY);
212c9f43 654 if (fd == -1)
4262e0b3 655 goto bail0;
212c9f43 656
212c9f43
PL
657 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
658 if (current_arch_start < 0)
4262e0b3 659 goto bail1;
212c9f43
PL
660
661 lseek (fd, current_arch_start, SEEK_SET);
662 if (FcCacheReadString (fd, candidate_arch_machine_name,
663 sizeof (candidate_arch_machine_name)) == 0)
4262e0b3 664 goto bail1;
212c9f43 665 if (FcCacheReadString (fd, bytes_in_block, 7) == 0)
4262e0b3 666 goto bail1;
212c9f43
PL
667
668 // sanity check for endianness issues
669 read(fd, &metadata, sizeof(FcCache));
670 if (metadata.magic != FC_CACHE_MAGIC)
4262e0b3 671 goto bail1;
212c9f43 672
4262e0b3
PL
673 if (metadata.count)
674 {
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)
679 perror("");
680
681 if (!FcFontSetUnserialize (metadata, set, current_dir_block))
682 goto bail1;
683 }
684
212c9f43
PL
685 close(fd);
686 free (current_arch_machine_name);
4262e0b3 687 free (cache_file);
212c9f43
PL
688 return FcTrue;
689
690 bail1:
212c9f43 691 close(fd);
4262e0b3 692 bail0:
212c9f43 693 free (current_arch_machine_name);
4262e0b3
PL
694 bail:
695 free (cache_file);
212c9f43
PL
696 return FcFalse;
697}
698
699/* write serialized state to the cache file */
700FcBool
4262e0b3 701FcDirCacheWrite (int bank, FcFontSet *set, const FcChar8 *dir)
212c9f43 702{
4262e0b3
PL
703 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
704 int fd, bytes_to_write, metadata_bytes;
212c9f43
PL
705 FcCache metadata;
706 off_t current_arch_start = 0, truncate_to;
707 char * current_arch_machine_name, bytes_written[7] = "dedbef";
4262e0b3 708 void * current_dir_block, *final_dir_block;
212c9f43 709
4262e0b3
PL
710 if (!cache_file)
711 goto bail;
712
713 FcFontSetNewBank();
714 bytes_to_write = FcFontSetNeededBytes (set);
715 metadata_bytes = FcCacheNextOffset (sizeof (FcCache));
212c9f43 716
4262e0b3
PL
717 if (!bytes_to_write)
718 {
719 unlink (cache_file);
720 free (cache_file);
721 return FcTrue;
722 }
723
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)
729 goto bail;
730 final_dir_block = FcFontSetDistributeBytes (&metadata, current_dir_block);
731
732 if (!FcFontSetSerialize (bank, set))
212c9f43
PL
733 return FcFalse;
734
4262e0b3
PL
735 if (FcDebug () & FC_DBG_CACHE)
736 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
737
738 fd = open(cache_file, O_RDWR | O_CREAT, 0666);
212c9f43
PL
739 if (fd == -1)
740 return FcFalse;
741
742 current_arch_machine_name = FcCacheGetCurrentArch();
743 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
744 if (current_arch_start < 0)
4262e0b3 745 current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END));
212c9f43
PL
746
747 if (!FcCacheMoveDown(fd, current_arch_start))
4262e0b3 748 goto bail1;
212c9f43
PL
749
750 current_arch_start = lseek(fd, 0, SEEK_CUR);
751 if (ftruncate (fd, current_arch_start) == -1)
4262e0b3 752 goto bail1;
212c9f43
PL
753
754 /* reserve space for arch, count & metadata */
755 if (!FcCacheWriteString (fd, current_arch_machine_name))
4262e0b3 756 goto bail1;
212c9f43
PL
757
758 /* now write the address of the next offset */
4262e0b3
PL
759 truncate_to = FcCacheNextOffset(current_arch_start + bytes_to_write + metadata_bytes) -
760 current_arch_start;
212c9f43
PL
761 strcpy (bytes_written, l64a(truncate_to));
762 if (!FcCacheWriteString (fd, bytes_written))
4262e0b3 763 goto bail1;
212c9f43 764
4262e0b3
PL
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);
769
770 /* this actually serves to pad out the cache file */
212c9f43 771 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
4262e0b3 772 goto bail1;
212c9f43
PL
773
774 close(fd);
775 return FcTrue;
776
4262e0b3
PL
777 bail1:
778 free (current_dir_block);
212c9f43 779 free (current_arch_machine_name);
4262e0b3
PL
780 bail:
781 unlink (cache_file);
782 free (cache_file);
212c9f43
PL
783 return FcFalse;
784}
785
786/* if true, ignore the cache file */
787void
788FcCacheForce (FcBool f)
789{
790 force = f;
791}
4262e0b3
PL
792
793static int banks_ptr = 0, banks_alloc = 0;
794static int * bankId = 0;
795
796int
797FcCacheBankCount (void)
798{
799 return banks_ptr;
800}
801
802FcBool
803FcCacheHaveBank (int bank)
804{
805 int i;
806
807 if (bank < FC_BANK_FIRST)
808 return FcTrue;
809
810 for (i = 0; i < banks_ptr; i++)
811 if (bankId[i] == bank)
812 return FcTrue;
813
814 return FcFalse;
815}
816
817int
818FcCacheBankToIndex (int bank)
819{
820 static int lastBank = FC_BANK_DYNAMIC, lastIndex = -1;
821 int i;
822 int * b;
823
824 if (bank == lastBank)
825 return lastIndex;
826
827 for (i = 0; i < banks_ptr; i++)
828 if (bankId[i] == bank)
829 return i;
830
831 if (banks_ptr <= banks_alloc)
832 {
833 b = realloc (bankId, banks_alloc + 4);
834 if (!b)
835 return -1;
836
837 bankId = b;
838 banks_alloc += 4;
839 }
840
841 i = banks_ptr++;
842 bankId[i] = bank;
843 return i;
844}