]> git.wh0rd.org - fontconfig.git/blame - src/fccache.c
Reinstate basename patch, but keep a hash table linking FcPatterns to their
[fontconfig.git] / src / fccache.c
CommitLineData
24330d27 1/*
4bd4418a 2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
24330d27 3 *
46b51147 4 * Copyright © 2000 Keith Packard
03a212e5 5 * Copyright © 2005 Patrick Lam
24330d27
KP
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
212c9f43 26#include <fcntl.h>
4262e0b3 27#include <dirent.h>
212c9f43
PL
28#include <sys/mman.h>
29#include <sys/utsname.h>
fd77c154
PL
30#include <sys/types.h>
31#include <unistd.h>
24330d27
KP
32#include "fcint.h"
33
2dbe7597
PL
34#define ENDIAN_TEST 0x12345678
35#define MACHINE_SIGNATURE_SIZE 9 + 5*19 + 1
36
eb0cf671
PL
37static off_t
38FcCacheSkipToArch (int fd, const char * arch);
1b7be377 39
eb0cf671 40static FcBool
1d879de2 41FcCacheCopyOld (int fd, int fd_orig, off_t start);
1b7be377 42
eb0cf671
PL
43static void *
44FcDirCacheProduce (FcFontSet *set, FcCache * metadata);
1b7be377
PL
45
46static FcBool
e77c1718 47FcDirCacheConsume (int fd, const char * dir, FcFontSet *set);
1b7be377 48
2eb84374 49FcBool
2304e38f 50FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir);
1b7be377 51
eb0cf671
PL
52static int
53FcCacheNextOffset(off_t w);
1b7be377 54
eb0cf671
PL
55static char *
56FcCacheMachineSignature (void);
1b7be377
PL
57
58static FcBool
eb0cf671 59FcCacheHaveBank (int bank);
1b7be377 60
e77c1718
PL
61static void
62FcCacheAddBankDir (int bank, const char * dir);
63
eb0cf671 64#define FC_DBG_CACHE_REF 1024
1b7be377 65
8245771d
PL
66static char *
67FcCacheReadString (int fd, char *dest, int len)
24330d27 68{
212c9f43 69 FcChar8 c;
ccb3e93b 70 FcBool escape;
ccb3e93b
KP
71 int size;
72 int i;
24330d27 73
24330d27 74 if (len == 0)
03a212e5 75 return 0;
24330d27 76
ccb3e93b
KP
77 size = len;
78 i = 0;
24330d27 79 escape = FcFalse;
212c9f43 80 while (read (fd, &c, 1) == 1)
24330d27
KP
81 {
82 if (!escape)
83 {
84 switch (c) {
85 case '"':
ccb3e93b
KP
86 c = '\0';
87 break;
24330d27
KP
88 case '\\':
89 escape = FcTrue;
90 continue;
91 }
92 }
ccb3e93b
KP
93 if (i == size)
94 {
212c9f43
PL
95 dest[i++] = 0;
96 return dest;
ccb3e93b 97 }
212c9f43 98 dest[i++] = c;
ccb3e93b 99 if (c == '\0')
212c9f43 100 return dest;
24330d27
KP
101 escape = FcFalse;
102 }
ccb3e93b 103 return 0;
24330d27
KP
104}
105
106static FcBool
8245771d 107FcCacheWriteString (int fd, const char *chars)
327a7fd4 108{
212c9f43 109 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
327a7fd4 110 return FcFalse;
327a7fd4
KP
111 return FcTrue;
112}
113
eb0cf671
PL
114static void
115FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
1b7be377 116{
5e678e94
PL
117 FcMemFree (FC_MEM_STRING, strlen (d->name)+1);
118 free (d->name);
119 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir));
120 free (d);
1b7be377
PL
121}
122
eb0cf671
PL
123FcGlobalCache *
124FcGlobalCacheCreate (void)
1b7be377 125{
eb0cf671 126 FcGlobalCache *cache;
1b7be377 127
eb0cf671
PL
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;
1b7be377
PL
136}
137
eb0cf671
PL
138void
139FcGlobalCacheDestroy (FcGlobalCache *cache)
327a7fd4 140{
eb0cf671 141 FcGlobalCacheDir *d, *next;
327a7fd4 142
eb0cf671 143 for (d = cache->dirs; d; d = next)
327a7fd4 144 {
eb0cf671
PL
145 next = d->next;
146 FcGlobalCacheDirDestroy (d);
327a7fd4 147 }
eb0cf671
PL
148 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
149 free (cache);
327a7fd4
KP
150}
151
152void
eb0cf671 153FcGlobalCacheLoad (FcGlobalCache *cache,
03a212e5 154 FcStrSet *staleDirs,
eb0cf671 155 const FcChar8 *cache_file)
327a7fd4 156{
8245771d 157 char name_buf[8192];
eb0cf671
PL
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;
327a7fd4 162
03a212e5
PL
163 struct stat cache_stat, dir_stat;
164
165 if (stat ((char *) cache_file, &cache_stat) < 0)
166 return;
167
eb0cf671
PL
168 cache->fd = open ((char *) cache_file, O_RDONLY);
169 if (cache->fd == -1)
170 return;
327a7fd4 171
eb0cf671 172 cache->updated = FcFalse;
327a7fd4 173
eb0cf671
PL
174 current_arch_machine_name = FcCacheMachineSignature ();
175 current_arch_start = FcCacheSkipToArch(cache->fd,
176 current_arch_machine_name);
177 if (current_arch_start < 0)
e58b50e8 178 goto bail_and_destroy;
327a7fd4 179
eb0cf671 180 lseek (cache->fd, current_arch_start, SEEK_SET);
03a212e5
PL
181 FcCacheReadString (cache->fd, candidate_arch_machine_name,
182 sizeof (candidate_arch_machine_name));
183 if (strlen(candidate_arch_machine_name) == 0)
e58b50e8 184 goto bail_and_destroy;
1b7be377 185
eb0cf671 186 while (1)
1b7be377 187 {
8cb4c56d
PL
188 off_t targ;
189
eb0cf671
PL
190 FcCacheReadString (cache->fd, name_buf, sizeof (name_buf));
191 if (!strlen(name_buf))
1b7be377 192 break;
1b7be377 193
03a212e5
PL
194 if (stat ((char *) name_buf, &dir_stat) < 0 ||
195 dir_stat.st_mtime > cache_stat.st_mtime)
196 {
197 FcCache md;
198
8245771d 199 FcStrSetAdd (staleDirs, FcStrCopy ((FcChar8 *)name_buf));
03a212e5
PL
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
eb0cf671
PL
205 d = malloc (sizeof (FcGlobalCacheDir));
206 if (!d)
207 goto bail1;
1b7be377 208
eb0cf671
PL
209 d->next = cache->dirs;
210 cache->dirs = d;
1b7be377 211
8245771d 212 d->name = (char *)FcStrCopy ((FcChar8 *)name_buf);
eb0cf671
PL
213 d->ent = 0;
214 d->offset = lseek (cache->fd, 0, SEEK_CUR);
8cb4c56d
PL
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;
1b7be377 220 }
eb0cf671 221 return;
1b7be377 222
eb0cf671
PL
223 bail1:
224 for (d = cache->dirs; d; d = next)
1b7be377 225 {
eb0cf671
PL
226 next = d->next;
227 free (d);
1b7be377 228 }
eb0cf671 229 cache->dirs = 0;
e58b50e8
PL
230
231 close (cache->fd);
232 cache->fd = -1;
233 return;
234
235 bail_and_destroy:
eb0cf671
PL
236 close (cache->fd);
237 cache->fd = -1;
e58b50e8
PL
238
239 if (stat ((char *) cache_file, &cache_stat) == 0)
240 unlink ((char *)cache_file);
241
eb0cf671 242 return;
e58b50e8 243
1b7be377
PL
244}
245
1b7be377 246FcBool
8245771d 247FcGlobalCacheReadDir (FcFontSet *set, FcStrSet *dirs, FcGlobalCache * cache, const char *dir, FcConfig *config)
1b7be377 248{
eb0cf671 249 FcGlobalCacheDir *d;
03a212e5 250 FcBool ret = FcFalse;
1b7be377 251
eb0cf671 252 if (cache->fd == -1)
1b7be377 253 return FcFalse;
1b7be377 254
eb0cf671 255 for (d = cache->dirs; d; d = d->next)
1b7be377 256 {
03a212e5 257 if (strncmp (d->name, dir, strlen(dir)) == 0)
1b7be377 258 {
eb0cf671 259 lseek (cache->fd, d->offset, SEEK_SET);
e77c1718 260 if (!FcDirCacheConsume (cache->fd, dir, set))
eb0cf671 261 return FcFalse;
03a212e5
PL
262 if (strcmp (d->name, dir) == 0)
263 ret = FcTrue;
1b7be377 264 }
1b7be377 265 }
1b7be377 266
03a212e5 267 return ret;
1b7be377
PL
268}
269
eb0cf671
PL
270FcBool
271FcGlobalCacheUpdate (FcGlobalCache *cache,
8245771d 272 const char *name,
eb0cf671 273 FcFontSet *set)
1b7be377 274{
eb0cf671 275 FcGlobalCacheDir * d;
1b7be377 276
eb0cf671
PL
277 if (!set->nfont)
278 return FcTrue;
1b7be377 279
eb0cf671 280 for (d = cache->dirs; d; d = d->next)
1b7be377 281 {
eb0cf671 282 if (strcmp(d->name, name) == 0)
1b7be377 283 break;
24330d27 284 }
24330d27 285
eb0cf671 286 if (!d)
24330d27 287 {
eb0cf671
PL
288 d = malloc (sizeof (FcGlobalCacheDir));
289 if (!d)
290 return FcFalse;
291 d->next = cache->dirs;
292 cache->dirs = d;
24330d27 293 }
24330d27 294
eb0cf671 295 cache->updated = FcTrue;
24330d27 296
8245771d 297 d->name = (char *)FcStrCopy ((FcChar8 *)name);
eb0cf671
PL
298 d->ent = FcDirCacheProduce (set, &d->metadata);
299 d->offset = 0;
300 return FcTrue;
24330d27
KP
301}
302
303FcBool
327a7fd4
KP
304FcGlobalCacheSave (FcGlobalCache *cache,
305 const FcChar8 *cache_file)
24330d27 306{
1d879de2 307 int fd, fd_orig;
327a7fd4 308 FcGlobalCacheDir *dir;
327a7fd4 309 FcAtomic *atomic;
eb0cf671
PL
310 off_t current_arch_start = 0, truncate_to;
311 char * current_arch_machine_name, * header;
24330d27 312
eb0cf671 313 if (!cache->updated)
24330d27
KP
314 return FcTrue;
315
daeed6e0 316#if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
a391da8f
KP
317 /* Set-UID programs can't safely update the cache */
318 if (getuid () != geteuid ())
319 return FcFalse;
daeed6e0 320#endif
a391da8f 321
134f6011
KP
322 atomic = FcAtomicCreate (cache_file);
323 if (!atomic)
1d879de2
PL
324 return FcFalse;
325
134f6011 326 if (!FcAtomicLock (atomic))
24330d27 327 goto bail1;
eb0cf671
PL
328 fd = open ((char *) FcAtomicNewFile(atomic), O_RDWR | O_CREAT,
329 S_IRUSR | S_IWUSR);
330 if (fd == -1)
24330d27
KP
331 goto bail2;
332
1d879de2
PL
333 fd_orig = open ((char *) FcAtomicOrigFile(atomic), O_RDONLY);
334
eb0cf671 335 current_arch_machine_name = FcCacheMachineSignature ();
1d879de2
PL
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
eb0cf671 342 if (current_arch_start < 0)
649cc361 343 current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
eb0cf671 344
1d879de2
PL
345 if (!FcCacheCopyOld(fd, fd_orig, current_arch_start))
346 goto bail3;
347
348 close (fd_orig);
349 fd_orig = -1;
eb0cf671
PL
350
351 current_arch_start = lseek(fd, 0, SEEK_CUR);
352 if (ftruncate (fd, current_arch_start) == -1)
1d879de2 353 goto bail3;
eb0cf671 354
03a212e5
PL
355 header = malloc (10 + strlen (current_arch_machine_name));
356 if (!header)
1d879de2 357 goto bail3;
03a212e5 358
8cb4c56d 359 truncate_to = current_arch_start + strlen(current_arch_machine_name) + 11;
eb0cf671 360 for (dir = cache->dirs; dir; dir = dir->next)
24330d27 361 {
eb0cf671
PL
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;
eb0cf671 368
8cb4c56d
PL
369 sprintf (header, "%8x ", (int)truncate_to);
370 strcat (header, current_arch_machine_name);
371 if (!FcCacheWriteString (fd, header))
1d879de2 372 goto bail4;
8cb4c56d 373
eb0cf671
PL
374 for (dir = cache->dirs; dir; dir = dir->next)
375 {
5e678e94
PL
376 if (dir->ent)
377 {
378 FcCacheWriteString (fd, dir->name);
379 write (fd, &dir->metadata, sizeof(FcCache));
f6ee3db5 380 lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_CUR)), SEEK_SET);
5e678e94
PL
381 write (fd, dir->ent, dir->metadata.count);
382 free (dir->ent);
383 }
24330d27 384 }
eb0cf671 385 FcCacheWriteString (fd, "");
24330d27 386
eb0cf671 387 if (close (fd) == -1)
1d879de2 388 goto bail25;
24330d27 389
134f6011 390 if (!FcAtomicReplaceOrig (atomic))
1d879de2 391 goto bail25;
24330d27 392
134f6011
KP
393 FcAtomicUnlock (atomic);
394 FcAtomicDestroy (atomic);
395
24330d27
KP
396 cache->updated = FcFalse;
397 return FcTrue;
398
1d879de2
PL
399 bail4:
400 free (header);
401 bail3:
402 if (fd_orig != -1)
403 close (fd_orig);
404
405 close (fd);
406 bail25:
134f6011 407 FcAtomicDeleteNew (atomic);
1d879de2 408 bail2:
134f6011 409 FcAtomicUnlock (atomic);
1d879de2 410 bail1:
134f6011 411 FcAtomicDestroy (atomic);
24330d27
KP
412 return FcFalse;
413}
414
eb0cf671 415#define PAGESIZE 8192
212c9f43 416/*
eb0cf671
PL
417 * Find the next presumably-mmapable offset after the supplied file
418 * position.
212c9f43 419 */
4262e0b3
PL
420static int
421FcCacheNextOffset(off_t w)
179c3995 422{
212c9f43
PL
423 if (w % PAGESIZE == 0)
424 return w;
425 else
426 return ((w / PAGESIZE)+1)*PAGESIZE;
179c3995
KP
427}
428
212c9f43
PL
429/* return the address of the segment for the provided arch,
430 * or -1 if arch not found */
431static off_t
432FcCacheSkipToArch (int fd, const char * arch)
433{
2dbe7597
PL
434 char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9];
435 char * candidate_arch;
212c9f43
PL
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
8cb4c56d
PL
443 if (lseek (fd, current_arch_start, SEEK_SET) != current_arch_start)
444 return -1;
445
eb0cf671 446 if (FcCacheReadString (fd, candidate_arch_machine_name_count,
2dbe7597 447 sizeof (candidate_arch_machine_name_count)) == 0)
5e678e94 448 return -1;
2dbe7597
PL
449 if (!strlen(candidate_arch_machine_name_count))
450 return -1;
451 bs = strtol(candidate_arch_machine_name_count, &candidate_arch, 16);
8cb4c56d 452
13cdf607 453 // count = 0 should probably be distinguished from the !bs condition
8cb4c56d
PL
454 if (!bs || bs < strlen (candidate_arch_machine_name_count))
455 return -1;
456
2dbe7597 457 candidate_arch++; /* skip leading space */
212c9f43 458
2dbe7597 459 if (strcmp (candidate_arch, arch)==0)
5e678e94 460 return current_arch_start;
212c9f43
PL
461 current_arch_start += bs;
462 }
463
5e678e94 464 return -1;
212c9f43
PL
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. */
212c9f43 470static FcBool
1d879de2 471FcCacheCopyOld (int fd, int fd_orig, off_t start)
212c9f43 472{
eb0cf671 473 char * buf = malloc (8192);
2dbe7597 474 char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
4262e0b3 475 long bs;
212c9f43 476 int c, bytes_skipped;
1d879de2 477 off_t loc;
212c9f43
PL
478
479 if (!buf)
480 return FcFalse;
481
1d879de2
PL
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
212c9f43 499 lseek (fd, start, SEEK_SET);
eb0cf671 500 if (FcCacheReadString (fd, candidate_arch_machine_name,
212c9f43
PL
501 sizeof (candidate_arch_machine_name)) == 0)
502 goto done;
2dbe7597 503 if (!strlen(candidate_arch_machine_name))
212c9f43
PL
504 goto done;
505
2dbe7597 506 bs = strtol(candidate_arch_machine_name, 0, 16);
212c9f43
PL
507 if (bs == 0)
508 goto done;
509
510 bytes_skipped = 0;
511 do
512 {
513 lseek (fd, start+bs+bytes_skipped, SEEK_SET);
eb0cf671 514 if ((c = read (fd, buf, 8192)) <= 0)
212c9f43
PL
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
55c8fa4f 533/* Does not check that the cache has the appropriate arch section. */
1b7be377
PL
534FcBool
535FcDirCacheValid (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 }
6bf23804 550
55c8fa4f
PL
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. */
563FcBool
564FcDirCacheHasCurrentArch (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
6bf23804 571 current_arch_machine_name = FcCacheMachineSignature();
55c8fa4f 572 fd = open ((char *)cache_file, O_RDONLY);
6bf23804
PL
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;
55c8fa4f
PL
581
582 return FcTrue;
583}
6bf23804 584
55c8fa4f
PL
585FcBool
586FcDirCacheUnlink (const FcChar8 *dir)
587{
588 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
15d7bd0a 589 struct stat cache_stat;
55c8fa4f 590
15d7bd0a
PL
591 if (stat ((char *) cache_file, &cache_stat) == 0 &&
592 unlink ((char *)cache_file) != 0)
1178b569
PL
593 {
594 FcStrFree (cache_file);
1b7be377 595 return FcFalse;
1178b569 596 }
55c8fa4f
PL
597
598 FcStrFree (cache_file);
1b7be377
PL
599 return FcTrue;
600}
601
4262e0b3 602static int
1b7be377
PL
603FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache,
604 FcStrList *list, FcFontSet * set)
4262e0b3 605{
4262e0b3
PL
606 int ret = 0;
607 FcChar8 *dir;
608 FcChar8 *file, *base;
609 FcStrSet *subdirs;
610 FcStrList *sublist;
611 struct stat statb;
612
613 /*
03a212e5 614 * Read in the results from 'list'.
4262e0b3
PL
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 }
2304e38f 668 if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, subdirs, dir))
4262e0b3 669 {
1b7be377 670 if (FcDebug () & FC_DBG_FONTSET)
03a212e5 671 printf ("cache scan dir %s\n", dir);
2304e38f 672
1b7be377
PL
673 FcDirScanConfig (set, subdirs, cache,
674 config->blanks, dir, FcFalse, config);
4262e0b3
PL
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 }
1b7be377 685 ret += FcCacheReadDirs (config, cache, sublist, set);
4262e0b3
PL
686 free (file);
687 }
688 FcStrListDone (list);
689 return ret;
690}
691
692FcFontSet *
1b7be377 693FcCacheRead (FcConfig *config, FcGlobalCache * cache)
4262e0b3
PL
694{
695 FcFontSet * s = FcFontSetCreate();
696 if (!s)
697 return 0;
698
1b7be377 699 if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s))
4262e0b3
PL
700 goto bail;
701
702 return s;
703
704 bail:
705 FcFontSetDestroy (s);
706 return 0;
707}
708
212c9f43 709/* read serialized state from the cache file */
2eb84374 710FcBool
2304e38f 711FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir)
212c9f43 712{
8245771d 713 char *cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
4262e0b3 714 int fd;
212c9f43 715 char * current_arch_machine_name;
2dbe7597 716 char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
212c9f43 717 off_t current_arch_start = 0;
8245771d 718 char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
212c9f43 719
4262e0b3
PL
720 if (!cache_file)
721 goto bail;
212c9f43 722
eb0cf671 723 current_arch_machine_name = FcCacheMachineSignature();
4262e0b3 724 fd = open(cache_file, O_RDONLY);
212c9f43 725 if (fd == -1)
2dbe7597 726 goto bail;
212c9f43 727
212c9f43
PL
728 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
729 if (current_arch_start < 0)
4262e0b3 730 goto bail1;
212c9f43
PL
731
732 lseek (fd, current_arch_start, SEEK_SET);
eb0cf671 733 if (FcCacheReadString (fd, candidate_arch_machine_name,
212c9f43 734 sizeof (candidate_arch_machine_name)) == 0)
4262e0b3 735 goto bail1;
2304e38f
PL
736
737 while (strlen(FcCacheReadString (fd, subdirName, sizeof (subdirName))) > 0)
8245771d 738 FcStrSetAdd (dirs, (FcChar8 *)subdirName);
2304e38f 739
e77c1718 740 if (!FcDirCacheConsume (fd, (const char *)dir, set))
eb0cf671
PL
741 goto bail1;
742
743 close(fd);
744 free (cache_file);
745 return FcTrue;
746
747 bail1:
2304e38f 748 close (fd);
eb0cf671
PL
749 bail:
750 free (cache_file);
751 return FcFalse;
752}
753
754static FcBool
e77c1718 755FcDirCacheConsume (int fd, const char * dir, FcFontSet *set)
eb0cf671
PL
756{
757 FcCache metadata;
758 void * current_dir_block;
fd77c154 759 off_t pos;
212c9f43 760
212c9f43
PL
761 read(fd, &metadata, sizeof(FcCache));
762 if (metadata.magic != FC_CACHE_MAGIC)
eb0cf671 763 return FcFalse;
212c9f43 764
2dbe7597 765 if (!metadata.count)
eb0cf671 766 return FcTrue;
2dbe7597 767
fd77c154 768 pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
2dbe7597
PL
769 current_dir_block = mmap (0, metadata.count,
770 PROT_READ, MAP_SHARED, fd, pos);
771 if (current_dir_block == MAP_FAILED)
eb0cf671 772 return FcFalse;
2dbe7597
PL
773
774 if (!FcFontSetUnserialize (metadata, set, current_dir_block))
eb0cf671
PL
775 return FcFalse;
776
e77c1718
PL
777 FcCacheAddBankDir (metadata.bank, dir);
778
212c9f43 779 return FcTrue;
eb0cf671
PL
780}
781
782static void *
783FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
784{
785 void * current_dir_block, * final_dir_block;
8245771d 786 static unsigned int rand_state = 0;
eb0cf671
PL
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;
212c9f43 817
4262e0b3 818 bail:
eb0cf671
PL
819 free (current_dir_block);
820 return 0;
212c9f43
PL
821}
822
823/* write serialized state to the cache file */
824FcBool
2304e38f 825FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
212c9f43 826{
4262e0b3 827 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1d879de2
PL
828 int fd, fd_orig, i, dirs_count;
829 FcAtomic *atomic;
830 FcCache metadata;
831 off_t current_arch_start = 0, truncate_to;
8245771d 832
1d879de2
PL
833 char *current_arch_machine_name, * header;
834 void *current_dir_block;
212c9f43 835
4262e0b3
PL
836 if (!cache_file)
837 goto bail;
838
eb0cf671 839 current_dir_block = FcDirCacheProduce (set, &metadata);
212c9f43 840
a9698bed 841 if (metadata.count && !current_dir_block)
1d879de2 842 goto bail0;
212c9f43 843
4262e0b3
PL
844 if (FcDebug () & FC_DBG_CACHE)
845 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
846
1d879de2
PL
847 atomic = FcAtomicCreate (cache_file);
848 if (!atomic)
eb0cf671 849 goto bail0;
212c9f43 850
1d879de2
PL
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
eb0cf671 860 current_arch_machine_name = FcCacheMachineSignature ();
1d879de2
PL
861 current_arch_start = 0;
862
863 if (fd_orig != -1)
864 current_arch_start =
865 FcCacheSkipToArch(fd_orig, current_arch_machine_name);
866
212c9f43 867 if (current_arch_start < 0)
649cc361 868 current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
212c9f43 869
1d879de2
PL
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);
212c9f43
PL
875
876 current_arch_start = lseek(fd, 0, SEEK_CUR);
877 if (ftruncate (fd, current_arch_start) == -1)
1d879de2 878 goto bail3;
212c9f43 879
6aee8c6f
PL
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
212c9f43 886 /* now write the address of the next offset */
6aee8c6f 887 truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + sizeof (FcCache) + dirs_count) + metadata.count) - current_arch_start;
2dbe7597 888 header = malloc (10 + strlen (current_arch_machine_name));
eb0cf671 889 if (!header)
1d879de2 890 goto bail3;
2dbe7597
PL
891 sprintf (header, "%8x ", (int)truncate_to);
892 strcat (header, current_arch_machine_name);
eb0cf671 893 if (!FcCacheWriteString (fd, header))
1d879de2 894 goto bail4;
212c9f43 895
2304e38f 896 for (i = 0; i < dirs->size; i++)
8245771d 897 FcCacheWriteString (fd, (char *)dirs->strs[i]);
2304e38f
PL
898 FcCacheWriteString (fd, "");
899
4262e0b3 900 write (fd, &metadata, sizeof(FcCache));
a9698bed
PL
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 }
4262e0b3 907
2dbe7597 908 /* this actually serves to pad out the cache file, if needed */
212c9f43 909 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
1d879de2 910 goto bail4;
212c9f43
PL
911
912 close(fd);
1d879de2
PL
913 if (!FcAtomicReplaceOrig(atomic))
914 goto bail4;
915 FcAtomicUnlock (atomic);
916 FcAtomicDestroy (atomic);
212c9f43
PL
917 return FcTrue;
918
1d879de2 919 bail4:
0230c9f8 920 free (header);
1d879de2
PL
921 bail3:
922 close (fd);
923 bail2:
924 FcAtomicUnlock (atomic);
925 bail1:
926 FcAtomicDestroy (atomic);
eb0cf671 927 bail0:
8245771d 928 unlink ((char *)cache_file);
4262e0b3 929 free (cache_file);
1d879de2
PL
930 if (current_dir_block)
931 free (current_dir_block);
932 bail:
212c9f43
PL
933 return FcFalse;
934}
935
2dbe7597 936static char *
eb0cf671 937FcCacheMachineSignature ()
2dbe7597
PL
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],
cd310911
PL
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));
2dbe7597
PL
966
967 return buf;
968}
969
4262e0b3 970static int banks_ptr = 0, banks_alloc = 0;
751932dd 971static int * bankId = 0, * bankIdx = 0;
e77c1718 972static const char ** bankDirs = 0;
4262e0b3 973
eb0cf671 974static FcBool
4262e0b3
PL
975FcCacheHaveBank (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
989int
990FcCacheBankToIndex (int bank)
991{
751932dd 992 int i, j;
4262e0b3
PL
993
994 for (i = 0; i < banks_ptr; i++)
751932dd
PL
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 }
4262e0b3 1004
2dbe7597 1005 if (banks_ptr >= banks_alloc)
4262e0b3 1006 {
751932dd 1007 int * b, * bidx;
e77c1718
PL
1008 const char ** bds;
1009
2dbe7597 1010 b = realloc (bankId, (banks_alloc + 4) * sizeof(int));
4262e0b3
PL
1011 if (!b)
1012 return -1;
4262e0b3 1013 bankId = b;
751932dd
PL
1014
1015 bidx = realloc (bankIdx, (banks_alloc + 4) * sizeof(int));
1016 if (!bidx)
1017 return -1;
1018 bankIdx = bidx;
1019
e77c1718
PL
1020 bds = realloc (bankDirs, (banks_alloc + 4) * sizeof (char *));
1021 if (!bds)
1022 return -1;
1023 bankDirs = bds;
1024
4262e0b3
PL
1025 banks_alloc += 4;
1026 }
1027
1028 i = banks_ptr++;
1029 bankId[i] = bank;
751932dd 1030 bankIdx[i] = i;
4262e0b3
PL
1031 return i;
1032}
e77c1718
PL
1033
1034static void
1035FcCacheAddBankDir (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
1045const char *
1046FcCacheFindBankDir (int bank)
1047{
1048 int bi = FcCacheBankToIndex (bank);
1049 return bankDirs[bi];
1050}
1051