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