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