]> git.wh0rd.org Git - fontconfig.git/blob - src/fccache.c
6b6b8e36c54614b7aca380ffc1d748a1ca60cad8
[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, 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         d = opendir ((char *) dir);
547         if (!d)
548         {
549             FcStrSetDestroy (subdirs);
550             free (file);
551             continue;
552         }
553         while ((e = readdir (d)))
554         {
555             if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN)
556             {
557                 strcpy ((char *) base, (char *) e->d_name);
558                 if (FcFileIsDir (file) && !FcStrSetAdd (subdirs, file))
559                     ret++;
560             }
561         }
562         closedir (d);
563         if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, dir))
564         {
565             if (FcDebug () & FC_DBG_FONTSET)
566                 printf ("scan dir %s\n", dir);
567             FcDirScanConfig (set, subdirs, cache, 
568                              config->blanks, dir, FcFalse, config);
569         }
570         sublist = FcStrListCreate (subdirs);
571         FcStrSetDestroy (subdirs);
572         if (!sublist)
573         {
574             fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
575             ret++;
576             free (file);
577             continue;
578         }
579         ret += FcCacheReadDirs (config, cache, sublist, set);
580         free (file);
581     }
582     FcStrListDone (list);
583     return ret;
584 }
585
586 FcFontSet *
587 FcCacheRead (FcConfig *config, FcGlobalCache * cache)
588 {
589     FcFontSet * s = FcFontSetCreate();
590     if (!s) 
591         return 0;
592
593     if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s))
594         goto bail;
595
596     return s;
597
598  bail:
599     FcFontSetDestroy (s);
600     return 0;
601 }
602
603 /* read serialized state from the cache file */
604 static FcBool
605 FcDirCacheRead (FcFontSet * set, const FcChar8 *dir)
606 {
607     FcChar8         *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
608     int fd;
609     char * current_arch_machine_name;
610     char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
611     off_t current_arch_start = 0;
612
613     if (!cache_file)
614         goto bail;
615
616     current_arch_machine_name = FcCacheMachineSignature();
617     fd = open(cache_file, O_RDONLY);
618     if (fd == -1)
619         goto bail;
620
621     current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
622     if (current_arch_start < 0)
623         goto bail1;
624
625     lseek (fd, current_arch_start, SEEK_SET);
626     if (FcCacheReadString (fd, candidate_arch_machine_name, 
627                            sizeof (candidate_arch_machine_name)) == 0)
628         goto bail1;
629     
630     if (!FcDirCacheConsume (fd, set))
631         goto bail1;
632         
633     close(fd);
634     free (cache_file);
635     return FcTrue;
636
637  bail1:
638     close(fd);
639  bail:
640     free (cache_file);
641     return FcFalse;
642 }
643
644 static FcBool
645 FcDirCacheConsume (int fd, FcFontSet *set)
646 {
647     FcCache metadata;
648     void * current_dir_block;
649     off_t pos;
650
651     read(fd, &metadata, sizeof(FcCache));
652     if (metadata.magic != FC_CACHE_MAGIC)
653         return FcFalse;
654
655     if (!metadata.count)
656         return FcTrue;
657
658     pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
659     current_dir_block = mmap (0, metadata.count, 
660                               PROT_READ, MAP_SHARED, fd, pos);
661     if (current_dir_block == MAP_FAILED)
662         return FcFalse;
663     
664     if (!FcFontSetUnserialize (metadata, set, current_dir_block))
665         return FcFalse;
666
667     return FcTrue;
668 }
669
670 static void *
671 FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
672 {
673     void * current_dir_block, * final_dir_block;
674     static int rand_state = 0;
675     int bank;
676
677     if (!rand_state) 
678         rand_state = time(0L);
679     bank = rand_r(&rand_state);
680
681     while (FcCacheHaveBank(bank))
682         bank = rand_r(&rand_state);
683
684     memset (metadata, 0, sizeof(FcCache));
685     FcFontSetNewBank();
686     metadata->count = FcFontSetNeededBytes (set);
687     metadata->magic = FC_CACHE_MAGIC;
688     metadata->bank = bank;
689
690     if (!metadata->count) /* not a failure, no fonts to write */
691         return 0;
692
693     current_dir_block = malloc (metadata->count);
694     if (!current_dir_block)
695         goto bail;
696     final_dir_block = FcFontSetDistributeBytes (metadata, current_dir_block);
697
698     if ((char *)current_dir_block + metadata->count != final_dir_block)
699         goto bail;
700                               
701     if (!FcFontSetSerialize (bank, set))
702         goto bail;
703
704     return current_dir_block;
705
706  bail:
707     free (current_dir_block);
708     return 0;
709 }
710
711 /* write serialized state to the cache file */
712 FcBool
713 FcDirCacheWrite (FcFontSet *set, const FcChar8 *dir)
714 {
715     FcChar8         *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
716     int fd;
717     FcCache metadata;
718     off_t current_arch_start = 0, truncate_to;
719     char * current_arch_machine_name, * header;
720     void * current_dir_block;
721
722     if (!cache_file)
723         goto bail;
724
725     current_dir_block = FcDirCacheProduce (set, &metadata);
726
727     if (!metadata.count)
728     {
729         unlink (cache_file);
730         free (cache_file);
731         return FcTrue;
732     }
733
734     if (!current_dir_block)
735         goto bail;
736
737     if (FcDebug () & FC_DBG_CACHE)
738         printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
739
740     fd = open(cache_file, O_RDWR | O_CREAT, 0666);
741     if (fd == -1)
742         goto bail0;
743
744     current_arch_machine_name = FcCacheMachineSignature ();
745     current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
746     if (current_arch_start < 0)
747         current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END));
748
749     if (!FcCacheMoveDown(fd, current_arch_start))
750         goto bail2;
751
752     current_arch_start = lseek(fd, 0, SEEK_CUR);
753     if (ftruncate (fd, current_arch_start) == -1)
754         goto bail2;
755
756     /* now write the address of the next offset */
757     truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + sizeof (FcCache)) + metadata.count) - current_arch_start;
758     header = malloc (10 + strlen (current_arch_machine_name));
759     if (!header)
760         goto bail1;
761     sprintf (header, "%8x ", (int)truncate_to);
762     strcat (header, current_arch_machine_name);
763     if (!FcCacheWriteString (fd, header))
764         goto bail1;
765
766     write (fd, &metadata, sizeof(FcCache));
767     lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_END)), SEEK_SET);
768     write (fd, current_dir_block, metadata.count);
769     free (current_dir_block);
770
771     /* this actually serves to pad out the cache file, if needed */
772     if (ftruncate (fd, current_arch_start + truncate_to) == -1)
773         goto bail1;
774
775     close(fd);
776     return FcTrue;
777
778  bail2:
779     free (header);
780  bail1:
781     free (current_arch_machine_name);
782  bail0:
783     free (current_dir_block);
784  bail:
785     unlink (cache_file);
786     free (cache_file);
787     return FcFalse;
788 }
789
790 static char *
791 FcCacheMachineSignature ()
792 {
793     static char buf[MACHINE_SIGNATURE_SIZE];
794     int magic = ENDIAN_TEST;
795     char * m = (char *)&magic;
796
797     sprintf (buf, "%2x%2x%2x%2x "
798              "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x "
799              "%4x %4x %4x %4x %4x %4x %4x\n", 
800              m[0], m[1], m[2], m[3],
801              sizeof (char),
802              sizeof (char *),
803              sizeof (int),
804              sizeof (FcPattern),
805              sizeof (FcPatternEltPtr),
806              sizeof (struct _FcPatternElt *),
807              sizeof (FcPatternElt),
808              sizeof (FcObjectPtr),
809              sizeof (FcValueListPtr),
810              sizeof (FcValue),
811              sizeof (FcValueBinding),
812              sizeof (struct _FcValueList *),
813              sizeof (FcCharSet),
814              sizeof (FcCharLeaf **),
815              sizeof (FcChar16 *),
816              sizeof (FcChar16),
817              sizeof (FcCharLeaf),
818              sizeof (FcChar32),
819              sizeof (FcCache));
820
821     return buf;
822 }
823
824 static int banks_ptr = 0, banks_alloc = 0;
825 static int * bankId = 0;
826
827 static FcBool
828 FcCacheHaveBank (int bank)
829 {
830     int i;
831
832     if (bank < FC_BANK_FIRST)
833         return FcTrue;
834
835     for (i = 0; i < banks_ptr; i++)
836         if (bankId[i] == bank)
837             return FcTrue;
838
839     return FcFalse;
840 }
841
842 int
843 FcCacheBankToIndex (int bank)
844 {
845     static int lastBank = FC_BANK_DYNAMIC, lastIndex = -1;
846     int i;
847     int * b;
848
849     if (bank == lastBank)
850         return lastIndex;
851
852     for (i = 0; i < banks_ptr; i++)
853         if (bankId[i] == bank)
854             return i;
855
856     if (banks_ptr >= banks_alloc)
857     {
858         b = realloc (bankId, (banks_alloc + 4) * sizeof(int));
859         if (!b)
860             return -1;
861
862         bankId = b;
863         banks_alloc += 4;
864     }
865
866     i = banks_ptr++;
867     bankId[i] = bank;
868     return i;
869 }