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