]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
Overhaul the serialization system to create one mmapable file per directory
[fontconfig.git] / src / fccache.c
1 /*
2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
3 *
4 * Copyright © 2000 Keith Packard
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
25 #include <fcntl.h>
26 #include <dirent.h>
27 #include <sys/mman.h>
28 #include <sys/utsname.h>
29 #include "fcint.h"
30
31 #define PAGESIZE 8192
32
33 static FcBool force;
34
35 static FcChar8 *
36 FcCacheReadString (int fd, FcChar8 *dest, int len)
37 {
38 FcChar8 c;
39 FcBool escape;
40 int size;
41 int i;
42
43 if (len == 0)
44 return FcFalse;
45
46 size = len;
47 i = 0;
48 escape = FcFalse;
49 while (read (fd, &c, 1) == 1)
50 {
51 if (!escape)
52 {
53 switch (c) {
54 case '"':
55 c = '\0';
56 break;
57 case '\\':
58 escape = FcTrue;
59 continue;
60 }
61 }
62 if (i == size)
63 {
64 dest[i++] = 0;
65 return dest;
66 }
67 dest[i++] = c;
68 if (c == '\0')
69 return dest;
70 escape = FcFalse;
71 }
72 return 0;
73 }
74
75 static FcBool
76 FcCacheWriteString (int fd, const FcChar8 *chars)
77 {
78 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
79 return FcFalse;
80 return FcTrue;
81 }
82
83 #if 0
84 /*
85 * Verify the saved timestamp for a file
86 */
87 FcBool
88 FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info)
89 {
90 struct stat statb;
91
92 if (stat ((char *) file, &statb) < 0)
93 {
94 if (FcDebug () & FC_DBG_CACHE)
95 printf (" file %s missing\n", file);
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
108 void
109 FcGlobalCacheReferenced (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
127 typedef struct _FcFilePathInfo {
128 const FcChar8 *dir;
129 int dir_len;
130 const FcChar8 *base;
131 unsigned int base_hash;
132 } FcFilePathInfo;
133
134 static FcFilePathInfo
135 FcFilePathInfoGet (const FcChar8 *path)
136 {
137 FcFilePathInfo i;
138 FcChar8 *slash;
139
140 slash = FcStrLastSlash (path);
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 }
155 i.base_hash = FcCacheHash (i.base, -1);
156 return i;
157 }
158
159 FcGlobalCache *
160 FcGlobalCacheCreate (void)
161 {
162 FcGlobalCache *cache;
163 int h;
164
165 cache = malloc (sizeof (FcGlobalCache));
166 if (!cache)
167 return 0;
168 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
169 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
170 cache->ents[h] = 0;
171 cache->entries = 0;
172 cache->referenced = 0;
173 cache->updated = FcFalse;
174 cache->broken = FcFalse;
175 return cache;
176 }
177
178 void
179 FcGlobalCacheDestroy (FcGlobalCache *cache)
180 {
181 FcGlobalCacheDir *d, *next;
182 int h;
183
184 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
185 {
186 for (d = cache->ents[h]; d; d = next)
187 {
188 next = d->next;
189 FcGlobalCacheDirDestroy (d);
190 }
191 }
192 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
193 free (cache);
194 }
195
196 void
197 FcGlobalCacheLoad (FcGlobalCache *cache,
198 const FcChar8 *cache_file)
199 {
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;
206
207 f = fopen ((char *) cache_file, "r");
208 if (!f)
209 return;
210
211 cache->updated = FcFalse;
212 file = 0;
213 name = 0;
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))))
218 {
219 if (FcDebug () & FC_DBG_CACHEV)
220 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
221 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
222 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue);
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);
232 if (file != file_buf)
233 free (file);
234 if (name != name_buf)
235 free (name);
236 file = 0;
237 name = 0;
238 }
239 if (file && file != file_buf)
240 free (file);
241 if (name && name != name_buf)
242 free (name);
243 fclose (f);
244 }
245
246 FcBool
247 FcGlobalCacheUpdate (FcGlobalCache *cache,
248 const FcChar8 *file,
249 int id,
250 const FcChar8 *name)
251 {
252 const FcChar8 *match;
253 struct stat statb;
254 FcGlobalCacheInfo *info;
255
256 match = file;
257
258 if (stat ((char *) file, &statb) < 0)
259 return FcFalse;
260 if (S_ISDIR (statb.st_mode))
261 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
262 FcTrue, FcTrue);
263 else
264 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
265 name, FcTrue);
266 if (info)
267 {
268 FcGlobalCacheReferenced (cache, info);
269 cache->updated = FcTrue;
270 }
271 else
272 cache->broken = FcTrue;
273 return info != 0;
274 }
275
276 FcBool
277 FcGlobalCacheSave (FcGlobalCache *cache,
278 const FcChar8 *cache_file)
279 {
280 FILE *f;
281 int dir_hash, file_hash;
282 FcGlobalCacheDir *dir;
283 FcGlobalCacheFile *file;
284 FcAtomic *atomic;
285
286 if (!cache->updated && cache->referenced == cache->entries)
287 return FcTrue;
288
289 if (cache->broken)
290 return FcFalse;
291
292 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
293 /* Set-UID programs can't safely update the cache */
294 if (getuid () != geteuid ())
295 return FcFalse;
296 #endif
297
298 atomic = FcAtomicCreate (cache_file);
299 if (!atomic)
300 goto bail0;
301 if (!FcAtomicLock (atomic))
302 goto bail1;
303 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
304 if (!f)
305 goto bail2;
306
307 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
308 {
309 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
310 {
311 if (!dir->info.referenced)
312 continue;
313 if (!FcCacheWriteString (f, dir->info.file))
314 goto bail4;
315 if (PUTC (' ', f) == EOF)
316 goto bail4;
317 if (!FcCacheWriteInt (f, 0))
318 goto bail4;
319 if (PUTC (' ', f) == EOF)
320 goto bail4;
321 if (!FcCacheWriteTime (f, dir->info.time))
322 goto bail4;
323 if (PUTC (' ', f) == EOF)
324 goto bail4;
325 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
326 goto bail4;
327 if (PUTC ('\n', f) == EOF)
328 goto bail4;
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;
338 if (PUTC (' ', f) == EOF)
339 goto bail4;
340 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
341 goto bail4;
342 if (PUTC (' ', f) == EOF)
343 goto bail4;
344 if (!FcCacheWriteTime (f, file->info.time))
345 goto bail4;
346 if (PUTC (' ', f) == EOF)
347 goto bail4;
348 if (!FcCacheWriteString (f, file->name))
349 goto bail4;
350 if (PUTC ('\n', f) == EOF)
351 goto bail4;
352 }
353 }
354 }
355 }
356
357 if (fclose (f) == EOF)
358 goto bail3;
359
360 if (!FcAtomicReplaceOrig (atomic))
361 goto bail3;
362
363 FcAtomicUnlock (atomic);
364 FcAtomicDestroy (atomic);
365
366 cache->updated = FcFalse;
367 return FcTrue;
368
369 bail4:
370 fclose (f);
371 bail3:
372 FcAtomicDeleteNew (atomic);
373 bail2:
374 FcAtomicUnlock (atomic);
375 bail1:
376 FcAtomicDestroy (atomic);
377 bail0:
378 return FcFalse;
379 }
380 #endif
381
382 /*
383 * Find the next presumably-mmapable offset after the current file
384 * pointer.
385 */
386 static int
387 FcCacheNextOffset(off_t w)
388 {
389 if (w % PAGESIZE == 0)
390 return w;
391 else
392 return ((w / PAGESIZE)+1)*PAGESIZE;
393 }
394
395 /* get the current arch name */
396 /* caller is responsible for freeing returned pointer */
397 static char *
398 FcCacheGetCurrentArch (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 */
413 static off_t
414 FcCacheSkipToArch (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
450 static FcBool
451 FcCacheMoveDown (int fd, off_t start)
452 {
453 char * buf = malloc (BUF_SIZE);
454 char candidate_arch_machine_name[64], bytes_to_skip[7];
455 long bs;
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
495 static int
496 FcCacheReadDirs (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
615 FcFontSet *
616 FcCacheRead (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
635 /* read serialized state from the cache file */
636 FcBool
637 FcDirCacheRead (FcFontSet * set, const FcChar8 *dir)
638 {
639 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
640 int fd;
641 FcCache metadata;
642 void * current_dir_block;
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)
648 goto bail;
649 if (!cache_file)
650 goto bail;
651
652 current_arch_machine_name = FcCacheGetCurrentArch();
653 fd = open(cache_file, O_RDONLY);
654 if (fd == -1)
655 goto bail0;
656
657 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
658 if (current_arch_start < 0)
659 goto bail1;
660
661 lseek (fd, current_arch_start, SEEK_SET);
662 if (FcCacheReadString (fd, candidate_arch_machine_name,
663 sizeof (candidate_arch_machine_name)) == 0)
664 goto bail1;
665 if (FcCacheReadString (fd, bytes_in_block, 7) == 0)
666 goto bail1;
667
668 // sanity check for endianness issues
669 read(fd, &metadata, sizeof(FcCache));
670 if (metadata.magic != FC_CACHE_MAGIC)
671 goto bail1;
672
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
685 close(fd);
686 free (current_arch_machine_name);
687 free (cache_file);
688 return FcTrue;
689
690 bail1:
691 close(fd);
692 bail0:
693 free (current_arch_machine_name);
694 bail:
695 free (cache_file);
696 return FcFalse;
697 }
698
699 /* write serialized state to the cache file */
700 FcBool
701 FcDirCacheWrite (int bank, FcFontSet *set, const FcChar8 *dir)
702 {
703 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
704 int fd, bytes_to_write, metadata_bytes;
705 FcCache metadata;
706 off_t current_arch_start = 0, truncate_to;
707 char * current_arch_machine_name, bytes_written[7] = "dedbef";
708 void * current_dir_block, *final_dir_block;
709
710 if (!cache_file)
711 goto bail;
712
713 FcFontSetNewBank();
714 bytes_to_write = FcFontSetNeededBytes (set);
715 metadata_bytes = FcCacheNextOffset (sizeof (FcCache));
716
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))
733 return FcFalse;
734
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);
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)
745 current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END));
746
747 if (!FcCacheMoveDown(fd, current_arch_start))
748 goto bail1;
749
750 current_arch_start = lseek(fd, 0, SEEK_CUR);
751 if (ftruncate (fd, current_arch_start) == -1)
752 goto bail1;
753
754 /* reserve space for arch, count & metadata */
755 if (!FcCacheWriteString (fd, current_arch_machine_name))
756 goto bail1;
757
758 /* now write the address of the next offset */
759 truncate_to = FcCacheNextOffset(current_arch_start + bytes_to_write + metadata_bytes) -
760 current_arch_start;
761 strcpy (bytes_written, l64a(truncate_to));
762 if (!FcCacheWriteString (fd, bytes_written))
763 goto bail1;
764
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 */
771 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
772 goto bail1;
773
774 close(fd);
775 return FcTrue;
776
777 bail1:
778 free (current_dir_block);
779 free (current_arch_machine_name);
780 bail:
781 unlink (cache_file);
782 free (cache_file);
783 return FcFalse;
784 }
785
786 /* if true, ignore the cache file */
787 void
788 FcCacheForce (FcBool f)
789 {
790 force = f;
791 }
792
793 static int banks_ptr = 0, banks_alloc = 0;
794 static int * bankId = 0;
795
796 int
797 FcCacheBankCount (void)
798 {
799 return banks_ptr;
800 }
801
802 FcBool
803 FcCacheHaveBank (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
817 int
818 FcCacheBankToIndex (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 }