Don't add current_arch_start more than once.
[platform/upstream/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         /* freed below */
628         file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
629         if (!file)
630             return FcFalse;
631
632         strcpy ((char *) file, (char *) dir);
633         strcat ((char *) file, "/");
634         base = file + strlen ((char *) file);
635
636         subdirs = FcStrSetCreate ();
637         if (!subdirs)
638         {
639             fprintf (stderr, "Can't create directory set\n");
640             ret++;
641             free (file);
642             continue;
643         }
644         
645         if (access ((char *) dir, X_OK) < 0)
646         {
647             switch (errno) {
648             case ENOENT:
649             case ENOTDIR:
650             case EACCES:
651                 break;
652             default:
653                 fprintf (stderr, "\"%s\": ", dir);
654                 perror ("");
655                 ret++;
656             }
657             FcStrSetDestroy (subdirs);
658             free (file);
659             continue;
660         }
661         if (stat ((char *) dir, &statb) == -1)
662         {
663             fprintf (stderr, "\"%s\": ", dir);
664             perror ("");
665             FcStrSetDestroy (subdirs);
666             ret++;
667             free (file);
668             continue;
669         }
670         if (!S_ISDIR (statb.st_mode))
671         {
672             fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
673             FcStrSetDestroy (subdirs);
674             free (file);
675             continue;
676         }
677         if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, subdirs, dir))
678         {
679             if (FcDebug () & FC_DBG_FONTSET)
680                 printf ("cache scan dir %s\n", dir);
681
682             FcDirScanConfig (set, subdirs, cache, 
683                              config->blanks, dir, FcFalse, config);
684         }
685         sublist = FcStrListCreate (subdirs);
686         FcStrSetDestroy (subdirs);
687         if (!sublist)
688         {
689             fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
690             ret++;
691             free (file);
692             continue;
693         }
694         ret += FcCacheReadDirs (config, cache, sublist, set);
695         free (file);
696     }
697     FcStrListDone (list);
698     return ret;
699 }
700
701 FcFontSet *
702 FcCacheRead (FcConfig *config, FcGlobalCache * cache)
703 {
704     FcFontSet * s = FcFontSetCreate();
705     if (!s) 
706         return 0;
707
708     if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s))
709         goto bail;
710
711     return s;
712
713  bail:
714     FcFontSetDestroy (s);
715     return 0;
716 }
717
718 /* read serialized state from the cache file */
719 FcBool
720 FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir)
721 {
722     char *cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
723     int fd;
724     char * current_arch_machine_name;
725     char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
726     off_t current_arch_start = 0;
727     char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
728
729     if (!cache_file)
730         goto bail;
731
732     current_arch_machine_name = FcCacheMachineSignature();
733     fd = open(cache_file, O_RDONLY);
734     if (fd == -1)
735         goto bail;
736
737     current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
738     if (current_arch_start < 0)
739         goto bail1;
740
741     lseek (fd, current_arch_start, SEEK_SET);
742     if (FcCacheReadString (fd, candidate_arch_machine_name, 
743                            sizeof (candidate_arch_machine_name)) == 0)
744         goto bail1;
745
746     while (strlen(FcCacheReadString (fd, subdirName, sizeof (subdirName))) > 0)
747         FcStrSetAdd (dirs, (FcChar8 *)subdirName);
748
749     if (!FcDirCacheConsume (fd, (const char *)dir, set))
750         goto bail1;
751         
752     close(fd);
753     free (cache_file);
754     return FcTrue;
755
756  bail1:
757     close (fd);
758  bail:
759     free (cache_file);
760     return FcFalse;
761 }
762
763 static FcBool
764 FcDirCacheConsume (int fd, const char * dir, FcFontSet *set)
765 {
766     FcCache metadata;
767     void * current_dir_block;
768     off_t pos;
769
770     read(fd, &metadata, sizeof(FcCache));
771     if (metadata.magic != FC_CACHE_MAGIC)
772         return FcFalse;
773
774     if (!metadata.count)
775         return FcTrue;
776
777     pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
778     current_dir_block = mmap (0, metadata.count, 
779                               PROT_READ, MAP_SHARED, fd, pos);
780     if (current_dir_block == MAP_FAILED)
781         return FcFalse;
782     
783     if (!FcFontSetUnserialize (metadata, set, current_dir_block))
784         return FcFalse;
785
786     FcCacheAddBankDir (metadata.bank, dir);
787
788     return FcTrue;
789 }
790
791 static void *
792 FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
793 {
794     void * current_dir_block, * final_dir_block;
795     static unsigned int rand_state = 0;
796     int bank, needed_bytes_no_align;
797
798     if (!rand_state) 
799         rand_state = time(0L);
800     bank = rand_r(&rand_state);
801
802     while (FcCacheHaveBank(bank))
803         bank = rand_r(&rand_state);
804
805     memset (metadata, 0, sizeof(FcCache));
806     FcFontSetNewBank();
807     needed_bytes_no_align = FcFontSetNeededBytes (set);
808     metadata->count = needed_bytes_no_align + 
809         FcFontSetNeededBytesAlign ();
810     metadata->magic = FC_CACHE_MAGIC;
811     metadata->bank = bank;
812
813     if (!needed_bytes_no_align) /* not a failure, no fonts to write */
814     {
815         /* no, you don't really need to write any bytes at all. */
816         metadata->count = 0;
817         return 0;
818     }
819
820     current_dir_block = malloc (metadata->count);
821     if (!current_dir_block)
822         goto bail;
823     // shut up valgrind
824     memset (current_dir_block, 0, metadata->count);
825     final_dir_block = FcFontSetDistributeBytes (metadata, current_dir_block);
826
827     if ((void *)((char *)current_dir_block+metadata->count) < final_dir_block)
828         goto bail;
829                               
830     if (!FcFontSetSerialize (bank, set))
831         goto bail;
832
833     return current_dir_block;
834
835  bail:
836     free (current_dir_block);
837     return 0;
838 }
839
840 /* write serialized state to the cache file */
841 FcBool
842 FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
843 {
844     FcChar8         *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
845     int             fd, fd_orig, i, dirs_count;
846     FcAtomic        *atomic;
847     FcCache         metadata;
848     off_t           current_arch_start = 0, truncate_to;
849
850     char            *current_arch_machine_name, * header;
851     void            *current_dir_block;
852
853     if (!cache_file)
854         goto bail;
855
856     current_dir_block = FcDirCacheProduce (set, &metadata);
857
858     if (metadata.count && !current_dir_block)
859         goto bail0;
860
861     if (FcDebug () & FC_DBG_CACHE)
862         printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
863
864     atomic = FcAtomicCreate (cache_file);
865     if (!atomic)
866         goto bail0;
867
868     if (!FcAtomicLock (atomic))
869         goto bail1;
870
871     fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY, 0666);
872
873     fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0666);
874     if (fd == -1)
875         goto bail2;
876
877     current_arch_machine_name = FcCacheMachineSignature ();
878     current_arch_start = 0;
879
880     if (fd_orig != -1)
881         current_arch_start = 
882             FcCacheSkipToArch(fd_orig, current_arch_machine_name);
883
884     if (current_arch_start < 0)
885         current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
886
887     if (fd_orig != -1 && !FcCacheCopyOld(fd, fd_orig, current_arch_start))
888         goto bail3;
889
890     if (fd_orig != -1)
891         close (fd_orig);
892
893     current_arch_start = lseek(fd, 0, SEEK_CUR);
894     if (ftruncate (fd, current_arch_start) == -1)
895         goto bail3;
896
897     /* allocate space for subdir names in this block */
898     dirs_count = 0;
899     for (i = 0; i < dirs->size; i++)
900         dirs_count += strlen((char *)dirs->strs[i]) + 1;
901     dirs_count ++;
902
903     /* now write the address of the next offset */
904     truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + sizeof (FcCache) + dirs_count) + metadata.count) - current_arch_start;
905     header = malloc (10 + strlen (current_arch_machine_name));
906     if (!header)
907         goto bail3;
908     sprintf (header, "%8x ", (int)truncate_to);
909     strcat (header, current_arch_machine_name);
910     if (!FcCacheWriteString (fd, header))
911         goto bail4;
912
913     for (i = 0; i < dirs->size; i++)
914         FcCacheWriteString (fd, (char *)dirs->strs[i]);
915     FcCacheWriteString (fd, "");
916
917     write (fd, &metadata, sizeof(FcCache));
918     if (metadata.count)
919     {
920         lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_END)), SEEK_SET);
921         write (fd, current_dir_block, metadata.count);
922         free (current_dir_block);
923     }
924
925     /* this actually serves to pad out the cache file, if needed */
926     if (ftruncate (fd, current_arch_start + truncate_to) == -1)
927         goto bail4;
928
929     close(fd);
930     if (!FcAtomicReplaceOrig(atomic))
931         goto bail4;
932     FcAtomicUnlock (atomic);
933     FcAtomicDestroy (atomic);
934     return FcTrue;
935
936  bail4:
937     free (header);
938  bail3:
939     close (fd);
940  bail2:
941     FcAtomicUnlock (atomic);
942  bail1:
943     FcAtomicDestroy (atomic);
944  bail0:
945     unlink ((char *)cache_file);
946     free (cache_file);
947     if (current_dir_block)
948         free (current_dir_block);
949  bail:
950     return FcFalse;
951 }
952
953 static char *
954 FcCacheMachineSignature ()
955 {
956     static char buf[MACHINE_SIGNATURE_SIZE];
957     int32_t magic = ENDIAN_TEST;
958     char * m = (char *)&magic;
959
960     sprintf (buf, "%2x%2x%2x%2x "
961              "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x "
962              "%4x %4x %4x %4x %4x %4x %4x %4x\n", 
963              m[0], m[1], m[2], m[3],
964              (unsigned int)sizeof (char),
965              (unsigned int)sizeof (char *),
966              (unsigned int)sizeof (int),
967              (unsigned int)sizeof (FcPattern),
968              (unsigned int)sizeof (FcPatternEltPtr),
969              (unsigned int)sizeof (struct _FcPatternElt *),
970              (unsigned int)sizeof (FcPatternElt),
971              (unsigned int)sizeof (FcObjectPtr),
972              (unsigned int)sizeof (FcValueListPtr),
973              (unsigned int)sizeof (FcValue),
974              (unsigned int)sizeof (FcValueBinding),
975              (unsigned int)sizeof (struct _FcValueList *),
976              (unsigned int)sizeof (FcCharSet),
977              (unsigned int)sizeof (FcCharLeaf **),
978              (unsigned int)sizeof (FcChar16 *),
979              (unsigned int)sizeof (FcChar16),
980              (unsigned int)sizeof (FcCharLeaf),
981              (unsigned int)sizeof (FcChar32),
982              (unsigned int)sizeof (FcCache),
983              (unsigned int)sysconf(_SC_PAGESIZE));
984
985     return buf;
986 }
987
988 static int banks_ptr = 0, banks_alloc = 0;
989 static int * bankId = 0, * bankIdx = 0;
990 static const char ** bankDirs = 0;
991
992 static FcBool
993 FcCacheHaveBank (int bank)
994 {
995     int i;
996
997     if (bank < FC_BANK_FIRST)
998         return FcTrue;
999
1000     for (i = 0; i < banks_ptr; i++)
1001         if (bankId[i] == bank)
1002             return FcTrue;
1003
1004     return FcFalse;
1005 }
1006
1007 int
1008 FcCacheBankToIndex (int bank)
1009 {
1010     int i, j;
1011
1012     for (i = 0; i < banks_ptr; i++)
1013         if (bankId[bankIdx[i]] == bank)
1014         {
1015             int t = bankIdx[i];
1016
1017             for (j = i; j > 0; j--)
1018                 bankIdx[j] = bankIdx[j-1];
1019             bankIdx[0] = t;
1020             return t;
1021         }
1022
1023     if (banks_ptr >= banks_alloc)
1024     {
1025         int * b, * bidx;
1026         const char ** bds;
1027
1028         b = realloc (bankId, (banks_alloc + 4) * sizeof(int));
1029         if (!b)
1030             return -1;
1031         bankId = b;
1032
1033         bidx = realloc (bankIdx, (banks_alloc + 4) * sizeof(int));
1034         if (!bidx)
1035             return -1;
1036         bankIdx = bidx;
1037
1038         bds = realloc (bankDirs, (banks_alloc + 4) * sizeof (char *));
1039         if (!bds)
1040             return -1;
1041         bankDirs = bds;
1042
1043         banks_alloc += 4;
1044     }
1045
1046     i = banks_ptr++;
1047     bankId[i] = bank;
1048     bankIdx[i] = i;
1049     return i;
1050 }
1051
1052 static void
1053 FcCacheAddBankDir (int bank, const char * dir)
1054 {
1055     int bi = FcCacheBankToIndex (bank);
1056
1057     if (bi < 0)
1058         return;
1059
1060     bankDirs[bi] = (const char *)FcStrCopy ((FcChar8 *)dir);
1061 }
1062
1063 const char *
1064 FcCacheFindBankDir (int bank)
1065 {
1066     int bi = FcCacheBankToIndex (bank);
1067     return bankDirs[bi];
1068 }
1069