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