]> git.wh0rd.org Git - fontconfig.git/blob - src/fccache.c
020230e3e6530f287cde4c8bb9dd274de340ce6c
[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  * Copyright © 2005 Patrick Lam
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of Keith Packard not be used in
12  * advertising or publicity pertaining to distribution of the software without
13  * specific, written prior permission.  Keith Packard makes no
14  * representations about the suitability of this software for any purpose.  It
15  * is provided "as is" without express or implied warranty.
16  *
17  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23  * PERFORMANCE OF THIS SOFTWARE.
24  */
25
26 #include "fcint.h"
27 #include "../fc-arch/fcarch.h"
28 #include <fcntl.h>
29 #include <dirent.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
33 #  include <unistd.h>
34 #  include <sys/mman.h>
35 #elif defined(_WIN32)
36 #  include <windows.h>
37 #endif
38
39 #define ENDIAN_TEST 0x12345678
40 #define MACHINE_SIGNATURE_SIZE (9 + 5*20 + 1)
41 /* for when we don't have sysconf: */
42 #define FC_HARDCODED_PAGESIZE 8192 
43
44 #ifndef O_BINARY
45 #define O_BINARY 0
46 #endif
47
48 static int
49 FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path);
50
51 static off_t
52 FcCacheSkipToArch (int fd, const char * arch);
53
54 static FcBool 
55 FcCacheCopyOld (int fd, int fd_orig, off_t start);
56
57 static void *
58 FcDirCacheProduce (FcFontSet *set, FcCache * metadata);
59
60 static FcBool
61 FcDirCacheConsume (int fd, const char * dir, FcFontSet *set, FcConfig *config);
62
63 static int
64 FcCacheNextOffset(off_t w);
65
66 static char *
67 FcCacheMachineSignature (void);
68
69 static FcBool
70 FcCacheHaveBank (int bank);
71
72 static void
73 FcCacheAddBankDir (int bank, const char * dir);
74
75 struct MD5Context {
76         FcChar32 buf[4];
77         FcChar32 bits[2];
78         unsigned char in[64];
79 };
80
81 static void MD5Init(struct MD5Context *ctx);
82 static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len);
83 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
84 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
85
86 #define FC_DBG_CACHE_REF    1024
87
88 static char *
89 FcCacheReadString (int fd, char *dest, int len)
90 {
91     int    size;
92     int    slen;
93
94     if (len == 0)
95         return 0;
96
97     size = read (fd, dest, len-1);
98
99     if (size > 0)
100     {
101         dest[size] = '\0';
102         slen = strlen (dest);
103
104         lseek (fd, slen - size + 1, SEEK_CUR);
105         return slen < len ? dest : 0;
106     }
107
108     return 0;
109 }
110
111 static void
112 FcCacheSkipString (int fd)
113 {
114     char buf[256];
115     int  size;
116     int  slen;
117
118     while ( (size = read (fd, buf, sizeof (buf)-1)) > 0) 
119     {
120         buf [size] = '\0';
121         slen = strlen (buf);
122         if (slen < size) 
123         {
124             lseek (fd, slen - size + 1, SEEK_CUR);
125             return;
126         }
127     }
128 }
129
130 static FcBool
131 FcCacheWriteString (int fd, const char *chars)
132 {
133     if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
134         return FcFalse;
135     return FcTrue;
136 }
137
138 static void
139 FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
140 {
141     FcStrSetDestroy (d->subdirs);
142     FcMemFree (FC_MEM_STRING, strlen (d->name)+1);
143     free (d->name);
144     FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir));
145     free (d);
146 }
147
148 FcGlobalCache *
149 FcGlobalCacheCreate (void)
150 {
151     FcGlobalCache   *cache;
152
153     cache = malloc (sizeof (FcGlobalCache));
154     if (!cache)
155         return 0;
156     FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
157     cache->dirs = 0;
158     cache->updated = FcFalse;
159     cache->fd = -1;
160     return cache;
161 }
162
163 void
164 FcGlobalCacheDestroy (FcGlobalCache *cache)
165 {
166     FcGlobalCacheDir    *d, *next;
167
168     for (d = cache->dirs; d; d = next)
169     {
170         next = d->next;
171         FcGlobalCacheDirDestroy (d);
172     }
173     FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
174     if (cache->fd != -1)
175         close (cache->fd);
176     free (cache);
177 }
178
179 void
180 FcGlobalCacheLoad (FcGlobalCache    *cache,
181                    FcStrSet         *staleDirs,
182                    const FcChar8    *cache_file,
183                    FcConfig         *config)
184 {
185     char                name_buf[FC_MAX_FILE_LEN];
186     FcGlobalCacheDir    *d, *next;
187     FcFileTime          config_time = FcConfigModifiedTime (config);
188     char                * current_arch_machine_name;
189     char                candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
190     off_t               current_arch_start;
191
192     struct stat         cache_stat, dir_stat;
193     char                subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
194
195     if (stat ((char *) cache_file, &cache_stat) < 0)
196         return;
197
198     cache->fd = open ((char *) cache_file, O_RDONLY | O_BINARY);
199     if (cache->fd == -1)
200         return;
201
202     cache->updated = FcFalse;
203
204     if (!FcCacheReadString (cache->fd, name_buf, sizeof (name_buf)))
205         goto bail_and_destroy;
206     if (strcmp (name_buf, FC_GLOBAL_MAGIC_COOKIE) != 0)
207         goto bail_and_destroy;
208
209     current_arch_machine_name = FcCacheMachineSignature ();
210     current_arch_start = FcCacheSkipToArch(cache->fd, 
211                                            current_arch_machine_name);
212     if (current_arch_start < 0)
213         goto bail1;
214
215     lseek (cache->fd, current_arch_start, SEEK_SET);
216     if (!FcCacheReadString (cache->fd, candidate_arch_machine_name, 
217                             sizeof (candidate_arch_machine_name)))
218         goto bail_and_destroy;
219     if (strlen(candidate_arch_machine_name) == 0)
220         goto bail_and_destroy;
221
222     while (1) 
223     {
224         off_t targ;
225
226         if (!FcCacheReadString (cache->fd, name_buf, sizeof (name_buf)) || 
227             !strlen(name_buf))
228             break;
229
230         /* Directory must be older than the global cache file; also
231            cache must be newer than the config file. */
232         if (stat ((char *) name_buf, &dir_stat) < 0 || 
233             dir_stat.st_mtime > cache_stat.st_mtime ||
234             (config_time.set && cache_stat.st_mtime < config_time.time))
235         {
236             FcCache md;
237             off_t off;
238
239             FcStrSetAdd (staleDirs, (FcChar8 *)name_buf);
240
241             /* skip subdirs */
242             while (FcCacheReadString (cache->fd, subdirName, 
243                                       sizeof (subdirName)) &&
244                    strlen (subdirName))
245                 ;
246
247             if (read (cache->fd, &md, sizeof (FcCache)) != sizeof(FcCache)) 
248             {
249                 perror ("read metadata");
250                 goto bail1;
251             }
252             off = FcCacheNextOffset (lseek(cache->fd, 0, SEEK_CUR)) + md.count;
253             if (lseek (cache->fd, off, SEEK_SET) != off) 
254             {
255                 perror ("lseek");
256                 goto bail1;
257             }
258             continue;
259         }
260
261         d = malloc (sizeof (FcGlobalCacheDir));
262         if (!d)
263             goto bail1;
264
265         d->next = cache->dirs;
266         cache->dirs = d;
267
268         d->name = (char *)FcStrCopy ((FcChar8 *)name_buf);
269         d->ent = 0;
270         d->state = FcGCDirFileRead;
271
272         d->subdirs = FcStrSetCreate();
273         do
274         {
275             if (!FcCacheReadString (cache->fd, subdirName, 
276                                     sizeof (subdirName)) ||
277                 !strlen (subdirName))
278                 break;
279             FcStrSetAdd (d->subdirs, (FcChar8 *)subdirName);
280         } while (1);
281
282         d->offset = lseek (cache->fd, 0, SEEK_CUR);
283         if (read (cache->fd, &d->metadata, sizeof (FcCache)) != sizeof (FcCache))
284             goto bail1;
285         targ = FcCacheNextOffset (lseek(cache->fd, 0, SEEK_CUR)) + d->metadata.count;
286         if (lseek (cache->fd, targ, SEEK_SET) != targ)
287             goto bail1;
288     }
289     return;
290
291  bail1:
292     for (d = cache->dirs; d; d = next)
293     {
294         next = d->next;
295         free (d);
296     }
297     cache->dirs = 0;
298
299     close (cache->fd);
300     cache->fd = -1;
301     return;
302
303  bail_and_destroy:
304     close (cache->fd);
305     cache->fd = -1;
306
307     if (stat ((char *) cache_file, &cache_stat) == 0)
308         unlink ((char *)cache_file);
309
310     return;
311
312 }
313
314 FcBool
315 FcGlobalCacheReadDir (FcFontSet *set, FcStrSet *dirs, FcGlobalCache * cache, const char *dir, FcConfig *config)
316 {
317     FcGlobalCacheDir    *d;
318     int                 i;
319
320     if (cache->fd == -1)
321         return FcFalse;
322
323     for (d = cache->dirs; d; d = d->next)
324     {
325         if (strcmp (d->name, dir) == 0)
326         {
327             if (d->state == FcGCDirDisabled)
328                 return FcFalse;
329
330             if (d->state == FcGCDirFileRead) 
331             {
332                 lseek (cache->fd, d->offset, SEEK_SET);
333                 if (!FcDirCacheConsume (cache->fd, d->name, set, config))
334                     return FcFalse;
335
336                 for (i = 0; i < d->subdirs->num; i++)
337                     FcStrSetAdd (dirs, (FcChar8 *)d->subdirs->strs[i]);
338
339                 d->state = FcGCDirConsumed;
340             }
341             return FcTrue;
342         }
343     }
344
345     return FcFalse;
346 }
347
348 static FcGlobalCacheDir *
349 FcGlobalCacheDirFind (FcGlobalCache *cache, const char *name)
350 {
351     FcGlobalCacheDir * d;
352
353     if (!cache || !name)
354         return NULL;
355
356     for (d = cache->dirs; d; d = d->next)
357         if (strcmp((const char *)d->name, (const char *)name) == 0)
358             return d;
359
360     return NULL;
361  }
362
363 FcBool
364 FcGlobalCacheUpdate (FcGlobalCache  *cache,
365                      FcStrSet       *dirs,
366                      const char     *name,
367                      FcFontSet      *set,
368                      FcConfig       *config)
369 {
370     FcGlobalCacheDir    *d;
371     int                 i;
372
373     d = FcGlobalCacheDirFind (cache, name);
374
375     if (!d)
376     {
377         d = malloc (sizeof (FcGlobalCacheDir));
378         if (!d)
379             return FcFalse;
380         d->next = cache->dirs;
381         cache->dirs = d;
382     } else {
383         /* free old resources */
384         FcStrFree ((FcChar8 *)d->name);
385         free (d->ent);
386         FcStrSetDestroy (d->subdirs);
387     }
388
389     cache->updated = FcTrue;
390
391     d->name = (char *)FcStrCopy ((FcChar8 *)name);
392     d->ent = FcDirCacheProduce (set, &d->metadata);
393     d->offset = 0;
394     d->subdirs = FcStrSetCreate();
395     d->state = FcGCDirUpdated;
396     for (i = 0; i < dirs->num; i++)
397         FcStrSetAdd (d->subdirs, dirs->strs[i]);
398     return FcTrue;
399 }
400
401 FcBool
402 FcGlobalCacheSave (FcGlobalCache    *cache,
403                    const FcChar8    *cache_file,
404                    FcConfig         *config)
405 {
406     int                 fd, fd_orig;
407     FcGlobalCacheDir    *dir;
408     FcAtomic            *atomic;
409     off_t               current_arch_start = 0, truncate_to;
410     char                * current_arch_machine_name, * header;
411
412     if (!cache->updated)
413         return FcTrue;
414     
415 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
416     /* Set-UID programs can't safely update the cache */
417     if (getuid () != geteuid ())
418         return FcFalse;
419 #endif
420     
421     atomic = FcAtomicCreate (cache_file);
422     if (!atomic)
423         return FcFalse;
424
425     if (!FcAtomicLock (atomic))
426         goto bail1;
427     fd = open ((char *) FcAtomicNewFile(atomic), O_RDWR | O_CREAT | O_BINARY, 
428                S_IRUSR | S_IWUSR);
429     if (fd == -1)
430         goto bail2;
431     FcCacheWriteString (fd, FC_GLOBAL_MAGIC_COOKIE);
432
433     fd_orig = open ((char *) FcAtomicOrigFile(atomic), O_RDONLY | O_BINARY);
434
435     current_arch_machine_name = FcCacheMachineSignature ();
436     if (fd_orig == -1)
437         current_arch_start = 0;
438     else
439         current_arch_start = FcCacheSkipToArch (fd_orig, 
440                                                 current_arch_machine_name);
441
442     if (current_arch_start < 0)
443     {
444         off_t i = lseek(fd_orig, 0, SEEK_END);
445         if (i < strlen (FC_GLOBAL_MAGIC_COOKIE)+1)
446             i = strlen (FC_GLOBAL_MAGIC_COOKIE)+1;
447         current_arch_start = FcCacheNextOffset (i);
448     }
449
450     if (!FcCacheCopyOld(fd, fd_orig, current_arch_start))
451         goto bail3;
452
453     current_arch_start = lseek(fd, 0, SEEK_CUR);
454 #if defined (HAVE_FTRUNCATE)
455     if (ftruncate (fd, current_arch_start) == -1)
456         goto bail3;
457 #else
458 #if defined (HAVE_CHSIZE)
459     if (chsize (fd, current_arch_start) == -1)
460         goto bail3;
461 #else
462     goto bail3;
463 #endif
464 #endif
465
466     header = malloc (10 + strlen (current_arch_machine_name));
467     if (!header)
468         goto bail3;
469
470     truncate_to = current_arch_start + strlen(current_arch_machine_name) + 11;
471     for (dir = cache->dirs; dir; dir = dir->next)
472     {
473         int i;
474
475         if (dir->state == FcGCDirDisabled)
476             continue;
477         truncate_to += strlen(dir->name) + 1;
478         truncate_to += sizeof (FcCache);
479         truncate_to = FcCacheNextOffset (truncate_to);
480         truncate_to += dir->metadata.count;
481
482         for (i = 0; i < dir->subdirs->size; i++)
483             truncate_to += strlen((char *)dir->subdirs->strs[i]) + 1;
484         truncate_to ++;
485     }
486     truncate_to -= current_arch_start;
487
488     sprintf (header, "%8x ", (int)truncate_to);
489     strcat (header, current_arch_machine_name);
490     if (!FcCacheWriteString (fd, header))
491         goto bail4;
492
493     free (header);
494
495     for (dir = cache->dirs; dir; dir = dir->next)
496     {
497         int i;
498         const char * d;
499         off_t off;
500
501         if (!dir->name || dir->state == FcGCDirDisabled)
502             continue;
503         d = dir->name;
504         if (!d) 
505             continue;
506             
507         if (dir->metadata.count && !dir->ent) 
508         {
509             if (dir->state == FcGCDirUpdated || fd_orig < 0) 
510             {
511                 fprintf(stderr, "Invalid metadata entry for %s, skipping...\n", d);
512                 continue;
513             }
514             /* copy the old content */
515             dir->ent = malloc (dir->metadata.count);
516             if (!dir->ent) 
517             {
518                 perror("malloc error");
519                 continue;
520             }
521             off = FcCacheNextOffset (dir->offset + sizeof(FcCache));
522             if (lseek (fd_orig, off, SEEK_SET) != off) 
523             {
524                 perror("lseek");
525                 free(dir->ent);
526                 continue;
527             }
528             if (read (fd_orig, dir->ent, dir->metadata.count)
529                 != dir->metadata.count) 
530             {
531                 perror("read");
532                 free(dir->ent);
533                 continue;
534             }
535         }
536         
537         FcCacheWriteString (fd, d);
538
539         for (i = 0; i < dir->subdirs->size; i++)
540             FcCacheWriteString (fd, (char *)dir->subdirs->strs[i]);
541         FcCacheWriteString (fd, "");
542         
543         if (write (fd, &dir->metadata, sizeof(FcCache)) != sizeof(FcCache))
544         {
545             perror ("write metadata");
546             free (dir->ent);
547             continue;
548         }
549         off = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
550         if (lseek (fd, off, SEEK_SET) != off)
551         {
552             perror ("lseek");
553             free (dir->ent);
554             continue;
555         }
556         if (dir->metadata.count)
557         {
558             if (write (fd, dir->ent, dir->metadata.count) != dir->metadata.count)
559             {
560                 perror ("write dirent");
561                 free (dir->ent);
562                 continue;
563             }
564         }
565         free (dir->ent);
566     }
567     FcCacheWriteString (fd, "");
568
569     if (close (fd) == -1)
570         goto bail25;
571
572     close (fd_orig);
573     fd_orig = -1;
574     
575     if (!FcAtomicReplaceOrig (atomic))
576         goto bail25;
577     
578     FcAtomicUnlock (atomic);
579     FcAtomicDestroy (atomic);
580
581     cache->updated = FcFalse;
582     return FcTrue;
583
584  bail4:
585     free (header);
586  bail3:
587     if (fd_orig != -1)
588         close (fd_orig);
589
590     close (fd);
591  bail25:
592     FcAtomicDeleteNew (atomic);
593  bail2:
594     FcAtomicUnlock (atomic);
595  bail1:
596     FcAtomicDestroy (atomic);
597     return FcFalse;
598 }
599
600 /* 
601  * Find the next presumably-mmapable offset after the supplied file
602  * position.
603  */
604 static int
605 FcCacheNextOffset(off_t w)
606 {
607     static long pagesize = -1;
608
609     if (w == -1)
610         return w;
611
612     if (pagesize == -1)
613 #if defined (HAVE_SYSCONF)
614         pagesize = sysconf(_SC_PAGESIZE);
615 #else
616         pagesize = FC_HARDCODED_PAGESIZE;
617 #endif
618     if (w % pagesize == 0) 
619         return w;
620     else
621         return ((w / pagesize)+1)*pagesize;
622 }
623
624 /* return the address of the segment for the provided arch,
625  * or -1 if arch not found */
626 static off_t
627 FcCacheSkipToArch (int fd, const char * arch)
628 {
629     char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9];
630     char * candidate_arch;
631     off_t current_arch_start = 0;
632
633     lseek (fd, 0, SEEK_SET);
634     FcCacheSkipString (fd);
635     current_arch_start = lseek (fd, 0, SEEK_CUR);
636
637     /* skip arches that are not the current arch */
638     while (1)
639     {
640         long bs;
641
642         if (lseek (fd, current_arch_start, SEEK_SET) != current_arch_start)
643             return -1;
644
645         if (FcCacheReadString (fd, candidate_arch_machine_name_count, 
646                                 sizeof (candidate_arch_machine_name_count)) == 0)
647             return -1;
648         if (!strlen(candidate_arch_machine_name_count))
649             return -1;
650         bs = strtol(candidate_arch_machine_name_count, &candidate_arch, 16);
651
652         /* count = 0 should probably be distinguished from the !bs condition */
653         if (!bs || bs < strlen (candidate_arch_machine_name_count))
654             return -1;
655
656         candidate_arch++; /* skip leading space */
657
658         if (strcmp (candidate_arch, arch)==0)
659             return current_arch_start;
660         current_arch_start += bs;
661         current_arch_start = FcCacheNextOffset (current_arch_start);
662     }
663 }
664
665 /* Cuts out the segment at the file pointer (moves everything else
666  * down to cover it), and leaves the file pointer at the end of the
667  * file. */
668 static FcBool 
669 FcCacheCopyOld (int fd, int fd_orig, off_t start)
670 {
671     char * buf = malloc (8192);
672     char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
673     long bs;
674     int c, bytes_skipped;
675     off_t loc;
676
677     if (!buf)
678         return FcFalse;
679
680     loc = 0;
681     lseek (fd, 0, SEEK_SET); lseek (fd_orig, 0, SEEK_SET);
682     FcCacheSkipString (fd); FcCacheSkipString (fd_orig);
683     do
684     {
685         int b = 8192;
686         if (loc + b > start)
687             b = start - loc;
688
689         if ((c = read (fd_orig, buf, b)) <= 0)
690             break;
691         if (write (fd, buf, c) < 0)
692             goto bail;
693
694         loc += c;
695     }
696     while (c > 0);
697
698     lseek (fd, start, SEEK_SET);
699     if (FcCacheReadString (fd, candidate_arch_machine_name, 
700                            sizeof (candidate_arch_machine_name)) == 0)
701         goto done;
702     if (!strlen(candidate_arch_machine_name))
703         goto done;
704
705     bs = strtol(candidate_arch_machine_name, 0, 16);
706     if (bs == 0)
707         goto done;
708
709     bytes_skipped = 0;
710     do
711     {
712         lseek (fd, start+bs+bytes_skipped, SEEK_SET);
713         if ((c = read (fd, buf, 8192)) <= 0)
714             break;
715         lseek (fd, start+bytes_skipped, SEEK_SET);
716         if (write (fd, buf, c) < 0)
717             goto bail;
718         bytes_skipped += c;
719     }
720     while (c > 0);
721     lseek (fd, start+bytes_skipped, SEEK_SET);
722
723  done:
724     free (buf);
725     return FcTrue;
726
727  bail:
728     free (buf);
729     return FcFalse;
730 }
731
732 /* Does not check that the cache has the appropriate arch section. */
733 FcBool
734 FcDirCacheValid (const FcChar8 *dir, FcConfig *config)
735 {
736     int         fd;
737
738     fd = FcDirCacheOpen (config, dir, NULL);
739
740     if (fd < 0)
741         return FcFalse;
742     close (fd);
743
744     return FcTrue;
745 }
746
747 /* Assumes that the cache file in 'dir' exists.
748  * Checks that the cache has the appropriate arch section. */
749 FcBool
750 FcDirCacheHasCurrentArch (const FcChar8 *dir, FcConfig *config)
751 {
752     int         fd;
753     off_t       current_arch_start;
754     char        *current_arch_machine_name;
755     FcCache     metadata;
756     char        subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
757
758     fd = FcDirCacheOpen (config, dir, NULL);
759     if (fd < 0)
760         goto bail;
761
762     current_arch_machine_name = FcCacheMachineSignature();
763     current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
764
765     if (current_arch_start >= 0)
766     {
767         if (lseek (fd, current_arch_start, SEEK_SET) != current_arch_start)
768             goto bail1;
769
770         FcCacheSkipString (fd);
771
772         while (FcCacheReadString (fd, subdirName, sizeof (subdirName)) && strlen (subdirName) > 0)
773             ;
774
775         if (read(fd, &metadata, sizeof(FcCache)) != sizeof(FcCache))
776             goto bail1;
777
778         if (metadata.magic != FC_CACHE_MAGIC)
779             goto bail1;
780     }
781
782     close (fd);
783
784     if (current_arch_start < 0)
785         return FcFalse;
786     
787     return FcTrue;
788
789  bail1:
790     close (fd);
791  bail:
792     return FcFalse;
793 }
794
795 #define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
796
797 static const char bin2hex[] = { '0', '1', '2', '3',
798                                 '4', '5', '6', '7',
799                                 '8', '9', 'a', 'b',
800                                 'c', 'd', 'e', 'f' };
801
802 static FcChar8 *
803 FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
804 {
805     unsigned char       hash[16];
806     FcChar8             *hex_hash;
807     int                 cnt;
808     struct MD5Context   ctx;
809
810     MD5Init (&ctx);
811     MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir));
812
813     MD5Final (hash, &ctx);
814
815     cache_base[0] = '/';
816     hex_hash = cache_base + 1;
817     for (cnt = 0; cnt < 16; ++cnt)
818     {
819         hex_hash[2*cnt  ] = bin2hex[hash[cnt] >> 4];
820         hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
821     }
822     hex_hash[2*cnt] = 0;
823     strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
824
825     return cache_base;
826 }
827
828 FcBool
829 FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
830 {
831     int         fd = -1;
832     FcChar8     *cache_hashed = NULL;
833     FcChar8     cache_base[CACHEBASE_LEN];
834     FcStrList   *list;
835     FcChar8     *cache_dir;
836     char        dir_buf[FC_MAX_FILE_LEN];
837
838     FcDirCacheBasename (dir, cache_base);
839
840     list = FcStrListCreate (config->cacheDirs);
841     if (!list)
842         return FcFalse;
843         
844     while ((cache_dir = FcStrListNext (list)))
845     {
846         cache_hashed = FcStrPlus (cache_dir, cache_base);
847         if (!cache_hashed)
848             break;
849         fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
850         FcStrFree (cache_hashed);
851         if (fd >= 0) 
852         {
853             if (FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) &&
854                 strcmp (dir_buf, (char *) dir) == 0)
855             {
856                 close (fd);
857                 if (unlink ((char *) cache_hashed) < 0)
858                     break;
859             } else
860                 close (fd);
861         }
862     }
863     FcStrListDone (list);
864     /* return FcFalse if something went wrong */
865     if (cache_dir)
866         return FcFalse;
867     return FcTrue;
868 }
869
870 static int
871 FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache, 
872                  FcStrList *list, FcFontSet * set, FcStrSet *processed_dirs)
873 {
874     int                 ret = 0;
875     FcChar8             *dir;
876     FcStrSet            *subdirs;
877     FcStrList           *sublist;
878     FcGlobalCacheDir   *d;
879
880     /*
881      * Read in the results from 'list'.
882      */
883     while ((dir = FcStrListNext (list)))
884     {
885         if (!FcConfigAcceptFilename (config, dir))
886             continue;
887
888         /* Skip this directory if already updated
889          * to avoid the looped directories via symlinks
890          * Clearly a dir not in fonts.conf shouldn't be globally cached.
891          */
892
893         if (FcStrSetMember (processed_dirs, dir))
894             continue;
895         if (!FcStrSetAdd (processed_dirs, dir))
896             continue;
897
898         subdirs = FcStrSetCreate ();
899         if (!subdirs)
900         {
901             fprintf (stderr, "Can't create directory set\n");
902             ret++;
903             continue;
904         }
905         
906         if (FcDirCacheRead (set, subdirs, dir, config))
907         {
908             /* if an old entry is found in the global cache, disable it */
909             if ((d = FcGlobalCacheDirFind (cache, (const char *)dir)) != NULL)
910             {
911                 d->state = FcGCDirDisabled;
912                 /* save the updated config later without this entry */
913                 cache->updated = FcTrue;
914             }
915         }
916         else
917         {
918             if (FcDebug () & FC_DBG_FONTSET)
919                 printf ("cache scan dir %s\n", dir);
920
921             FcDirScanConfig (set, subdirs, cache, 
922                              config->blanks, dir, FcFalse, config);
923         }
924         sublist = FcStrListCreate (subdirs);
925         FcStrSetDestroy (subdirs);
926         if (!sublist)
927         {
928             fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
929             ret++;
930             continue;
931         }
932         ret += FcCacheReadDirs (config, cache, sublist, set, processed_dirs);
933     }
934     FcStrListDone (list);
935     return ret;
936 }
937
938 FcFontSet *
939 FcCacheRead (FcConfig *config, FcGlobalCache * cache)
940 {
941     FcFontSet   *s = FcFontSetCreate();
942     FcStrSet    *processed_dirs;
943
944     if (!s) 
945         return 0;
946
947     processed_dirs = FcStrSetCreate();
948     if (!processed_dirs)
949         goto bail;
950
951     if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s, processed_dirs))
952         goto bail1;
953
954     FcStrSetDestroy (processed_dirs);
955     return s;
956
957  bail1:
958     FcStrSetDestroy (processed_dirs);
959  bail:
960     FcFontSetDestroy (s);
961     return 0;
962 }
963
964 /* Opens the cache file for 'dir' for reading.
965  * This searches the list of cache dirs for the relevant cache file,
966  * returning the first one found.
967  */
968 static int
969 FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path)
970 {
971     int         fd = -1;
972     FcChar8     *cache_hashed = NULL;
973     FcChar8     cache_base[CACHEBASE_LEN];
974     FcStrList   *list;
975     FcChar8     *cache_dir;
976     char        dir_buf[FC_MAX_FILE_LEN];
977     struct stat file_stat, dir_stat;
978
979     if (stat ((char *) dir, &dir_stat) < 0)
980         return -1;
981
982     FcDirCacheBasename (dir, cache_base);
983
984     list = FcStrListCreate (config->cacheDirs);
985     if (!list)
986         return -1;
987         
988     while ((cache_dir = FcStrListNext (list)))
989     {
990         cache_hashed = FcStrPlus (cache_dir, cache_base);
991         if (!cache_hashed)
992             break;
993         fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
994         if (fd >= 0) {
995             if (fstat (fd, &file_stat) >= 0 &&
996                 dir_stat.st_mtime <= file_stat.st_mtime)
997             {
998                 break;
999             }
1000             close (fd);
1001             fd = -1;
1002         }
1003         FcStrFree (cache_hashed);
1004         cache_hashed = NULL;
1005     }
1006     FcStrListDone (list);
1007
1008     if (fd < 0)
1009         return -1;
1010     
1011     if (!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) ||
1012         strcmp (dir_buf, (char *) dir) != 0)
1013     {
1014         close (fd);
1015         FcStrFree (cache_hashed);
1016         return -1;
1017     }
1018     
1019     if (cache_path)
1020         *cache_path = cache_hashed;
1021     else
1022         FcStrFree (cache_hashed);
1023
1024     return fd;
1025 }
1026
1027 /* read serialized state from the cache file */
1028 FcBool
1029 FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir, FcConfig *config)
1030 {
1031     int         fd;
1032     char        *current_arch_machine_name;
1033     char        candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
1034     off_t       current_arch_start = 0;
1035     char        subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
1036
1037     fd = FcDirCacheOpen (config, dir, NULL);
1038     if (fd < 0)
1039         goto bail;
1040
1041     current_arch_machine_name = FcCacheMachineSignature();
1042     current_arch_start = FcCacheSkipToArch(fd, 
1043                                            current_arch_machine_name);
1044     if (current_arch_start < 0)
1045         goto bail1;
1046
1047     lseek (fd, current_arch_start, SEEK_SET);
1048     if (FcCacheReadString (fd, candidate_arch_machine_name, 
1049                            sizeof (candidate_arch_machine_name)) == 0)
1050         goto bail1;
1051
1052     while (FcCacheReadString (fd, subdirName, sizeof (subdirName)) && strlen (subdirName) > 0)
1053         FcStrSetAdd (dirs, (FcChar8 *)subdirName);
1054
1055     if (!FcDirCacheConsume (fd, (const char *)dir, set, config))
1056         goto bail1;
1057         
1058     close(fd);
1059     return FcTrue;
1060
1061  bail1:
1062     close (fd);
1063  bail:
1064     return FcFalse;
1065 }
1066
1067 static FcBool
1068 FcDirCacheConsume (int fd, const char * dir, FcFontSet *set, FcConfig *config)
1069 {
1070     FcCache metadata;
1071     void * current_dir_block;
1072     off_t pos, endpos;
1073
1074     if (read(fd, &metadata, sizeof(FcCache)) != sizeof(FcCache))
1075         return FcFalse;
1076     if (metadata.magic != FC_CACHE_MAGIC)
1077         return FcFalse;
1078
1079     if (!metadata.count)
1080     {
1081         pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
1082         lseek (fd, pos, SEEK_SET);
1083         if (config)
1084             FcConfigAddFontDir (config, (FcChar8 *)dir);
1085         return FcTrue;
1086     }
1087
1088     pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
1089
1090     /* This is not failsafe (multi-arches can break it),
1091      * but fd has got to have at least as many bytes as
1092      * metadata.count, or something's going to go horribly wrong. */
1093     if (pos == (off_t)-1)
1094         return FcFalse;
1095
1096     endpos = lseek (fd, 0, SEEK_END);
1097     if (endpos == (off_t)-1 || endpos - pos < metadata.count)
1098         return FcFalse;
1099     if (lseek (fd, pos, SEEK_SET) == -1)
1100         return FcFalse;
1101
1102 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
1103     current_dir_block = mmap (0, metadata.count, 
1104                               PROT_READ, MAP_SHARED, fd, pos);
1105     if (current_dir_block == MAP_FAILED)
1106         return FcFalse;
1107 #elif defined(_WIN32)
1108         {
1109                 HANDLE hFileMap;
1110
1111                 hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL, PAGE_READONLY, 0, 0, NULL);
1112                 if (hFileMap == NULL)
1113                         return FcFalse;
1114
1115                 current_dir_block = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, metadata.count + pos);
1116                 if (current_dir_block == NULL)
1117                 {
1118                         CloseHandle (hFileMap);
1119                         return FcFalse;
1120                 }
1121
1122                 current_dir_block = (void *)((char *)current_dir_block + pos);
1123         }
1124 #else
1125     current_dir_block = malloc (metadata.count);
1126     if (!current_dir_block)
1127         return FcFalse;
1128
1129     /* could also use CreateMappedViewOfFile under MinGW... */
1130     if (read (fd, current_dir_block, metadata.count) != metadata.count)
1131         goto bail;
1132 #endif
1133     lseek (fd, pos+metadata.count, SEEK_SET);
1134
1135     FcCacheAddBankDir (metadata.bank, dir);
1136     if (config)
1137         FcConfigAddFontDir (config, (FcChar8 *)dir);
1138
1139     if (!FcFontSetUnserialize (&metadata, set, current_dir_block))
1140         goto bail;
1141
1142     return FcTrue;
1143  bail:
1144 #if !(defined(HAVE_MMAP) || defined(__CYGWIN__))
1145     free (current_dir_block);
1146 #endif
1147     return FcFalse;
1148 }
1149
1150 static void *
1151 FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
1152 {
1153     void * current_dir_block, * final_dir_block;
1154     static unsigned int rand_state = 0;
1155     int bank, needed_bytes_no_align;
1156
1157 #if defined (HAVE_RAND_R)
1158     if (!rand_state) 
1159         rand_state = time(0L);
1160     bank = rand_r(&rand_state);
1161
1162     while (FcCacheHaveBank(bank))
1163         bank = rand_r(&rand_state);
1164 #else
1165     if (!rand_state)
1166     {
1167         rand_state = 1;
1168         srand (time (0L));
1169     }
1170     bank = rand();
1171
1172     while (FcCacheHaveBank(bank))
1173         bank = rand();
1174 #endif
1175
1176     memset (metadata, 0, sizeof(FcCache));
1177     FcFontSetNewBank();
1178     needed_bytes_no_align = FcFontSetNeededBytes (set);
1179     metadata->count = needed_bytes_no_align + 
1180         FcFontSetNeededBytesAlign ();
1181     metadata->magic = FC_CACHE_MAGIC;
1182     metadata->bank = bank;
1183
1184     if (!needed_bytes_no_align) /* not a failure, no fonts to write */
1185     {
1186         /* no, you don't really need to write any bytes at all. */
1187         metadata->count = 0;
1188         return 0;
1189     }
1190
1191     current_dir_block = malloc (metadata->count);
1192     if (!current_dir_block)
1193         goto bail;
1194     /* shut up valgrind */
1195     memset (current_dir_block, 0, metadata->count);
1196     final_dir_block = FcFontSetDistributeBytes (metadata, current_dir_block);
1197
1198     if ((void *)((char *)current_dir_block+metadata->count) < final_dir_block)
1199         goto bail;
1200                               
1201     if (!FcFontSetSerialize (bank, set))
1202         goto bail;
1203
1204     return current_dir_block;
1205
1206  bail:
1207     free (current_dir_block);
1208     return 0;
1209 }
1210
1211 static FcBool
1212 FcMakeDirectory (const FcChar8 *dir)
1213 {
1214     FcChar8 *parent;
1215     FcBool  ret;
1216     
1217     if (strlen ((char *) dir) == 0)
1218         return FcFalse;
1219     
1220     parent = FcStrDirname (dir);
1221     if (!parent)
1222         return FcFalse;
1223     if (access ((char *) parent, W_OK|X_OK) == 0)
1224         ret = mkdir ((char *) dir, 0777) == 0;
1225     else if (access ((char *) parent, F_OK) == -1)
1226         ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0777) == 0);
1227     else
1228         ret = FcFalse;
1229     FcStrFree (parent);
1230     return ret;
1231 }
1232
1233 /* write serialized state to the cache file */
1234 FcBool
1235 FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config)
1236 {
1237     FcChar8         cache_base[CACHEBASE_LEN];
1238     FcChar8         *cache_hashed;
1239     int             fd, fd_orig, i, dirs_count;
1240     FcAtomic        *atomic;
1241     FcCache         metadata;
1242     off_t           current_arch_start = 0, truncate_to;
1243     FcStrList       *list;
1244     char            *current_arch_machine_name, * header;
1245     void            *current_dir_block = 0;
1246     FcChar8         *cache_dir = NULL;
1247     FcChar8         *test_dir;
1248
1249     /*
1250      * Write it to the first directory in the list which is writable
1251      */
1252     
1253     list = FcStrListCreate (config->cacheDirs);
1254     if (!list)
1255         return FcFalse;
1256     while ((test_dir = FcStrListNext (list))) {
1257         if (access ((char *) test_dir, W_OK|X_OK) == 0)
1258         {
1259             cache_dir = test_dir;
1260             break;
1261         }
1262         else
1263         {
1264             /*
1265              * If the directory doesn't exist, try to create it
1266              */
1267             if (access ((char *) test_dir, F_OK) == -1) {
1268                 if (FcMakeDirectory (test_dir))
1269                 {
1270                     cache_dir = test_dir;
1271                     break;
1272                 }
1273             }
1274         }
1275     }
1276     FcStrListDone (list);
1277     if (!cache_dir)
1278         return FcFalse;
1279     FcDirCacheBasename (dir, cache_base);
1280     cache_hashed = FcStrPlus (cache_dir, cache_base);
1281     if (!cache_hashed)
1282         return FcFalse;
1283
1284     current_dir_block = FcDirCacheProduce (set, &metadata);
1285
1286     if (metadata.count && !current_dir_block)
1287         goto bail1;
1288
1289     if (FcDebug () & FC_DBG_CACHE)
1290         printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
1291                 dir, cache_hashed);
1292
1293     atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
1294     if (!atomic)
1295         goto bail1;
1296
1297     if (!FcAtomicLock (atomic))
1298         goto bail2;
1299
1300     /* open the original file to save relevant portions */
1301     fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY | O_BINARY);
1302
1303     fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
1304     if (fd == -1)
1305         goto bail3;
1306
1307     FcCacheWriteString (fd, (char *) dir);
1308
1309     current_arch_machine_name = FcCacheMachineSignature ();
1310     current_arch_start = 0;
1311
1312     if (fd_orig != -1)
1313         current_arch_start = 
1314             FcCacheSkipToArch(fd_orig, current_arch_machine_name);
1315
1316     if (current_arch_start < 0)
1317     {
1318         off_t offset = lseek(fd_orig, 0, SEEK_END);
1319         current_arch_start = FcCacheNextOffset (offset);
1320     }
1321
1322     if (fd_orig != -1 && !FcCacheCopyOld(fd, fd_orig, current_arch_start))
1323         goto bail4;
1324
1325     if (fd_orig != -1)
1326         close (fd_orig);
1327
1328     current_arch_start = lseek(fd, 0, SEEK_CUR);
1329 #if defined (HAVE_FTRUNCATE)
1330     if (ftruncate (fd, current_arch_start) == -1)
1331         goto bail4;
1332 #else
1333 #if defined (HAVE_CHSIZE)
1334     if (chsize (fd, current_arch_start) == -1)
1335         goto bail4;
1336 #else
1337     goto bail4;
1338 #endif
1339 #endif
1340
1341     /* allocate space for subdir names in this block */
1342     dirs_count = 0;
1343     for (i = 0; i < dirs->size; i++)
1344         dirs_count += strlen((char *)dirs->strs[i]) + 1;
1345     dirs_count ++;
1346
1347     /* now write the address of the next offset */
1348     truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + sizeof (FcCache) + dirs_count) + metadata.count) - current_arch_start;
1349     header = malloc (10 + strlen (current_arch_machine_name));
1350     if (!header)
1351         goto bail4;
1352     sprintf (header, "%8x ", (int)truncate_to);
1353     strcat (header, current_arch_machine_name);
1354     if (!FcCacheWriteString (fd, header))
1355         goto bail5;
1356
1357     for (i = 0; i < dirs->size; i++)
1358         FcCacheWriteString (fd, (char *)dirs->strs[i]);
1359     FcCacheWriteString (fd, "");
1360
1361     if (write (fd, &metadata, sizeof(FcCache)) != sizeof(FcCache)) 
1362     {
1363         perror("write metadata");
1364         goto bail5;
1365     }
1366     if (metadata.count)
1367     {
1368         off_t off = FcCacheNextOffset (lseek(fd, 0, SEEK_END));
1369         if (lseek (fd, off, SEEK_SET) != off)
1370             perror("lseek");
1371         else if (write (fd, current_dir_block, metadata.count) !=
1372                  metadata.count)
1373             perror("write current_dir_block");
1374         free (current_dir_block);
1375         current_dir_block = 0;
1376     }
1377
1378     /* this actually serves to pad out the cache file, if needed */
1379 #if defined (HAVE_FTRUNCATE)
1380     if (ftruncate (fd, current_arch_start + truncate_to) == -1)
1381         goto bail5;
1382 #else
1383 #if defined (HAVE_CHSIZE)
1384     if (chsize (fd, current_arch_start + truncate_to) == -1)
1385         goto bail5;
1386 #else
1387     goto bail5;
1388 #endif
1389 #endif
1390
1391     free (header);
1392     close(fd);
1393     if (!FcAtomicReplaceOrig(atomic))
1394         goto bail3;
1395     FcStrFree ((FcChar8 *)cache_hashed);
1396     FcAtomicUnlock (atomic);
1397     FcAtomicDestroy (atomic);
1398     return FcTrue;
1399
1400  bail5:
1401     free (header);
1402  bail4:
1403     close (fd);
1404  bail3:
1405     FcAtomicUnlock (atomic);
1406  bail2:
1407     FcAtomicDestroy (atomic);
1408  bail1:
1409     FcStrFree ((FcChar8 *)cache_hashed);
1410     if (current_dir_block)
1411         free (current_dir_block);
1412     return FcFalse;
1413 }
1414
1415 static char *
1416 FcCacheMachineSignature ()
1417 {
1418     static char buf[MACHINE_SIGNATURE_SIZE];
1419     int32_t magic = ENDIAN_TEST;
1420     char * m = (char *)&magic;
1421
1422     sprintf (buf, "%2x%2x%2x%2x "
1423              "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x "
1424              "%4x %4x %4x %4x %4x %4x %4x %4x\n", 
1425              m[0], m[1], m[2], m[3],
1426              (unsigned int)sizeof (char),
1427              (unsigned int)sizeof (char *),
1428              (unsigned int)sizeof (int),
1429              (unsigned int)sizeof (FcPattern),
1430              (unsigned int)sizeof (FcPatternEltPtr),
1431              (unsigned int)sizeof (struct _FcPatternElt *),
1432              (unsigned int)sizeof (FcPatternElt),
1433              (unsigned int)sizeof (FcObjectPtr),
1434              (unsigned int)sizeof (FcValueListPtr),
1435              (unsigned int)sizeof (FcValue),
1436              (unsigned int)sizeof (FcValueBinding),
1437              (unsigned int)sizeof (struct _FcValueList *),
1438              (unsigned int)sizeof (FcCharSet),
1439              (unsigned int)sizeof (FcCharLeaf **),
1440              (unsigned int)sizeof (FcChar16 *),
1441              (unsigned int)sizeof (FcChar16),
1442              (unsigned int)sizeof (FcCharLeaf),
1443              (unsigned int)sizeof (FcChar32),
1444              (unsigned int)sizeof (FcCache),
1445 #if defined (HAVE_SYSCONF)
1446              (unsigned int)sysconf(_SC_PAGESIZE));
1447 #else
1448              (unsigned int)FC_HARDCODED_PAGESIZE);
1449 #endif
1450
1451     return buf;
1452 }
1453
1454 static int banks_ptr = 0, banks_alloc = 0;
1455 int * _fcBankId = 0, * _fcBankIdx = 0;
1456 static const char ** bankDirs = 0;
1457
1458 static FcBool
1459 FcCacheHaveBank (int bank)
1460 {
1461     int i;
1462
1463     if (bank < FC_BANK_FIRST)
1464         return FcTrue;
1465
1466     for (i = 0; i < banks_ptr; i++)
1467         if (_fcBankId[i] == bank)
1468             return FcTrue;
1469
1470     return FcFalse;
1471 }
1472
1473 int
1474 FcCacheBankToIndexMTF (int bank)
1475 {
1476     int i, j;
1477
1478     for (i = 0; i < banks_ptr; i++)
1479         if (_fcBankId[_fcBankIdx[i]] == bank)
1480         {
1481             int t = _fcBankIdx[i];
1482
1483             for (j = i; j > 0; j--)
1484                 _fcBankIdx[j] = _fcBankIdx[j-1];
1485             _fcBankIdx[0] = t;
1486             return t;
1487         }
1488
1489     if (banks_ptr >= banks_alloc)
1490     {
1491         int * b, * bidx;
1492         const char ** bds;
1493
1494         b = realloc (_fcBankId, (banks_alloc + 4) * sizeof(int));
1495         if (!b)
1496             return -1;
1497         _fcBankId = b;
1498
1499         bidx = realloc (_fcBankIdx, (banks_alloc + 4) * sizeof(int));
1500         if (!bidx)
1501             return -1;
1502         _fcBankIdx = bidx;
1503
1504         bds = realloc (bankDirs, (banks_alloc + 4) * sizeof (char *));
1505         if (!bds)
1506             return -1;
1507         bankDirs = bds;
1508
1509         banks_alloc += 4;
1510     }
1511
1512     i = banks_ptr++;
1513     _fcBankId[i] = bank;
1514     _fcBankIdx[i] = i;
1515     return i;
1516 }
1517
1518 static void
1519 FcCacheAddBankDir (int bank, const char * dir)
1520 {
1521     int bi = FcCacheBankToIndexMTF (bank);
1522
1523     if (bi < 0)
1524         return;
1525
1526     bankDirs[bi] = (const char *)FcStrCopy ((FcChar8 *)dir);
1527 }
1528
1529 const char *
1530 FcCacheFindBankDir (int bank)
1531 {
1532     int bi = FcCacheBankToIndex (bank);
1533     return bankDirs[bi];
1534 }
1535
1536 /*
1537  * This code implements the MD5 message-digest algorithm.
1538  * The algorithm is due to Ron Rivest.  This code was
1539  * written by Colin Plumb in 1993, no copyright is claimed.
1540  * This code is in the public domain; do with it what you wish.
1541  *
1542  * Equivalent code is available from RSA Data Security, Inc.
1543  * This code has been tested against that, and is equivalent,
1544  * except that you don't need to include two pages of legalese
1545  * with every copy.
1546  *
1547  * To compute the message digest of a chunk of bytes, declare an
1548  * MD5Context structure, pass it to MD5Init, call MD5Update as
1549  * needed on buffers full of bytes, and then call MD5Final, which
1550  * will fill a supplied 16-byte array with the digest.
1551  */
1552
1553 #ifndef HIGHFIRST
1554 #define byteReverse(buf, len)   /* Nothing */
1555 #else
1556 /*
1557  * Note: this code is harmless on little-endian machines.
1558  */
1559 void byteReverse(unsigned char *buf, unsigned longs)
1560 {
1561     FcChar32 t;
1562     do {
1563         t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
1564             ((unsigned) buf[1] << 8 | buf[0]);
1565         *(FcChar32 *) buf = t;
1566         buf += 4;
1567     } while (--longs);
1568 }
1569 #endif
1570
1571 /*
1572  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
1573  * initialization constants.
1574  */
1575 static void MD5Init(struct MD5Context *ctx)
1576 {
1577     ctx->buf[0] = 0x67452301;
1578     ctx->buf[1] = 0xefcdab89;
1579     ctx->buf[2] = 0x98badcfe;
1580     ctx->buf[3] = 0x10325476;
1581
1582     ctx->bits[0] = 0;
1583     ctx->bits[1] = 0;
1584 }
1585
1586 /*
1587  * Update context to reflect the concatenation of another buffer full
1588  * of bytes.
1589  */
1590 static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len)
1591 {
1592     FcChar32 t;
1593
1594     /* Update bitcount */
1595
1596     t = ctx->bits[0];
1597     if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t)
1598         ctx->bits[1]++;         /* Carry from low to high */
1599     ctx->bits[1] += len >> 29;
1600
1601     t = (t >> 3) & 0x3f;        /* Bytes already in shsInfo->data */
1602
1603     /* Handle any leading odd-sized chunks */
1604
1605     if (t) {
1606         unsigned char *p = (unsigned char *) ctx->in + t;
1607
1608         t = 64 - t;
1609         if (len < t) {
1610             memcpy(p, buf, len);
1611             return;
1612         }
1613         memcpy(p, buf, t);
1614         byteReverse(ctx->in, 16);
1615         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1616         buf += t;
1617         len -= t;
1618     }
1619     /* Process data in 64-byte chunks */
1620
1621     while (len >= 64) {
1622         memcpy(ctx->in, buf, 64);
1623         byteReverse(ctx->in, 16);
1624         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1625         buf += 64;
1626         len -= 64;
1627     }
1628
1629     /* Handle any remaining bytes of data. */
1630
1631     memcpy(ctx->in, buf, len);
1632 }
1633
1634 /*
1635  * Final wrapup - pad to 64-byte boundary with the bit pattern 
1636  * 1 0* (64-bit count of bits processed, MSB-first)
1637  */
1638 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
1639 {
1640     unsigned count;
1641     unsigned char *p;
1642
1643     /* Compute number of bytes mod 64 */
1644     count = (ctx->bits[0] >> 3) & 0x3F;
1645
1646     /* Set the first char of padding to 0x80.  This is safe since there is
1647        always at least one byte free */
1648     p = ctx->in + count;
1649     *p++ = 0x80;
1650
1651     /* Bytes of padding needed to make 64 bytes */
1652     count = 64 - 1 - count;
1653
1654     /* Pad out to 56 mod 64 */
1655     if (count < 8) {
1656         /* Two lots of padding:  Pad the first block to 64 bytes */
1657         memset(p, 0, count);
1658         byteReverse(ctx->in, 16);
1659         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1660
1661         /* Now fill the next block with 56 bytes */
1662         memset(ctx->in, 0, 56);
1663     } else {
1664         /* Pad block to 56 bytes */
1665         memset(p, 0, count - 8);
1666     }
1667     byteReverse(ctx->in, 14);
1668
1669     /* Append length in bits and transform */
1670     ((FcChar32 *) ctx->in)[14] = ctx->bits[0];
1671     ((FcChar32 *) ctx->in)[15] = ctx->bits[1];
1672
1673     MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1674     byteReverse((unsigned char *) ctx->buf, 4);
1675     memcpy(digest, ctx->buf, 16);
1676     memset(ctx, 0, sizeof(ctx));        /* In case it's sensitive */
1677 }
1678
1679
1680 /* The four core functions - F1 is optimized somewhat */
1681
1682 /* #define F1(x, y, z) (x & y | ~x & z) */
1683 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1684 #define F2(x, y, z) F1(z, x, y)
1685 #define F3(x, y, z) (x ^ y ^ z)
1686 #define F4(x, y, z) (y ^ (x | ~z))
1687
1688 /* This is the central step in the MD5 algorithm. */
1689 #define MD5STEP(f, w, x, y, z, data, s) \
1690         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
1691
1692 /*
1693  * The core of the MD5 algorithm, this alters an existing MD5 hash to
1694  * reflect the addition of 16 longwords of new data.  MD5Update blocks
1695  * the data and converts bytes into longwords for this routine.
1696  */
1697 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16])
1698 {
1699     register FcChar32 a, b, c, d;
1700
1701     a = buf[0];
1702     b = buf[1];
1703     c = buf[2];
1704     d = buf[3];
1705
1706     MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
1707     MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
1708     MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
1709     MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
1710     MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
1711     MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
1712     MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
1713     MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
1714     MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
1715     MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
1716     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
1717     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
1718     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
1719     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
1720     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
1721     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
1722
1723     MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
1724     MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
1725     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
1726     MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
1727     MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
1728     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
1729     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
1730     MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
1731     MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
1732     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
1733     MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
1734     MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
1735     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
1736     MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1737     MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1738     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1739
1740     MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1741     MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1742     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1743     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1744     MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1745     MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1746     MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1747     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1748     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1749     MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1750     MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1751     MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1752     MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1753     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1754     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1755     MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1756
1757     MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1758     MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1759     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1760     MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1761     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1762     MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1763     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1764     MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1765     MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1766     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1767     MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1768     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1769     MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1770     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1771     MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1772     MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1773
1774     buf[0] += a;
1775     buf[1] += b;
1776     buf[2] += c;
1777     buf[3] += d;
1778 }