]>
Commit | Line | Data |
---|---|---|
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 |
24330d27 KP |
5 | * |
6 | * Permission to use, copy, modify, distribute, and sell this software and its | |
7 | * documentation for any purpose is hereby granted without fee, provided that | |
8 | * the above copyright notice appear in all copies and that both that | |
9 | * copyright notice and this permission notice appear in supporting | |
10 | * documentation, and that the name of Keith Packard not be used in | |
11 | * advertising or publicity pertaining to distribution of the software without | |
12 | * specific, written prior permission. Keith Packard makes no | |
13 | * representations about the suitability of this software for any purpose. It | |
14 | * is provided "as is" without express or implied warranty. | |
15 | * | |
16 | * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
17 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
18 | * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
19 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
20 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
21 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
22 | * PERFORMANCE OF THIS SOFTWARE. | |
23 | */ | |
24 | ||
212c9f43 | 25 | #include <fcntl.h> |
4262e0b3 | 26 | #include <dirent.h> |
212c9f43 PL |
27 | #include <sys/mman.h> |
28 | #include <sys/utsname.h> | |
24330d27 KP |
29 | #include "fcint.h" |
30 | ||
2dbe7597 PL |
31 | #define ENDIAN_TEST 0x12345678 |
32 | #define MACHINE_SIGNATURE_SIZE 9 + 5*19 + 1 | |
33 | ||
34 | static char * | |
35 | FcCacheProduceMachineSignature (void); | |
36 | ||
1b7be377 PL |
37 | /* |
38 | * POSIX has broken stdio so that getc must do thread-safe locking, | |
39 | * this is a serious performance problem for applications doing large | |
40 | * amounts of IO with getc (as is done here). If available, use | |
41 | * the getc_unlocked varient instead. | |
42 | */ | |
43 | ||
44 | #if defined(getc_unlocked) || defined(_IO_getc_unlocked) | |
45 | #define GETC(f) getc_unlocked(f) | |
46 | #define PUTC(c,f) putc_unlocked(c,f) | |
47 | #else | |
48 | #define GETC(f) getc(f) | |
49 | #define PUTC(c,f) putc(c,f) | |
50 | #endif | |
51 | ||
52 | #define FC_DBG_CACHE_REF 1024 | |
53 | ||
212c9f43 | 54 | #define PAGESIZE 8192 |
fa244f3d | 55 | |
212c9f43 | 56 | static FcBool force; |
24330d27 | 57 | |
ccb3e93b | 58 | static FcChar8 * |
1b7be377 PL |
59 | FcCacheReadString (FILE *f, FcChar8 *dest, int len) |
60 | { | |
61 | int c; | |
62 | FcBool escape; | |
63 | FcChar8 *d; | |
64 | int size; | |
65 | int i; | |
66 | ||
67 | while ((c = GETC (f)) != EOF) | |
68 | if (c == '"') | |
69 | break; | |
70 | if (c == EOF) | |
71 | return FcFalse; | |
72 | if (len == 0) | |
73 | return FcFalse; | |
74 | ||
75 | size = len; | |
76 | i = 0; | |
77 | d = dest; | |
78 | escape = FcFalse; | |
79 | while ((c = GETC (f)) != EOF) | |
80 | { | |
81 | if (!escape) | |
82 | { | |
83 | switch (c) { | |
84 | case '"': | |
85 | c = '\0'; | |
86 | break; | |
87 | case '\\': | |
88 | escape = FcTrue; | |
89 | continue; | |
90 | } | |
91 | } | |
92 | if (i == size) | |
93 | { | |
94 | FcChar8 *new = malloc (size * 2); /* freed in caller */ | |
95 | if (!new) | |
96 | break; | |
97 | memcpy (new, d, size); | |
98 | size *= 2; | |
99 | if (d != dest) | |
100 | free (d); | |
101 | d = new; | |
102 | } | |
103 | d[i++] = c; | |
104 | if (c == '\0') | |
105 | return d; | |
106 | escape = FcFalse; | |
107 | } | |
108 | if (d != dest) | |
109 | free (d); | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static FcBool | |
114 | FcCacheReadUlong (FILE *f, unsigned long *dest) | |
115 | { | |
116 | unsigned long t; | |
117 | int c; | |
118 | ||
119 | while ((c = GETC (f)) != EOF) | |
120 | { | |
121 | if (!isspace (c)) | |
122 | break; | |
123 | } | |
124 | if (c == EOF) | |
125 | return FcFalse; | |
126 | t = 0; | |
127 | for (;;) | |
128 | { | |
129 | if (c == EOF || isspace (c)) | |
130 | break; | |
131 | if (!isdigit (c)) | |
132 | return FcFalse; | |
133 | t = t * 10 + (c - '0'); | |
134 | c = GETC (f); | |
135 | } | |
136 | *dest = t; | |
137 | return FcTrue; | |
138 | } | |
139 | ||
140 | static FcBool | |
141 | FcCacheReadInt (FILE *f, int *dest) | |
142 | { | |
143 | unsigned long t; | |
144 | FcBool ret; | |
145 | ||
146 | ret = FcCacheReadUlong (f, &t); | |
147 | if (ret) | |
148 | *dest = (int) t; | |
149 | return ret; | |
150 | } | |
151 | ||
152 | static FcBool | |
153 | FcCacheReadTime (FILE *f, time_t *dest) | |
154 | { | |
155 | unsigned long t; | |
156 | FcBool ret; | |
157 | ||
158 | ret = FcCacheReadUlong (f, &t); | |
159 | if (ret) | |
160 | *dest = (time_t) t; | |
161 | return ret; | |
162 | } | |
163 | ||
164 | static FcBool | |
165 | FcCacheWriteChars (FILE *f, const FcChar8 *chars) | |
166 | { | |
167 | FcChar8 c; | |
168 | while ((c = *chars++)) | |
169 | { | |
170 | switch (c) { | |
171 | case '"': | |
172 | case '\\': | |
173 | if (PUTC ('\\', f) == EOF) | |
174 | return FcFalse; | |
175 | /* fall through */ | |
176 | default: | |
177 | if (PUTC (c, f) == EOF) | |
178 | return FcFalse; | |
179 | } | |
180 | } | |
181 | return FcTrue; | |
182 | } | |
183 | ||
184 | static FcBool | |
185 | FcCacheWriteString (FILE *f, const FcChar8 *string) | |
186 | { | |
187 | ||
188 | if (PUTC ('"', f) == EOF) | |
189 | return FcFalse; | |
190 | if (!FcCacheWriteChars (f, string)) | |
191 | return FcFalse; | |
192 | if (PUTC ('"', f) == EOF) | |
193 | return FcFalse; | |
194 | return FcTrue; | |
195 | } | |
196 | ||
197 | static FcBool | |
198 | FcCacheWritePath (FILE *f, const FcChar8 *dir, const FcChar8 *file) | |
199 | { | |
200 | if (PUTC ('"', f) == EOF) | |
201 | return FcFalse; | |
202 | if (dir) | |
203 | if (!FcCacheWriteChars (f, dir)) | |
204 | return FcFalse; | |
205 | #ifdef _WIN32 | |
206 | if (dir && | |
207 | dir[strlen((const char *) dir) - 1] != '/' && | |
208 | dir[strlen((const char *) dir) - 1] != '\\') | |
209 | { | |
210 | if (!FcCacheWriteChars (f, "\\")) | |
211 | return FcFalse; | |
212 | } | |
213 | #else | |
214 | if (dir && dir[strlen((const char *) dir) - 1] != '/') | |
215 | if (PUTC ('/', f) == EOF) | |
216 | return FcFalse; | |
217 | #endif | |
218 | if (!FcCacheWriteChars (f, file)) | |
219 | return FcFalse; | |
220 | if (PUTC ('"', f) == EOF) | |
221 | return FcFalse; | |
222 | return FcTrue; | |
223 | } | |
224 | ||
225 | static FcBool | |
226 | FcCacheWriteUlong (FILE *f, unsigned long t) | |
227 | { | |
228 | int pow; | |
229 | unsigned long temp, digit; | |
230 | ||
231 | temp = t; | |
232 | pow = 1; | |
233 | while (temp >= 10) | |
234 | { | |
235 | temp /= 10; | |
236 | pow *= 10; | |
237 | } | |
238 | temp = t; | |
239 | while (pow) | |
240 | { | |
241 | digit = temp / pow; | |
242 | if (PUTC ((char) digit + '0', f) == EOF) | |
243 | return FcFalse; | |
244 | temp = temp - pow * digit; | |
245 | pow = pow / 10; | |
246 | } | |
247 | return FcTrue; | |
248 | } | |
249 | ||
250 | static FcBool | |
251 | FcCacheWriteInt (FILE *f, int i) | |
252 | { | |
253 | return FcCacheWriteUlong (f, (unsigned long) i); | |
254 | } | |
255 | ||
256 | static FcBool | |
257 | FcCacheWriteTime (FILE *f, time_t t) | |
258 | { | |
259 | return FcCacheWriteUlong (f, (unsigned long) t); | |
260 | } | |
261 | ||
262 | static FcChar8 * | |
263 | FcCacheReadString2 (int fd, FcChar8 *dest, int len) | |
24330d27 | 264 | { |
212c9f43 | 265 | FcChar8 c; |
ccb3e93b | 266 | FcBool escape; |
ccb3e93b KP |
267 | int size; |
268 | int i; | |
24330d27 | 269 | |
24330d27 KP |
270 | if (len == 0) |
271 | return FcFalse; | |
272 | ||
ccb3e93b KP |
273 | size = len; |
274 | i = 0; | |
24330d27 | 275 | escape = FcFalse; |
212c9f43 | 276 | while (read (fd, &c, 1) == 1) |
24330d27 KP |
277 | { |
278 | if (!escape) | |
279 | { | |
280 | switch (c) { | |
281 | case '"': | |
ccb3e93b KP |
282 | c = '\0'; |
283 | break; | |
24330d27 KP |
284 | case '\\': |
285 | escape = FcTrue; | |
286 | continue; | |
287 | } | |
288 | } | |
ccb3e93b KP |
289 | if (i == size) |
290 | { | |
212c9f43 PL |
291 | dest[i++] = 0; |
292 | return dest; | |
ccb3e93b | 293 | } |
212c9f43 | 294 | dest[i++] = c; |
ccb3e93b | 295 | if (c == '\0') |
212c9f43 | 296 | return dest; |
24330d27 KP |
297 | escape = FcFalse; |
298 | } | |
ccb3e93b | 299 | return 0; |
24330d27 KP |
300 | } |
301 | ||
302 | static FcBool | |
1b7be377 | 303 | FcCacheWriteString2 (int fd, const FcChar8 *chars) |
327a7fd4 | 304 | { |
212c9f43 | 305 | if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1) |
327a7fd4 | 306 | return FcFalse; |
327a7fd4 KP |
307 | return FcTrue; |
308 | } | |
309 | ||
1b7be377 PL |
310 | static FcBool |
311 | FcCacheFontSetAdd (FcFontSet *set, | |
312 | FcStrSet *dirs, | |
313 | const FcChar8 *dir, | |
314 | int dir_len, | |
315 | const FcChar8 *file, | |
316 | const FcChar8 *name, | |
317 | FcConfig *config) | |
318 | { | |
319 | FcChar8 path_buf[8192], *path; | |
320 | int len; | |
321 | FcBool ret = FcFalse; | |
322 | FcPattern *font; | |
323 | FcPattern *frozen; | |
324 | ||
325 | path = path_buf; | |
326 | len = (dir_len + 1 + strlen ((const char *) file) + 1); | |
327 | if (len > sizeof (path_buf)) | |
328 | { | |
329 | path = malloc (len); /* freed down below */ | |
330 | if (!path) | |
331 | return FcFalse; | |
332 | } | |
333 | strncpy ((char *) path, (const char *) dir, dir_len); | |
334 | #ifdef _WIN32 | |
335 | if (dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\' ) | |
336 | path[dir_len++] = '\\'; | |
337 | #else | |
338 | if (dir[dir_len - 1] != '/') | |
339 | path[dir_len++] = '/'; | |
340 | #endif | |
341 | strcpy ((char *) path + dir_len, (const char *) file); | |
342 | if (config && !FcConfigAcceptFilename (config, path)) | |
343 | ret = FcTrue; | |
344 | else if (!FcStrCmp (name, FC_FONT_FILE_DIR)) | |
345 | { | |
346 | if (FcDebug () & FC_DBG_CACHEV) | |
347 | printf (" dir cache dir \"%s\"\n", path); | |
348 | ret = FcStrSetAdd (dirs, path); | |
349 | } | |
350 | else if (!FcStrCmp (name, FC_FONT_FILE_INVALID)) | |
351 | { | |
352 | ret = FcTrue; | |
353 | } | |
354 | else | |
355 | { | |
356 | font = FcNameParse (name); | |
357 | if (font) | |
358 | { | |
359 | FcChar8 *family; | |
360 | ||
361 | if (FcDebug () & FC_DBG_CACHEV) | |
362 | printf (" dir cache file \"%s\"\n", file); | |
363 | ret = FcPatternAddString (font, FC_FILE, path); | |
364 | /* | |
365 | * Make sure the pattern has the file name as well as | |
366 | * already containing at least one family name. | |
367 | */ | |
368 | if (ret && | |
369 | FcPatternGetString (font, FC_FAMILY, 0, &family) == FcResultMatch && | |
370 | (!config || FcConfigAcceptFont (config, font))) | |
371 | { | |
372 | frozen = FcPatternFreeze (font); | |
373 | ret = (frozen != 0); | |
374 | if (ret) | |
375 | ret = FcFontSetAdd (set, frozen); | |
376 | } | |
377 | FcPatternDestroy (font); | |
378 | } | |
379 | } | |
380 | if (path != path_buf) free (path); | |
381 | return ret; | |
382 | ||
383 | } | |
384 | ||
385 | static unsigned int | |
386 | FcCacheHash (const FcChar8 *string, int len) | |
387 | { | |
388 | unsigned int h = 0; | |
389 | FcChar8 c; | |
390 | ||
391 | while (len-- && (c = *string++)) | |
392 | h = (h << 1) ^ c; | |
393 | return h; | |
394 | } | |
395 | ||
327a7fd4 KP |
396 | /* |
397 | * Verify the saved timestamp for a file | |
398 | */ | |
399 | FcBool | |
a8386abc | 400 | FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info) |
327a7fd4 KP |
401 | { |
402 | struct stat statb; | |
403 | ||
a8386abc | 404 | if (stat ((char *) file, &statb) < 0) |
327a7fd4 KP |
405 | { |
406 | if (FcDebug () & FC_DBG_CACHE) | |
a8386abc | 407 | printf (" file %s missing\n", file); |
327a7fd4 KP |
408 | return FcFalse; |
409 | } | |
410 | if (statb.st_mtime != info->time) | |
411 | { | |
412 | if (FcDebug () & FC_DBG_CACHE) | |
413 | printf (" timestamp mismatch (was %d is %d)\n", | |
414 | (int) info->time, (int) statb.st_mtime); | |
415 | return FcFalse; | |
416 | } | |
417 | return FcTrue; | |
418 | } | |
419 | ||
420 | void | |
421 | FcGlobalCacheReferenced (FcGlobalCache *cache, | |
422 | FcGlobalCacheInfo *info) | |
423 | { | |
424 | if (!info->referenced) | |
425 | { | |
426 | info->referenced = FcTrue; | |
427 | cache->referenced++; | |
428 | if (FcDebug () & FC_DBG_CACHE_REF) | |
429 | printf ("Reference %d %s\n", cache->referenced, info->file); | |
430 | } | |
431 | } | |
432 | ||
433 | /* | |
434 | * Break a path into dir/base elements and compute the base hash | |
435 | * and the dir length. This is shared between the functions | |
436 | * which walk the file caches | |
437 | */ | |
438 | ||
439 | typedef struct _FcFilePathInfo { | |
440 | const FcChar8 *dir; | |
441 | int dir_len; | |
442 | const FcChar8 *base; | |
443 | unsigned int base_hash; | |
444 | } FcFilePathInfo; | |
445 | ||
446 | static FcFilePathInfo | |
447 | FcFilePathInfoGet (const FcChar8 *path) | |
448 | { | |
449 | FcFilePathInfo i; | |
450 | FcChar8 *slash; | |
451 | ||
daeed6e0 | 452 | slash = FcStrLastSlash (path); |
327a7fd4 KP |
453 | if (slash) |
454 | { | |
455 | i.dir = path; | |
456 | i.dir_len = slash - path; | |
457 | if (!i.dir_len) | |
458 | i.dir_len = 1; | |
459 | i.base = slash + 1; | |
460 | } | |
461 | else | |
462 | { | |
463 | i.dir = (const FcChar8 *) "."; | |
464 | i.dir_len = 1; | |
465 | i.base = path; | |
466 | } | |
c8d5753c | 467 | i.base_hash = FcCacheHash (i.base, -1); |
327a7fd4 KP |
468 | return i; |
469 | } | |
470 | ||
1b7be377 PL |
471 | FcGlobalCacheDir * |
472 | FcGlobalCacheDirGet (FcGlobalCache *cache, | |
473 | const FcChar8 *dir, | |
474 | int len, | |
475 | FcBool create_missing) | |
476 | { | |
477 | unsigned int hash = FcCacheHash (dir, len); | |
478 | FcGlobalCacheDir *d, **prev; | |
479 | ||
480 | for (prev = &cache->ents[hash % FC_GLOBAL_CACHE_DIR_HASH_SIZE]; | |
481 | (d = *prev); | |
482 | prev = &(*prev)->next) | |
483 | { | |
484 | if (d->info.hash == hash && d->len == len && | |
485 | !strncmp ((const char *) d->info.file, | |
486 | (const char *) dir, len)) | |
487 | break; | |
488 | } | |
489 | if (!(d = *prev)) | |
490 | { | |
491 | int i; | |
492 | if (!create_missing) | |
493 | return 0; | |
494 | d = malloc (sizeof (FcGlobalCacheDir) + len + 1); | |
495 | if (!d) | |
496 | return 0; | |
497 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + len + 1); | |
498 | d->next = *prev; | |
499 | *prev = d; | |
500 | d->info.hash = hash; | |
501 | d->info.file = (FcChar8 *) (d + 1); | |
502 | strncpy ((char *) d->info.file, (const char *) dir, len); | |
503 | d->info.file[len] = '\0'; | |
504 | d->info.time = 0; | |
505 | d->info.referenced = FcFalse; | |
506 | d->len = len; | |
507 | for (i = 0; i < FC_GLOBAL_CACHE_FILE_HASH_SIZE; i++) | |
508 | d->ents[i] = 0; | |
509 | d->subdirs = 0; | |
510 | } | |
511 | return d; | |
512 | } | |
513 | ||
514 | static FcGlobalCacheInfo * | |
515 | FcGlobalCacheDirAdd (FcGlobalCache *cache, | |
516 | const FcChar8 *dir, | |
517 | time_t time, | |
518 | FcBool replace, | |
519 | FcBool create_missing) | |
520 | { | |
521 | FcGlobalCacheDir *d; | |
522 | FcFilePathInfo i; | |
523 | FcGlobalCacheSubdir *subdir; | |
524 | FcGlobalCacheDir *parent; | |
525 | ||
526 | i = FcFilePathInfoGet (dir); | |
527 | parent = FcGlobalCacheDirGet (cache, i.dir, i.dir_len, create_missing); | |
528 | /* | |
529 | * Tricky here -- directories containing fonts.cache-1 files | |
530 | * need entries only when the parent doesn't have a cache file. | |
531 | * That is, when the parent already exists in the cache, is | |
532 | * referenced and has a "real" timestamp. The time of 0 is | |
533 | * special and marks directories which got stuck in the | |
534 | * global cache for this very reason. Yes, it could | |
535 | * use a separate boolean field, and probably should. | |
536 | */ | |
537 | if (!parent || (!create_missing && | |
538 | (!parent->info.referenced || | |
539 | (parent->info.time == 0)))) | |
540 | return 0; | |
541 | /* | |
542 | * Add this directory to the cache | |
543 | */ | |
544 | d = FcGlobalCacheDirGet (cache, dir, strlen ((const char *) dir), FcTrue); | |
545 | if (!d) | |
546 | return 0; | |
547 | d->info.time = time; | |
548 | /* | |
549 | * Add this directory to the subdirectory list of the parent | |
550 | */ | |
551 | subdir = malloc (sizeof (FcGlobalCacheSubdir)); | |
552 | if (!subdir) | |
553 | return 0; | |
554 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir)); | |
555 | subdir->ent = d; | |
556 | subdir->next = parent->subdirs; | |
557 | parent->subdirs = subdir; | |
558 | return &d->info; | |
559 | } | |
560 | ||
561 | static void | |
562 | FcGlobalCacheDirDestroy (FcGlobalCacheDir *d) | |
563 | { | |
564 | FcGlobalCacheFile *f, *next; | |
565 | int h; | |
566 | FcGlobalCacheSubdir *s, *nexts; | |
567 | ||
568 | for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++) | |
569 | for (f = d->ents[h]; f; f = next) | |
570 | { | |
571 | next = f->next; | |
572 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) + | |
573 | strlen ((char *) f->info.file) + 1 + | |
574 | strlen ((char *) f->name) + 1); | |
575 | free (f); | |
576 | } | |
577 | for (s = d->subdirs; s; s = nexts) | |
578 | { | |
579 | nexts = s->next; | |
580 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir)); | |
581 | free (s); | |
582 | } | |
583 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1); | |
584 | free (d); | |
585 | } | |
586 | ||
587 | /* | |
588 | * If the parent is in the global cache and referenced, add | |
589 | * an entry for 'dir' to the global cache. This is used | |
590 | * for directories with fonts.cache files | |
591 | */ | |
592 | ||
593 | void | |
594 | FcGlobalCacheReferenceSubdir (FcGlobalCache *cache, | |
595 | const FcChar8 *dir) | |
596 | { | |
597 | FcGlobalCacheInfo *info; | |
598 | info = FcGlobalCacheDirAdd (cache, dir, 0, FcFalse, FcFalse); | |
599 | if (info && !info->referenced) | |
600 | { | |
601 | info->referenced = FcTrue; | |
602 | cache->referenced++; | |
603 | } | |
604 | } | |
605 | ||
606 | /* | |
607 | * Check to see if the global cache contains valid data for 'dir'. | |
608 | * If so, scan the global cache for files and directories in 'dir'. | |
609 | * else, return False. | |
610 | */ | |
611 | FcBool | |
612 | FcGlobalCacheScanDir (FcFontSet *set, | |
613 | FcStrSet *dirs, | |
614 | FcGlobalCache *cache, | |
615 | const FcChar8 *dir, | |
616 | FcConfig *config) | |
617 | { | |
618 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, dir, | |
619 | strlen ((const char *) dir), | |
620 | FcFalse); | |
621 | FcGlobalCacheFile *f; | |
622 | int h; | |
623 | int dir_len; | |
624 | FcGlobalCacheSubdir *subdir; | |
625 | FcBool any_in_cache = FcFalse; | |
626 | ||
627 | if (FcDebug() & FC_DBG_CACHE) | |
628 | printf ("FcGlobalCacheScanDir %s\n", dir); | |
629 | ||
630 | if (!d) | |
631 | { | |
632 | if (FcDebug () & FC_DBG_CACHE) | |
633 | printf ("\tNo dir cache entry\n"); | |
634 | return FcFalse; | |
635 | } | |
636 | ||
637 | /* | |
638 | * See if the timestamp recorded in the global cache | |
639 | * matches the directory time, if not, return False | |
640 | */ | |
641 | if (!FcGlobalCacheCheckTime (d->info.file, &d->info)) | |
642 | { | |
643 | if (FcDebug () & FC_DBG_CACHE) | |
644 | printf ("\tdir cache entry time mismatch\n"); | |
645 | return FcFalse; | |
646 | } | |
647 | ||
648 | /* | |
649 | * Add files from 'dir' to the fontset | |
650 | */ | |
651 | dir_len = strlen ((const char *) dir); | |
652 | for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++) | |
653 | for (f = d->ents[h]; f; f = f->next) | |
654 | { | |
655 | if (FcDebug() & FC_DBG_CACHEV) | |
656 | printf ("FcGlobalCacheScanDir add file %s\n", f->info.file); | |
657 | any_in_cache = FcTrue; | |
658 | if (!FcCacheFontSetAdd (set, dirs, dir, dir_len, | |
659 | f->info.file, f->name, config)) | |
660 | { | |
661 | cache->broken = FcTrue; | |
662 | return FcFalse; | |
663 | } | |
664 | FcGlobalCacheReferenced (cache, &f->info); | |
665 | } | |
666 | /* | |
667 | * Add directories in 'dir' to 'dirs' | |
668 | */ | |
669 | for (subdir = d->subdirs; subdir; subdir = subdir->next) | |
670 | { | |
671 | FcFilePathInfo info = FcFilePathInfoGet (subdir->ent->info.file); | |
672 | ||
673 | any_in_cache = FcTrue; | |
674 | if (!FcCacheFontSetAdd (set, dirs, dir, dir_len, | |
675 | info.base, FC_FONT_FILE_DIR, config)) | |
676 | { | |
677 | cache->broken = FcTrue; | |
678 | return FcFalse; | |
679 | } | |
680 | FcGlobalCacheReferenced (cache, &subdir->ent->info); | |
681 | } | |
682 | ||
683 | FcGlobalCacheReferenced (cache, &d->info); | |
684 | ||
685 | /* | |
686 | * To recover from a bug in previous versions of fontconfig, | |
687 | * return FcFalse if no entries in the cache were found | |
688 | * for this directory. This will cause any empty directories | |
689 | * to get rescanned every time fontconfig is initialized. This | |
690 | * might get removed at some point when the older cache files are | |
691 | * presumably fixed. | |
692 | */ | |
693 | return any_in_cache; | |
694 | } | |
695 | ||
696 | /* | |
697 | * Locate the cache entry for a particular file | |
698 | */ | |
699 | FcGlobalCacheFile * | |
700 | FcGlobalCacheFileGet (FcGlobalCache *cache, | |
701 | const FcChar8 *file, | |
702 | int id, | |
703 | int *count) | |
704 | { | |
705 | FcFilePathInfo i = FcFilePathInfoGet (file); | |
706 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir, | |
707 | i.dir_len, FcFalse); | |
708 | FcGlobalCacheFile *f, *match = 0; | |
709 | int max = -1; | |
710 | ||
711 | if (!d) | |
712 | return 0; | |
713 | for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next) | |
714 | { | |
715 | if (f->info.hash == i.base_hash && | |
716 | !strcmp ((const char *) f->info.file, (const char *) i.base)) | |
717 | { | |
718 | if (f->id == id) | |
719 | match = f; | |
720 | if (f->id > max) | |
721 | max = f->id; | |
722 | } | |
723 | } | |
724 | if (count) | |
725 | *count = max + 1; | |
726 | return match; | |
727 | } | |
728 | ||
729 | /* | |
730 | * Add a file entry to the cache | |
731 | */ | |
732 | static FcGlobalCacheInfo * | |
733 | FcGlobalCacheFileAdd (FcGlobalCache *cache, | |
734 | const FcChar8 *path, | |
735 | int id, | |
736 | time_t time, | |
737 | const FcChar8 *name, | |
738 | FcBool replace) | |
739 | { | |
740 | FcFilePathInfo i = FcFilePathInfoGet (path); | |
741 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir, | |
742 | i.dir_len, FcTrue); | |
743 | FcGlobalCacheFile *f, **prev; | |
744 | int size; | |
745 | ||
746 | if (!d) | |
747 | return 0; | |
748 | for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; | |
749 | (f = *prev); | |
750 | prev = &(*prev)->next) | |
751 | { | |
752 | if (f->info.hash == i.base_hash && | |
753 | f->id == id && | |
754 | !strcmp ((const char *) f->info.file, (const char *) i.base)) | |
755 | { | |
756 | break; | |
757 | } | |
758 | } | |
759 | if (*prev) | |
760 | { | |
761 | if (!replace) | |
762 | return 0; | |
763 | ||
764 | f = *prev; | |
765 | if (f->info.referenced) | |
766 | cache->referenced--; | |
767 | *prev = f->next; | |
768 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) + | |
769 | strlen ((char *) f->info.file) + 1 + | |
770 | strlen ((char *) f->name) + 1); | |
771 | free (f); | |
772 | } | |
773 | size = (sizeof (FcGlobalCacheFile) + | |
774 | strlen ((char *) i.base) + 1 + | |
775 | strlen ((char *) name) + 1); | |
776 | f = malloc (size); | |
777 | if (!f) | |
778 | return 0; | |
779 | FcMemAlloc (FC_MEM_CACHE, size); | |
780 | f->next = *prev; | |
781 | *prev = f; | |
782 | f->info.hash = i.base_hash; | |
783 | f->info.file = (FcChar8 *) (f + 1); | |
784 | f->info.time = time; | |
785 | f->info.referenced = FcFalse; | |
786 | f->id = id; | |
787 | f->name = f->info.file + strlen ((char *) i.base) + 1; | |
788 | strcpy ((char *) f->info.file, (const char *) i.base); | |
789 | strcpy ((char *) f->name, (const char *) name); | |
790 | return &f->info; | |
791 | } | |
792 | ||
327a7fd4 KP |
793 | FcGlobalCache * |
794 | FcGlobalCacheCreate (void) | |
24330d27 | 795 | { |
327a7fd4 KP |
796 | FcGlobalCache *cache; |
797 | int h; | |
24330d27 | 798 | |
327a7fd4 | 799 | cache = malloc (sizeof (FcGlobalCache)); |
24330d27 KP |
800 | if (!cache) |
801 | return 0; | |
9dac3c59 | 802 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
327a7fd4 | 803 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 KP |
804 | cache->ents[h] = 0; |
805 | cache->entries = 0; | |
806 | cache->referenced = 0; | |
807 | cache->updated = FcFalse; | |
bb356b68 | 808 | cache->broken = FcFalse; |
24330d27 KP |
809 | return cache; |
810 | } | |
811 | ||
812 | void | |
327a7fd4 | 813 | FcGlobalCacheDestroy (FcGlobalCache *cache) |
24330d27 | 814 | { |
327a7fd4 KP |
815 | FcGlobalCacheDir *d, *next; |
816 | int h; | |
24330d27 | 817 | |
327a7fd4 | 818 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 | 819 | { |
327a7fd4 | 820 | for (d = cache->ents[h]; d; d = next) |
24330d27 | 821 | { |
327a7fd4 KP |
822 | next = d->next; |
823 | FcGlobalCacheDirDestroy (d); | |
24330d27 KP |
824 | } |
825 | } | |
9dac3c59 | 826 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
24330d27 KP |
827 | free (cache); |
828 | } | |
829 | ||
1b7be377 PL |
830 | /* |
831 | * Cache file syntax is quite simple: | |
832 | * | |
833 | * "file_name" id time "font_name" \n | |
834 | */ | |
835 | ||
24330d27 | 836 | void |
327a7fd4 KP |
837 | FcGlobalCacheLoad (FcGlobalCache *cache, |
838 | const FcChar8 *cache_file) | |
24330d27 | 839 | { |
327a7fd4 KP |
840 | FILE *f; |
841 | FcChar8 file_buf[8192], *file; | |
842 | int id; | |
843 | time_t time; | |
844 | FcChar8 name_buf[8192], *name; | |
845 | FcGlobalCacheInfo *info; | |
24330d27 | 846 | |
ccb3e93b | 847 | f = fopen ((char *) cache_file, "r"); |
24330d27 KP |
848 | if (!f) |
849 | return; | |
850 | ||
851 | cache->updated = FcFalse; | |
ccb3e93b KP |
852 | file = 0; |
853 | name = 0; | |
327a7fd4 KP |
854 | while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) && |
855 | FcCacheReadInt (f, &id) && | |
856 | FcCacheReadTime (f, &time) && | |
857 | (name = FcCacheReadString (f, name_buf, sizeof (name_buf)))) | |
24330d27 | 858 | { |
327a7fd4 KP |
859 | if (FcDebug () & FC_DBG_CACHEV) |
860 | printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name); | |
861 | if (!FcStrCmp (name, FC_FONT_FILE_DIR)) | |
c4ab52dc | 862 | info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue); |
327a7fd4 KP |
863 | else |
864 | info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse); | |
865 | if (!info) | |
866 | cache->broken = FcTrue; | |
867 | else | |
868 | cache->entries++; | |
869 | if (FcDebug () & FC_DBG_CACHE_REF) | |
870 | printf ("FcGlobalCacheLoad entry %d %s\n", | |
871 | cache->entries, file); | |
ccb3e93b KP |
872 | if (file != file_buf) |
873 | free (file); | |
874 | if (name != name_buf) | |
875 | free (name); | |
876 | file = 0; | |
877 | name = 0; | |
24330d27 | 878 | } |
ccb3e93b KP |
879 | if (file && file != file_buf) |
880 | free (file); | |
881 | if (name && name != name_buf) | |
882 | free (name); | |
24330d27 KP |
883 | fclose (f); |
884 | } | |
885 | ||
886 | FcBool | |
327a7fd4 KP |
887 | FcGlobalCacheUpdate (FcGlobalCache *cache, |
888 | const FcChar8 *file, | |
889 | int id, | |
890 | const FcChar8 *name) | |
24330d27 | 891 | { |
327a7fd4 KP |
892 | const FcChar8 *match; |
893 | struct stat statb; | |
894 | FcGlobalCacheInfo *info; | |
24330d27 KP |
895 | |
896 | match = file; | |
897 | ||
ccb3e93b | 898 | if (stat ((char *) file, &statb) < 0) |
24330d27 | 899 | return FcFalse; |
327a7fd4 KP |
900 | if (S_ISDIR (statb.st_mode)) |
901 | info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime, | |
c4ab52dc | 902 | FcTrue, FcTrue); |
327a7fd4 KP |
903 | else |
904 | info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime, | |
905 | name, FcTrue); | |
906 | if (info) | |
24330d27 | 907 | { |
327a7fd4 KP |
908 | FcGlobalCacheReferenced (cache, info); |
909 | cache->updated = FcTrue; | |
24330d27 | 910 | } |
327a7fd4 KP |
911 | else |
912 | cache->broken = FcTrue; | |
913 | return info != 0; | |
24330d27 KP |
914 | } |
915 | ||
916 | FcBool | |
327a7fd4 KP |
917 | FcGlobalCacheSave (FcGlobalCache *cache, |
918 | const FcChar8 *cache_file) | |
24330d27 | 919 | { |
327a7fd4 KP |
920 | FILE *f; |
921 | int dir_hash, file_hash; | |
922 | FcGlobalCacheDir *dir; | |
923 | FcGlobalCacheFile *file; | |
924 | FcAtomic *atomic; | |
24330d27 KP |
925 | |
926 | if (!cache->updated && cache->referenced == cache->entries) | |
927 | return FcTrue; | |
928 | ||
327a7fd4 KP |
929 | if (cache->broken) |
930 | return FcFalse; | |
931 | ||
daeed6e0 | 932 | #if defined (HAVE_GETUID) && defined (HAVE_GETEUID) |
a391da8f KP |
933 | /* Set-UID programs can't safely update the cache */ |
934 | if (getuid () != geteuid ()) | |
935 | return FcFalse; | |
daeed6e0 | 936 | #endif |
a391da8f | 937 | |
134f6011 KP |
938 | atomic = FcAtomicCreate (cache_file); |
939 | if (!atomic) | |
24330d27 | 940 | goto bail0; |
134f6011 | 941 | if (!FcAtomicLock (atomic)) |
24330d27 | 942 | goto bail1; |
134f6011 | 943 | f = fopen ((char *) FcAtomicNewFile(atomic), "w"); |
24330d27 KP |
944 | if (!f) |
945 | goto bail2; | |
946 | ||
327a7fd4 | 947 | for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++) |
24330d27 | 948 | { |
327a7fd4 | 949 | for (dir = cache->ents[dir_hash]; dir; dir = dir->next) |
24330d27 | 950 | { |
327a7fd4 | 951 | if (!dir->info.referenced) |
24330d27 | 952 | continue; |
327a7fd4 | 953 | if (!FcCacheWriteString (f, dir->info.file)) |
24330d27 | 954 | goto bail4; |
fa244f3d | 955 | if (PUTC (' ', f) == EOF) |
24330d27 | 956 | goto bail4; |
327a7fd4 | 957 | if (!FcCacheWriteInt (f, 0)) |
24330d27 | 958 | goto bail4; |
fa244f3d | 959 | if (PUTC (' ', f) == EOF) |
24330d27 | 960 | goto bail4; |
327a7fd4 | 961 | if (!FcCacheWriteTime (f, dir->info.time)) |
24330d27 | 962 | goto bail4; |
fa244f3d | 963 | if (PUTC (' ', f) == EOF) |
24330d27 | 964 | goto bail4; |
327a7fd4 | 965 | if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR)) |
24330d27 | 966 | goto bail4; |
fa244f3d | 967 | if (PUTC ('\n', f) == EOF) |
24330d27 | 968 | goto bail4; |
327a7fd4 KP |
969 | |
970 | for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++) | |
971 | { | |
972 | for (file = dir->ents[file_hash]; file; file = file->next) | |
973 | { | |
974 | if (!file->info.referenced) | |
975 | continue; | |
976 | if (!FcCacheWritePath (f, dir->info.file, file->info.file)) | |
977 | goto bail4; | |
fa244f3d | 978 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
979 | goto bail4; |
980 | if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id)) | |
981 | goto bail4; | |
fa244f3d | 982 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
983 | goto bail4; |
984 | if (!FcCacheWriteTime (f, file->info.time)) | |
985 | goto bail4; | |
fa244f3d | 986 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
987 | goto bail4; |
988 | if (!FcCacheWriteString (f, file->name)) | |
989 | goto bail4; | |
fa244f3d | 990 | if (PUTC ('\n', f) == EOF) |
327a7fd4 KP |
991 | goto bail4; |
992 | } | |
993 | } | |
24330d27 KP |
994 | } |
995 | } | |
996 | ||
997 | if (fclose (f) == EOF) | |
998 | goto bail3; | |
999 | ||
134f6011 | 1000 | if (!FcAtomicReplaceOrig (atomic)) |
24330d27 KP |
1001 | goto bail3; |
1002 | ||
134f6011 KP |
1003 | FcAtomicUnlock (atomic); |
1004 | FcAtomicDestroy (atomic); | |
1005 | ||
24330d27 KP |
1006 | cache->updated = FcFalse; |
1007 | return FcTrue; | |
1008 | ||
1009 | bail4: | |
1010 | fclose (f); | |
1011 | bail3: | |
134f6011 | 1012 | FcAtomicDeleteNew (atomic); |
24330d27 | 1013 | bail2: |
134f6011 | 1014 | FcAtomicUnlock (atomic); |
24330d27 | 1015 | bail1: |
134f6011 | 1016 | FcAtomicDestroy (atomic); |
24330d27 KP |
1017 | bail0: |
1018 | return FcFalse; | |
1019 | } | |
1020 | ||
212c9f43 PL |
1021 | /* |
1022 | * Find the next presumably-mmapable offset after the current file | |
1023 | * pointer. | |
1024 | */ | |
4262e0b3 PL |
1025 | static int |
1026 | FcCacheNextOffset(off_t w) | |
179c3995 | 1027 | { |
212c9f43 PL |
1028 | if (w % PAGESIZE == 0) |
1029 | return w; | |
1030 | else | |
1031 | return ((w / PAGESIZE)+1)*PAGESIZE; | |
179c3995 KP |
1032 | } |
1033 | ||
212c9f43 PL |
1034 | /* return the address of the segment for the provided arch, |
1035 | * or -1 if arch not found */ | |
1036 | static off_t | |
1037 | FcCacheSkipToArch (int fd, const char * arch) | |
1038 | { | |
2dbe7597 PL |
1039 | char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9]; |
1040 | char * candidate_arch; | |
212c9f43 PL |
1041 | off_t current_arch_start = 0; |
1042 | ||
1043 | /* skip arches that are not the current arch */ | |
1044 | while (1) | |
1045 | { | |
1046 | long bs; | |
1047 | ||
1048 | lseek (fd, current_arch_start, SEEK_SET); | |
2dbe7597 PL |
1049 | if (FcCacheReadString2 (fd, candidate_arch_machine_name_count, |
1050 | sizeof (candidate_arch_machine_name_count)) == 0) | |
212c9f43 | 1051 | break; |
2dbe7597 PL |
1052 | if (!strlen(candidate_arch_machine_name_count)) |
1053 | return -1; | |
1054 | bs = strtol(candidate_arch_machine_name_count, &candidate_arch, 16); | |
1055 | candidate_arch++; /* skip leading space */ | |
212c9f43 | 1056 | |
2dbe7597 | 1057 | if (strcmp (candidate_arch, arch)==0) |
212c9f43 PL |
1058 | break; |
1059 | current_arch_start += bs; | |
1060 | } | |
1061 | ||
2dbe7597 | 1062 | if (strcmp (candidate_arch, arch)!=0) |
212c9f43 PL |
1063 | return -1; |
1064 | ||
1065 | return current_arch_start; | |
1066 | } | |
1067 | ||
1068 | /* Cuts out the segment at the file pointer (moves everything else | |
1069 | * down to cover it), and leaves the file pointer at the end of the | |
1070 | * file. */ | |
1071 | #define BUF_SIZE 8192 | |
1072 | ||
1073 | static FcBool | |
1074 | FcCacheMoveDown (int fd, off_t start) | |
1075 | { | |
1076 | char * buf = malloc (BUF_SIZE); | |
2dbe7597 | 1077 | char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9]; |
4262e0b3 | 1078 | long bs; |
212c9f43 PL |
1079 | int c, bytes_skipped; |
1080 | ||
1081 | if (!buf) | |
1082 | return FcFalse; | |
1083 | ||
1084 | lseek (fd, start, SEEK_SET); | |
1b7be377 | 1085 | if (FcCacheReadString2 (fd, candidate_arch_machine_name, |
212c9f43 PL |
1086 | sizeof (candidate_arch_machine_name)) == 0) |
1087 | goto done; | |
2dbe7597 | 1088 | if (!strlen(candidate_arch_machine_name)) |
212c9f43 PL |
1089 | goto done; |
1090 | ||
2dbe7597 | 1091 | bs = strtol(candidate_arch_machine_name, 0, 16); |
212c9f43 PL |
1092 | if (bs == 0) |
1093 | goto done; | |
1094 | ||
1095 | bytes_skipped = 0; | |
1096 | do | |
1097 | { | |
1098 | lseek (fd, start+bs+bytes_skipped, SEEK_SET); | |
1099 | if ((c = read (fd, buf, BUF_SIZE)) <= 0) | |
1100 | break; | |
1101 | lseek (fd, start+bytes_skipped, SEEK_SET); | |
1102 | if (write (fd, buf, c) < 0) | |
1103 | goto bail; | |
1104 | bytes_skipped += c; | |
1105 | } | |
1106 | while (c > 0); | |
1107 | lseek (fd, start+bytes_skipped, SEEK_SET); | |
1108 | ||
1109 | done: | |
1110 | free (buf); | |
1111 | return FcTrue; | |
1112 | ||
1113 | bail: | |
1114 | free (buf); | |
1115 | return FcFalse; | |
1116 | } | |
1117 | ||
1b7be377 PL |
1118 | FcBool |
1119 | FcDirCacheValid (const FcChar8 *dir) | |
1120 | { | |
1121 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); | |
1122 | struct stat file_stat, dir_stat; | |
1123 | ||
1124 | if (stat ((char *) dir, &dir_stat) < 0) | |
1125 | { | |
1126 | FcStrFree (cache_file); | |
1127 | return FcFalse; | |
1128 | } | |
1129 | if (stat ((char *) cache_file, &file_stat) < 0) | |
1130 | { | |
1131 | FcStrFree (cache_file); | |
1132 | return FcFalse; | |
1133 | } | |
1134 | FcStrFree (cache_file); | |
1135 | /* | |
1136 | * If the directory has been modified more recently than | |
1137 | * the cache file, the cache is not valid | |
1138 | */ | |
1139 | if (dir_stat.st_mtime - file_stat.st_mtime > 0) | |
1140 | return FcFalse; | |
1141 | return FcTrue; | |
1142 | } | |
1143 | ||
4262e0b3 | 1144 | static int |
1b7be377 PL |
1145 | FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache, |
1146 | FcStrList *list, FcFontSet * set) | |
4262e0b3 PL |
1147 | { |
1148 | DIR *d; | |
1149 | struct dirent *e; | |
1150 | int ret = 0; | |
1151 | FcChar8 *dir; | |
1152 | FcChar8 *file, *base; | |
1153 | FcStrSet *subdirs; | |
1154 | FcStrList *sublist; | |
1155 | struct stat statb; | |
1156 | ||
1157 | /* | |
1158 | * Now scan all of the directories into separate databases | |
1159 | * and write out the results | |
1160 | */ | |
1161 | while ((dir = FcStrListNext (list))) | |
1162 | { | |
1163 | /* freed below */ | |
1164 | file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1); | |
1165 | if (!file) | |
1166 | return FcFalse; | |
1167 | ||
1168 | strcpy ((char *) file, (char *) dir); | |
1169 | strcat ((char *) file, "/"); | |
1170 | base = file + strlen ((char *) file); | |
1171 | ||
1172 | subdirs = FcStrSetCreate (); | |
1173 | if (!subdirs) | |
1174 | { | |
1175 | fprintf (stderr, "Can't create directory set\n"); | |
1176 | ret++; | |
1177 | free (file); | |
1178 | continue; | |
1179 | } | |
1180 | ||
1181 | if (access ((char *) dir, X_OK) < 0) | |
1182 | { | |
1183 | switch (errno) { | |
1184 | case ENOENT: | |
1185 | case ENOTDIR: | |
1186 | case EACCES: | |
1187 | break; | |
1188 | default: | |
1189 | fprintf (stderr, "\"%s\": ", dir); | |
1190 | perror (""); | |
1191 | ret++; | |
1192 | } | |
1193 | FcStrSetDestroy (subdirs); | |
1194 | free (file); | |
1195 | continue; | |
1196 | } | |
1197 | if (stat ((char *) dir, &statb) == -1) | |
1198 | { | |
1199 | fprintf (stderr, "\"%s\": ", dir); | |
1200 | perror (""); | |
1201 | FcStrSetDestroy (subdirs); | |
1202 | ret++; | |
1203 | free (file); | |
1204 | continue; | |
1205 | } | |
1206 | if (!S_ISDIR (statb.st_mode)) | |
1207 | { | |
1208 | fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); | |
1209 | FcStrSetDestroy (subdirs); | |
1210 | free (file); | |
1211 | continue; | |
1212 | } | |
1213 | d = opendir ((char *) dir); | |
1214 | if (!d) | |
1215 | { | |
1216 | FcStrSetDestroy (subdirs); | |
1217 | free (file); | |
1218 | continue; | |
1219 | } | |
1220 | while ((e = readdir (d))) | |
1221 | { | |
1222 | if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN) | |
1223 | { | |
1224 | strcpy ((char *) base, (char *) e->d_name); | |
1225 | if (FcFileIsDir (file) && !FcStrSetAdd (subdirs, file)) | |
1226 | ret++; | |
1227 | } | |
1228 | } | |
1229 | closedir (d); | |
2dbe7597 | 1230 | if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, dir)) |
4262e0b3 | 1231 | { |
1b7be377 PL |
1232 | if (FcDebug () & FC_DBG_FONTSET) |
1233 | printf ("scan dir %s\n", dir); | |
1234 | FcDirScanConfig (set, subdirs, cache, | |
1235 | config->blanks, dir, FcFalse, config); | |
4262e0b3 PL |
1236 | } |
1237 | sublist = FcStrListCreate (subdirs); | |
1238 | FcStrSetDestroy (subdirs); | |
1239 | if (!sublist) | |
1240 | { | |
1241 | fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir); | |
1242 | ret++; | |
1243 | free (file); | |
1244 | continue; | |
1245 | } | |
1b7be377 | 1246 | ret += FcCacheReadDirs (config, cache, sublist, set); |
4262e0b3 PL |
1247 | free (file); |
1248 | } | |
1249 | FcStrListDone (list); | |
1250 | return ret; | |
1251 | } | |
1252 | ||
1253 | FcFontSet * | |
1b7be377 | 1254 | FcCacheRead (FcConfig *config, FcGlobalCache * cache) |
4262e0b3 PL |
1255 | { |
1256 | FcFontSet * s = FcFontSetCreate(); | |
1257 | if (!s) | |
1258 | return 0; | |
1259 | ||
1260 | if (force) | |
1261 | goto bail; | |
1262 | ||
1b7be377 | 1263 | if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s)) |
4262e0b3 PL |
1264 | goto bail; |
1265 | ||
1266 | return s; | |
1267 | ||
1268 | bail: | |
1269 | FcFontSetDestroy (s); | |
1270 | return 0; | |
1271 | } | |
1272 | ||
212c9f43 PL |
1273 | /* read serialized state from the cache file */ |
1274 | FcBool | |
4262e0b3 | 1275 | FcDirCacheRead (FcFontSet * set, const FcChar8 *dir) |
212c9f43 | 1276 | { |
4262e0b3 PL |
1277 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
1278 | int fd; | |
212c9f43 | 1279 | FcCache metadata; |
4262e0b3 | 1280 | void * current_dir_block; |
212c9f43 | 1281 | char * current_arch_machine_name; |
2dbe7597 | 1282 | char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE]; |
212c9f43 PL |
1283 | off_t current_arch_start = 0; |
1284 | ||
1285 | if (force) | |
4262e0b3 PL |
1286 | goto bail; |
1287 | if (!cache_file) | |
1288 | goto bail; | |
212c9f43 | 1289 | |
2dbe7597 | 1290 | current_arch_machine_name = FcCacheProduceMachineSignature(); |
4262e0b3 | 1291 | fd = open(cache_file, O_RDONLY); |
212c9f43 | 1292 | if (fd == -1) |
2dbe7597 | 1293 | goto bail; |
212c9f43 | 1294 | |
212c9f43 PL |
1295 | current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); |
1296 | if (current_arch_start < 0) | |
4262e0b3 | 1297 | goto bail1; |
212c9f43 PL |
1298 | |
1299 | lseek (fd, current_arch_start, SEEK_SET); | |
1b7be377 | 1300 | if (FcCacheReadString2 (fd, candidate_arch_machine_name, |
212c9f43 | 1301 | sizeof (candidate_arch_machine_name)) == 0) |
4262e0b3 | 1302 | goto bail1; |
212c9f43 PL |
1303 | |
1304 | // sanity check for endianness issues | |
1305 | read(fd, &metadata, sizeof(FcCache)); | |
1306 | if (metadata.magic != FC_CACHE_MAGIC) | |
4262e0b3 | 1307 | goto bail1; |
212c9f43 | 1308 | |
2dbe7597 PL |
1309 | if (!metadata.count) |
1310 | goto bail1; | |
1311 | ||
1312 | off_t pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR)); | |
1313 | current_dir_block = mmap (0, metadata.count, | |
1314 | PROT_READ, MAP_SHARED, fd, pos); | |
1315 | if (current_dir_block == MAP_FAILED) | |
1316 | perror(""); | |
1317 | ||
1318 | if (!FcFontSetUnserialize (metadata, set, current_dir_block)) | |
1319 | goto bail1; | |
4262e0b3 | 1320 | |
212c9f43 | 1321 | close(fd); |
4262e0b3 | 1322 | free (cache_file); |
212c9f43 PL |
1323 | return FcTrue; |
1324 | ||
1325 | bail1: | |
212c9f43 | 1326 | close(fd); |
4262e0b3 PL |
1327 | bail: |
1328 | free (cache_file); | |
212c9f43 PL |
1329 | return FcFalse; |
1330 | } | |
1331 | ||
1332 | /* write serialized state to the cache file */ | |
1333 | FcBool | |
4262e0b3 | 1334 | FcDirCacheWrite (int bank, FcFontSet *set, const FcChar8 *dir) |
212c9f43 | 1335 | { |
4262e0b3 PL |
1336 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
1337 | int fd, bytes_to_write, metadata_bytes; | |
212c9f43 PL |
1338 | FcCache metadata; |
1339 | off_t current_arch_start = 0, truncate_to; | |
2dbe7597 | 1340 | char * current_arch_machine_name, * header; |
4262e0b3 | 1341 | void * current_dir_block, *final_dir_block; |
212c9f43 | 1342 | |
4262e0b3 PL |
1343 | if (!cache_file) |
1344 | goto bail; | |
1345 | ||
1346 | FcFontSetNewBank(); | |
1347 | bytes_to_write = FcFontSetNeededBytes (set); | |
1348 | metadata_bytes = FcCacheNextOffset (sizeof (FcCache)); | |
212c9f43 | 1349 | |
4262e0b3 PL |
1350 | if (!bytes_to_write) |
1351 | { | |
1352 | unlink (cache_file); | |
1353 | free (cache_file); | |
1354 | return FcTrue; | |
1355 | } | |
1356 | ||
1357 | current_dir_block = malloc (bytes_to_write); | |
1358 | memset (&metadata, 0, sizeof(FcCache)); | |
1359 | metadata.count = bytes_to_write; | |
1360 | metadata.bank = bank; | |
1361 | if (!current_dir_block) | |
1362 | goto bail; | |
1363 | final_dir_block = FcFontSetDistributeBytes (&metadata, current_dir_block); | |
2dbe7597 PL |
1364 | |
1365 | if ((char *)current_dir_block + bytes_to_write != final_dir_block) | |
1366 | goto bail; | |
4262e0b3 PL |
1367 | |
1368 | if (!FcFontSetSerialize (bank, set)) | |
2dbe7597 | 1369 | goto bail; |
212c9f43 | 1370 | |
4262e0b3 PL |
1371 | if (FcDebug () & FC_DBG_CACHE) |
1372 | printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file); | |
1373 | ||
1374 | fd = open(cache_file, O_RDWR | O_CREAT, 0666); | |
212c9f43 | 1375 | if (fd == -1) |
2dbe7597 | 1376 | goto bail; |
212c9f43 | 1377 | |
2dbe7597 | 1378 | current_arch_machine_name = FcCacheProduceMachineSignature (); |
212c9f43 PL |
1379 | current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); |
1380 | if (current_arch_start < 0) | |
4262e0b3 | 1381 | current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END)); |
212c9f43 PL |
1382 | |
1383 | if (!FcCacheMoveDown(fd, current_arch_start)) | |
4262e0b3 | 1384 | goto bail1; |
212c9f43 PL |
1385 | |
1386 | current_arch_start = lseek(fd, 0, SEEK_CUR); | |
1387 | if (ftruncate (fd, current_arch_start) == -1) | |
4262e0b3 | 1388 | goto bail1; |
212c9f43 | 1389 | |
212c9f43 | 1390 | /* now write the address of the next offset */ |
2dbe7597 PL |
1391 | truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + metadata_bytes) + bytes_to_write) - current_arch_start; |
1392 | ||
1393 | header = malloc (10 + strlen (current_arch_machine_name)); | |
1394 | sprintf (header, "%8x ", (int)truncate_to); | |
1395 | strcat (header, current_arch_machine_name); | |
1396 | if (!FcCacheWriteString2 (fd, header)) | |
4262e0b3 | 1397 | goto bail1; |
212c9f43 | 1398 | |
4262e0b3 PL |
1399 | metadata.magic = FC_CACHE_MAGIC; |
1400 | write (fd, &metadata, sizeof(FcCache)); | |
1401 | lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_END)), SEEK_SET); | |
1402 | write (fd, current_dir_block, bytes_to_write); | |
1403 | ||
2dbe7597 | 1404 | /* this actually serves to pad out the cache file, if needed */ |
212c9f43 | 1405 | if (ftruncate (fd, current_arch_start + truncate_to) == -1) |
4262e0b3 | 1406 | goto bail1; |
212c9f43 PL |
1407 | |
1408 | close(fd); | |
1409 | return FcTrue; | |
1410 | ||
4262e0b3 PL |
1411 | bail1: |
1412 | free (current_dir_block); | |
212c9f43 | 1413 | free (current_arch_machine_name); |
4262e0b3 PL |
1414 | bail: |
1415 | unlink (cache_file); | |
1416 | free (cache_file); | |
212c9f43 PL |
1417 | return FcFalse; |
1418 | } | |
1419 | ||
2dbe7597 PL |
1420 | static char * |
1421 | FcCacheProduceMachineSignature () | |
1422 | { | |
1423 | static char buf[MACHINE_SIGNATURE_SIZE]; | |
1424 | int magic = ENDIAN_TEST; | |
1425 | char * m = (char *)&magic; | |
1426 | ||
1427 | sprintf (buf, "%2x%2x%2x%2x " | |
1428 | "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x " | |
1429 | "%4x %4x %4x %4x %4x %4x %4x\n", | |
1430 | m[0], m[1], m[2], m[3], | |
1431 | sizeof (char), | |
1432 | sizeof (char *), | |
1433 | sizeof (int), | |
1434 | sizeof (FcPattern), | |
1435 | sizeof (FcPatternEltPtr), | |
1436 | sizeof (struct _FcPatternElt *), | |
1437 | sizeof (FcPatternElt), | |
1438 | sizeof (FcObjectPtr), | |
1439 | sizeof (FcValueListPtr), | |
1440 | sizeof (FcValue), | |
1441 | sizeof (FcValueBinding), | |
1442 | sizeof (struct _FcValueList *), | |
1443 | sizeof (FcCharSet), | |
1444 | sizeof (FcCharLeaf **), | |
1445 | sizeof (FcChar16 *), | |
1446 | sizeof (FcChar16), | |
1447 | sizeof (FcCharLeaf), | |
1448 | sizeof (FcChar32), | |
1449 | sizeof (FcCache)); | |
1450 | ||
1451 | return buf; | |
1452 | } | |
1453 | ||
212c9f43 PL |
1454 | /* if true, ignore the cache file */ |
1455 | void | |
1456 | FcCacheForce (FcBool f) | |
1457 | { | |
1458 | force = f; | |
1459 | } | |
4262e0b3 PL |
1460 | |
1461 | static int banks_ptr = 0, banks_alloc = 0; | |
1462 | static int * bankId = 0; | |
1463 | ||
1464 | int | |
1465 | FcCacheBankCount (void) | |
1466 | { | |
1467 | return banks_ptr; | |
1468 | } | |
1469 | ||
1470 | FcBool | |
1471 | FcCacheHaveBank (int bank) | |
1472 | { | |
1473 | int i; | |
1474 | ||
1475 | if (bank < FC_BANK_FIRST) | |
1476 | return FcTrue; | |
1477 | ||
1478 | for (i = 0; i < banks_ptr; i++) | |
1479 | if (bankId[i] == bank) | |
1480 | return FcTrue; | |
1481 | ||
1482 | return FcFalse; | |
1483 | } | |
1484 | ||
1485 | int | |
1486 | FcCacheBankToIndex (int bank) | |
1487 | { | |
1488 | static int lastBank = FC_BANK_DYNAMIC, lastIndex = -1; | |
1489 | int i; | |
1490 | int * b; | |
1491 | ||
1492 | if (bank == lastBank) | |
1493 | return lastIndex; | |
1494 | ||
1495 | for (i = 0; i < banks_ptr; i++) | |
1496 | if (bankId[i] == bank) | |
1497 | return i; | |
1498 | ||
2dbe7597 | 1499 | if (banks_ptr >= banks_alloc) |
4262e0b3 | 1500 | { |
2dbe7597 | 1501 | b = realloc (bankId, (banks_alloc + 4) * sizeof(int)); |
4262e0b3 PL |
1502 | if (!b) |
1503 | return -1; | |
1504 | ||
1505 | bankId = b; | |
1506 | banks_alloc += 4; | |
1507 | } | |
1508 | ||
1509 | i = banks_ptr++; | |
1510 | bankId[i] = bank; | |
1511 | return i; | |
1512 | } |