]>
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 | ||
25 | #include "fcint.h" | |
26 | ||
fa244f3d KP |
27 | /* |
28 | * POSIX has broken stdio so that getc must do thread-safe locking, | |
29 | * this is a serious performance problem for applications doing large | |
30 | * amounts of IO with getc (as is done here). If available, use | |
31 | * the getc_unlocked varient instead. | |
32 | */ | |
33 | ||
34 | #if defined(getc_unlocked) || defined(_IO_getc_unlocked) | |
35 | #define GETC(f) getc_unlocked(f) | |
36 | #define PUTC(c,f) putc_unlocked(c,f) | |
37 | #else | |
38 | #define GETC(f) getc(f) | |
39 | #define PUTC(c,f) putc(c,f) | |
40 | #endif | |
41 | ||
327a7fd4 | 42 | #define FC_DBG_CACHE_REF 1024 |
24330d27 | 43 | |
ccb3e93b | 44 | static FcChar8 * |
327a7fd4 | 45 | FcCacheReadString (FILE *f, FcChar8 *dest, int len) |
24330d27 | 46 | { |
ccb3e93b KP |
47 | int c; |
48 | FcBool escape; | |
49 | FcChar8 *d; | |
50 | int size; | |
51 | int i; | |
24330d27 | 52 | |
fa244f3d | 53 | while ((c = GETC (f)) != EOF) |
24330d27 KP |
54 | if (c == '"') |
55 | break; | |
56 | if (c == EOF) | |
57 | return FcFalse; | |
58 | if (len == 0) | |
59 | return FcFalse; | |
60 | ||
ccb3e93b KP |
61 | size = len; |
62 | i = 0; | |
63 | d = dest; | |
24330d27 | 64 | escape = FcFalse; |
fa244f3d | 65 | while ((c = GETC (f)) != EOF) |
24330d27 KP |
66 | { |
67 | if (!escape) | |
68 | { | |
69 | switch (c) { | |
70 | case '"': | |
ccb3e93b KP |
71 | c = '\0'; |
72 | break; | |
24330d27 KP |
73 | case '\\': |
74 | escape = FcTrue; | |
75 | continue; | |
76 | } | |
77 | } | |
ccb3e93b KP |
78 | if (i == size) |
79 | { | |
9dac3c59 | 80 | FcChar8 *new = malloc (size * 2); /* freed in caller */ |
ccb3e93b KP |
81 | if (!new) |
82 | break; | |
83 | memcpy (new, d, size); | |
84 | size *= 2; | |
85 | if (d != dest) | |
86 | free (d); | |
87 | d = new; | |
88 | } | |
89 | d[i++] = c; | |
90 | if (c == '\0') | |
91 | return d; | |
24330d27 KP |
92 | escape = FcFalse; |
93 | } | |
ccb3e93b KP |
94 | if (d != dest) |
95 | free (d); | |
96 | return 0; | |
24330d27 KP |
97 | } |
98 | ||
99 | static FcBool | |
327a7fd4 | 100 | FcCacheReadUlong (FILE *f, unsigned long *dest) |
24330d27 KP |
101 | { |
102 | unsigned long t; | |
103 | int c; | |
104 | ||
fa244f3d | 105 | while ((c = GETC (f)) != EOF) |
24330d27 KP |
106 | { |
107 | if (!isspace (c)) | |
108 | break; | |
109 | } | |
110 | if (c == EOF) | |
111 | return FcFalse; | |
112 | t = 0; | |
113 | for (;;) | |
114 | { | |
115 | if (c == EOF || isspace (c)) | |
116 | break; | |
117 | if (!isdigit (c)) | |
118 | return FcFalse; | |
119 | t = t * 10 + (c - '0'); | |
fa244f3d | 120 | c = GETC (f); |
24330d27 KP |
121 | } |
122 | *dest = t; | |
123 | return FcTrue; | |
124 | } | |
125 | ||
126 | static FcBool | |
327a7fd4 | 127 | FcCacheReadInt (FILE *f, int *dest) |
24330d27 KP |
128 | { |
129 | unsigned long t; | |
130 | FcBool ret; | |
131 | ||
327a7fd4 | 132 | ret = FcCacheReadUlong (f, &t); |
24330d27 KP |
133 | if (ret) |
134 | *dest = (int) t; | |
135 | return ret; | |
136 | } | |
137 | ||
138 | static FcBool | |
327a7fd4 | 139 | FcCacheReadTime (FILE *f, time_t *dest) |
24330d27 KP |
140 | { |
141 | unsigned long t; | |
142 | FcBool ret; | |
143 | ||
327a7fd4 | 144 | ret = FcCacheReadUlong (f, &t); |
24330d27 KP |
145 | if (ret) |
146 | *dest = (time_t) t; | |
147 | return ret; | |
148 | } | |
149 | ||
150 | static FcBool | |
327a7fd4 | 151 | FcCacheWriteChars (FILE *f, const FcChar8 *chars) |
24330d27 | 152 | { |
327a7fd4 KP |
153 | FcChar8 c; |
154 | while ((c = *chars++)) | |
155 | { | |
156 | switch (c) { | |
157 | case '"': | |
158 | case '\\': | |
fa244f3d | 159 | if (PUTC ('\\', f) == EOF) |
327a7fd4 KP |
160 | return FcFalse; |
161 | /* fall through */ | |
162 | default: | |
fa244f3d | 163 | if (PUTC (c, f) == EOF) |
327a7fd4 KP |
164 | return FcFalse; |
165 | } | |
166 | } | |
167 | return FcTrue; | |
168 | } | |
24330d27 | 169 | |
327a7fd4 KP |
170 | static FcBool |
171 | FcCacheWriteString (FILE *f, const FcChar8 *string) | |
172 | { | |
173 | ||
fa244f3d | 174 | if (PUTC ('"', f) == EOF) |
327a7fd4 KP |
175 | return FcFalse; |
176 | if (!FcCacheWriteChars (f, string)) | |
177 | return FcFalse; | |
fa244f3d | 178 | if (PUTC ('"', f) == EOF) |
327a7fd4 KP |
179 | return FcFalse; |
180 | return FcTrue; | |
181 | } | |
182 | ||
183 | static FcBool | |
184 | FcCacheWritePath (FILE *f, const FcChar8 *dir, const FcChar8 *file) | |
185 | { | |
fa244f3d | 186 | if (PUTC ('"', f) == EOF) |
327a7fd4 KP |
187 | return FcFalse; |
188 | if (dir) | |
189 | if (!FcCacheWriteChars (f, dir)) | |
190 | return FcFalse; | |
daeed6e0 TL |
191 | #ifdef _WIN32 |
192 | if (dir && | |
193 | dir[strlen((const char *) dir) - 1] != '/' && | |
194 | dir[strlen((const char *) dir) - 1] != '\\') | |
195 | { | |
196 | if (!FcCacheWriteChars (f, "\\")) | |
197 | return FcFalse; | |
198 | } | |
199 | #else | |
327a7fd4 | 200 | if (dir && dir[strlen((const char *) dir) - 1] != '/') |
fa244f3d | 201 | if (PUTC ('/', f) == EOF) |
327a7fd4 | 202 | return FcFalse; |
daeed6e0 | 203 | #endif |
327a7fd4 KP |
204 | if (!FcCacheWriteChars (f, file)) |
205 | return FcFalse; | |
fa244f3d | 206 | if (PUTC ('"', f) == EOF) |
327a7fd4 KP |
207 | return FcFalse; |
208 | return FcTrue; | |
209 | } | |
210 | ||
211 | static FcBool | |
212 | FcCacheWriteUlong (FILE *f, unsigned long t) | |
213 | { | |
214 | int pow; | |
215 | unsigned long temp, digit; | |
216 | ||
217 | temp = t; | |
218 | pow = 1; | |
219 | while (temp >= 10) | |
24330d27 | 220 | { |
327a7fd4 KP |
221 | temp /= 10; |
222 | pow *= 10; | |
24330d27 | 223 | } |
327a7fd4 KP |
224 | temp = t; |
225 | while (pow) | |
226 | { | |
227 | digit = temp / pow; | |
fa244f3d | 228 | if (PUTC ((char) digit + '0', f) == EOF) |
327a7fd4 KP |
229 | return FcFalse; |
230 | temp = temp - pow * digit; | |
231 | pow = pow / 10; | |
232 | } | |
233 | return FcTrue; | |
234 | } | |
235 | ||
236 | static FcBool | |
237 | FcCacheWriteInt (FILE *f, int i) | |
238 | { | |
239 | return FcCacheWriteUlong (f, (unsigned long) i); | |
240 | } | |
241 | ||
242 | static FcBool | |
243 | FcCacheWriteTime (FILE *f, time_t t) | |
244 | { | |
245 | return FcCacheWriteUlong (f, (unsigned long) t); | |
246 | } | |
247 | ||
248 | static FcBool | |
249 | FcCacheFontSetAdd (FcFontSet *set, | |
250 | FcStrSet *dirs, | |
251 | const FcChar8 *dir, | |
252 | int dir_len, | |
253 | const FcChar8 *file, | |
d47c9d6e KP |
254 | const FcChar8 *name, |
255 | FcConfig *config) | |
327a7fd4 KP |
256 | { |
257 | FcChar8 path_buf[8192], *path; | |
258 | int len; | |
259 | FcBool ret = FcFalse; | |
260 | FcPattern *font; | |
d8d73958 | 261 | FcPattern *frozen; |
327a7fd4 KP |
262 | |
263 | path = path_buf; | |
264 | len = (dir_len + 1 + strlen ((const char *) file) + 1); | |
265 | if (len > sizeof (path_buf)) | |
266 | { | |
9dac3c59 | 267 | path = malloc (len); /* freed down below */ |
327a7fd4 KP |
268 | if (!path) |
269 | return FcFalse; | |
270 | } | |
271 | strncpy ((char *) path, (const char *) dir, dir_len); | |
daeed6e0 TL |
272 | #ifdef _WIN32 |
273 | if (dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\' ) | |
274 | path[dir_len++] = '\\'; | |
275 | #else | |
327a7fd4 KP |
276 | if (dir[dir_len - 1] != '/') |
277 | path[dir_len++] = '/'; | |
daeed6e0 | 278 | #endif |
327a7fd4 | 279 | strcpy ((char *) path + dir_len, (const char *) file); |
d47c9d6e KP |
280 | if (config && !FcConfigAcceptFilename (config, path)) |
281 | ret = FcTrue; | |
282 | else if (!FcStrCmp (name, FC_FONT_FILE_DIR)) | |
327a7fd4 KP |
283 | { |
284 | if (FcDebug () & FC_DBG_CACHEV) | |
285 | printf (" dir cache dir \"%s\"\n", path); | |
286 | ret = FcStrSetAdd (dirs, path); | |
287 | } | |
288 | else if (!FcStrCmp (name, FC_FONT_FILE_INVALID)) | |
289 | { | |
290 | ret = FcTrue; | |
291 | } | |
292 | else | |
293 | { | |
294 | font = FcNameParse (name); | |
295 | if (font) | |
296 | { | |
297 | if (FcDebug () & FC_DBG_CACHEV) | |
298 | printf (" dir cache file \"%s\"\n", file); | |
d8d73958 | 299 | ret = FcPatternAddString (font, FC_FILE, path); |
4f27c1c0 | 300 | if (ret && (!config || FcConfigAcceptFont (config, font))) |
d8d73958 KP |
301 | { |
302 | frozen = FcPatternFreeze (font); | |
303 | ret = (frozen != 0); | |
304 | if (ret) | |
305 | ret = FcFontSetAdd (set, frozen); | |
306 | } | |
307 | FcPatternDestroy (font); | |
327a7fd4 KP |
308 | } |
309 | } | |
310 | if (path != path_buf) free (path); | |
311 | return ret; | |
312 | ||
313 | } | |
314 | ||
315 | static unsigned int | |
c8d5753c | 316 | FcCacheHash (const FcChar8 *string, int len) |
327a7fd4 KP |
317 | { |
318 | unsigned int h = 0; | |
319 | FcChar8 c; | |
320 | ||
c8d5753c | 321 | while (len-- && (c = *string++)) |
327a7fd4 | 322 | h = (h << 1) ^ c; |
c8d5753c | 323 | return h; |
327a7fd4 KP |
324 | } |
325 | ||
326 | /* | |
327 | * Verify the saved timestamp for a file | |
328 | */ | |
329 | FcBool | |
a8386abc | 330 | FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info) |
327a7fd4 KP |
331 | { |
332 | struct stat statb; | |
333 | ||
a8386abc | 334 | if (stat ((char *) file, &statb) < 0) |
327a7fd4 KP |
335 | { |
336 | if (FcDebug () & FC_DBG_CACHE) | |
a8386abc | 337 | printf (" file %s missing\n", file); |
327a7fd4 KP |
338 | return FcFalse; |
339 | } | |
340 | if (statb.st_mtime != info->time) | |
341 | { | |
342 | if (FcDebug () & FC_DBG_CACHE) | |
343 | printf (" timestamp mismatch (was %d is %d)\n", | |
344 | (int) info->time, (int) statb.st_mtime); | |
345 | return FcFalse; | |
346 | } | |
347 | return FcTrue; | |
348 | } | |
349 | ||
350 | void | |
351 | FcGlobalCacheReferenced (FcGlobalCache *cache, | |
352 | FcGlobalCacheInfo *info) | |
353 | { | |
354 | if (!info->referenced) | |
355 | { | |
356 | info->referenced = FcTrue; | |
357 | cache->referenced++; | |
358 | if (FcDebug () & FC_DBG_CACHE_REF) | |
359 | printf ("Reference %d %s\n", cache->referenced, info->file); | |
360 | } | |
361 | } | |
362 | ||
363 | /* | |
364 | * Break a path into dir/base elements and compute the base hash | |
365 | * and the dir length. This is shared between the functions | |
366 | * which walk the file caches | |
367 | */ | |
368 | ||
369 | typedef struct _FcFilePathInfo { | |
370 | const FcChar8 *dir; | |
371 | int dir_len; | |
372 | const FcChar8 *base; | |
373 | unsigned int base_hash; | |
374 | } FcFilePathInfo; | |
375 | ||
376 | static FcFilePathInfo | |
377 | FcFilePathInfoGet (const FcChar8 *path) | |
378 | { | |
379 | FcFilePathInfo i; | |
380 | FcChar8 *slash; | |
381 | ||
daeed6e0 | 382 | slash = FcStrLastSlash (path); |
327a7fd4 KP |
383 | if (slash) |
384 | { | |
385 | i.dir = path; | |
386 | i.dir_len = slash - path; | |
387 | if (!i.dir_len) | |
388 | i.dir_len = 1; | |
389 | i.base = slash + 1; | |
390 | } | |
391 | else | |
392 | { | |
393 | i.dir = (const FcChar8 *) "."; | |
394 | i.dir_len = 1; | |
395 | i.base = path; | |
396 | } | |
c8d5753c | 397 | i.base_hash = FcCacheHash (i.base, -1); |
327a7fd4 KP |
398 | return i; |
399 | } | |
400 | ||
401 | FcGlobalCacheDir * | |
402 | FcGlobalCacheDirGet (FcGlobalCache *cache, | |
403 | const FcChar8 *dir, | |
404 | int len, | |
405 | FcBool create_missing) | |
406 | { | |
c8d5753c | 407 | unsigned int hash = FcCacheHash (dir, len); |
327a7fd4 KP |
408 | FcGlobalCacheDir *d, **prev; |
409 | ||
410 | for (prev = &cache->ents[hash % FC_GLOBAL_CACHE_DIR_HASH_SIZE]; | |
411 | (d = *prev); | |
24330d27 KP |
412 | prev = &(*prev)->next) |
413 | { | |
327a7fd4 KP |
414 | if (d->info.hash == hash && d->len == len && |
415 | !strncmp ((const char *) d->info.file, | |
416 | (const char *) dir, len)) | |
24330d27 KP |
417 | break; |
418 | } | |
327a7fd4 KP |
419 | if (!(d = *prev)) |
420 | { | |
421 | int i; | |
422 | if (!create_missing) | |
423 | return 0; | |
424 | d = malloc (sizeof (FcGlobalCacheDir) + len + 1); | |
425 | if (!d) | |
426 | return 0; | |
9dac3c59 | 427 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + len + 1); |
327a7fd4 KP |
428 | d->next = *prev; |
429 | *prev = d; | |
430 | d->info.hash = hash; | |
431 | d->info.file = (FcChar8 *) (d + 1); | |
432 | strncpy ((char *) d->info.file, (const char *) dir, len); | |
433 | d->info.file[len] = '\0'; | |
434 | d->info.time = 0; | |
435 | d->info.referenced = FcFalse; | |
436 | d->len = len; | |
437 | for (i = 0; i < FC_GLOBAL_CACHE_FILE_HASH_SIZE; i++) | |
438 | d->ents[i] = 0; | |
439 | d->subdirs = 0; | |
440 | } | |
441 | return d; | |
442 | } | |
443 | ||
444 | static FcGlobalCacheInfo * | |
445 | FcGlobalCacheDirAdd (FcGlobalCache *cache, | |
446 | const FcChar8 *dir, | |
447 | time_t time, | |
c4ab52dc KP |
448 | FcBool replace, |
449 | FcBool create_missing) | |
327a7fd4 KP |
450 | { |
451 | FcGlobalCacheDir *d; | |
452 | FcFilePathInfo i; | |
453 | FcGlobalCacheSubdir *subdir; | |
454 | FcGlobalCacheDir *parent; | |
455 | ||
c4ab52dc KP |
456 | i = FcFilePathInfoGet (dir); |
457 | parent = FcGlobalCacheDirGet (cache, i.dir, i.dir_len, create_missing); | |
458 | /* | |
459 | * Tricky here -- directories containing fonts.cache-1 files | |
460 | * need entries only when the parent doesn't have a cache file. | |
461 | * That is, when the parent already exists in the cache, is | |
462 | * referenced and has a "real" timestamp. The time of 0 is | |
463 | * special and marks directories which got stuck in the | |
464 | * global cache for this very reason. Yes, it could | |
465 | * use a separate boolean field, and probably should. | |
466 | */ | |
467 | if (!parent || (!create_missing && | |
468 | (!parent->info.referenced || | |
469 | (parent->info.time == 0)))) | |
470 | return 0; | |
327a7fd4 KP |
471 | /* |
472 | * Add this directory to the cache | |
473 | */ | |
474 | d = FcGlobalCacheDirGet (cache, dir, strlen ((const char *) dir), FcTrue); | |
475 | if (!d) | |
476 | return 0; | |
477 | d->info.time = time; | |
327a7fd4 KP |
478 | /* |
479 | * Add this directory to the subdirectory list of the parent | |
480 | */ | |
e712133c | 481 | subdir = malloc (sizeof (FcGlobalCacheSubdir)); |
327a7fd4 KP |
482 | if (!subdir) |
483 | return 0; | |
e712133c KP |
484 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir)); |
485 | subdir->ent = d; | |
327a7fd4 KP |
486 | subdir->next = parent->subdirs; |
487 | parent->subdirs = subdir; | |
488 | return &d->info; | |
489 | } | |
490 | ||
491 | static void | |
492 | FcGlobalCacheDirDestroy (FcGlobalCacheDir *d) | |
493 | { | |
494 | FcGlobalCacheFile *f, *next; | |
495 | int h; | |
496 | FcGlobalCacheSubdir *s, *nexts; | |
497 | ||
498 | for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++) | |
499 | for (f = d->ents[h]; f; f = next) | |
500 | { | |
501 | next = f->next; | |
9dac3c59 KP |
502 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) + |
503 | strlen ((char *) f->info.file) + 1 + | |
504 | strlen ((char *) f->name) + 1); | |
327a7fd4 KP |
505 | free (f); |
506 | } | |
507 | for (s = d->subdirs; s; s = nexts) | |
508 | { | |
509 | nexts = s->next; | |
e712133c | 510 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir)); |
327a7fd4 KP |
511 | free (s); |
512 | } | |
9dac3c59 | 513 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1); |
327a7fd4 KP |
514 | free (d); |
515 | } | |
516 | ||
c4ab52dc KP |
517 | /* |
518 | * If the parent is in the global cache and referenced, add | |
519 | * an entry for 'dir' to the global cache. This is used | |
520 | * for directories with fonts.cache files | |
521 | */ | |
522 | ||
523 | void | |
524 | FcGlobalCacheReferenceSubdir (FcGlobalCache *cache, | |
525 | const FcChar8 *dir) | |
526 | { | |
527 | FcGlobalCacheInfo *info; | |
528 | info = FcGlobalCacheDirAdd (cache, dir, 0, FcFalse, FcFalse); | |
529 | if (info && !info->referenced) | |
530 | { | |
531 | info->referenced = FcTrue; | |
532 | cache->referenced++; | |
533 | } | |
534 | } | |
535 | ||
c8d5753c KP |
536 | /* |
537 | * Check to see if the global cache contains valid data for 'dir'. | |
538 | * If so, scan the global cache for files and directories in 'dir'. | |
539 | * else, return False. | |
540 | */ | |
327a7fd4 KP |
541 | FcBool |
542 | FcGlobalCacheScanDir (FcFontSet *set, | |
543 | FcStrSet *dirs, | |
544 | FcGlobalCache *cache, | |
d47c9d6e KP |
545 | const FcChar8 *dir, |
546 | FcConfig *config) | |
327a7fd4 KP |
547 | { |
548 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, dir, | |
549 | strlen ((const char *) dir), | |
550 | FcFalse); | |
551 | FcGlobalCacheFile *f; | |
552 | int h; | |
553 | int dir_len; | |
554 | FcGlobalCacheSubdir *subdir; | |
c8d5753c | 555 | FcBool any_in_cache = FcFalse; |
327a7fd4 KP |
556 | |
557 | if (FcDebug() & FC_DBG_CACHE) | |
558 | printf ("FcGlobalCacheScanDir %s\n", dir); | |
559 | ||
560 | if (!d) | |
561 | { | |
562 | if (FcDebug () & FC_DBG_CACHE) | |
563 | printf ("\tNo dir cache entry\n"); | |
564 | return FcFalse; | |
565 | } | |
566 | ||
c8d5753c KP |
567 | /* |
568 | * See if the timestamp recorded in the global cache | |
569 | * matches the directory time, if not, return False | |
570 | */ | |
a8386abc | 571 | if (!FcGlobalCacheCheckTime (d->info.file, &d->info)) |
327a7fd4 KP |
572 | { |
573 | if (FcDebug () & FC_DBG_CACHE) | |
574 | printf ("\tdir cache entry time mismatch\n"); | |
575 | return FcFalse; | |
576 | } | |
577 | ||
c8d5753c KP |
578 | /* |
579 | * Add files from 'dir' to the fontset | |
580 | */ | |
327a7fd4 KP |
581 | dir_len = strlen ((const char *) dir); |
582 | for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++) | |
583 | for (f = d->ents[h]; f; f = f->next) | |
584 | { | |
585 | if (FcDebug() & FC_DBG_CACHEV) | |
586 | printf ("FcGlobalCacheScanDir add file %s\n", f->info.file); | |
c8d5753c | 587 | any_in_cache = FcTrue; |
327a7fd4 | 588 | if (!FcCacheFontSetAdd (set, dirs, dir, dir_len, |
d47c9d6e | 589 | f->info.file, f->name, config)) |
327a7fd4 KP |
590 | { |
591 | cache->broken = FcTrue; | |
592 | return FcFalse; | |
593 | } | |
594 | FcGlobalCacheReferenced (cache, &f->info); | |
595 | } | |
c8d5753c KP |
596 | /* |
597 | * Add directories in 'dir' to 'dirs' | |
598 | */ | |
327a7fd4 KP |
599 | for (subdir = d->subdirs; subdir; subdir = subdir->next) |
600 | { | |
e712133c KP |
601 | FcFilePathInfo info = FcFilePathInfoGet (subdir->ent->info.file); |
602 | ||
c8d5753c | 603 | any_in_cache = FcTrue; |
327a7fd4 | 604 | if (!FcCacheFontSetAdd (set, dirs, dir, dir_len, |
d47c9d6e | 605 | info.base, FC_FONT_FILE_DIR, config)) |
327a7fd4 KP |
606 | { |
607 | cache->broken = FcTrue; | |
608 | return FcFalse; | |
609 | } | |
e712133c | 610 | FcGlobalCacheReferenced (cache, &subdir->ent->info); |
327a7fd4 KP |
611 | } |
612 | ||
613 | FcGlobalCacheReferenced (cache, &d->info); | |
614 | ||
c8d5753c KP |
615 | /* |
616 | * To recover from a bug in previous versions of fontconfig, | |
617 | * return FcFalse if no entries in the cache were found | |
618 | * for this directory. This will cause any empty directories | |
619 | * to get rescanned every time fontconfig is initialized. This | |
620 | * might get removed at some point when the older cache files are | |
621 | * presumably fixed. | |
622 | */ | |
623 | return any_in_cache; | |
327a7fd4 KP |
624 | } |
625 | ||
626 | /* | |
627 | * Locate the cache entry for a particular file | |
628 | */ | |
629 | FcGlobalCacheFile * | |
630 | FcGlobalCacheFileGet (FcGlobalCache *cache, | |
631 | const FcChar8 *file, | |
632 | int id, | |
633 | int *count) | |
634 | { | |
635 | FcFilePathInfo i = FcFilePathInfoGet (file); | |
636 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir, | |
637 | i.dir_len, FcFalse); | |
638 | FcGlobalCacheFile *f, *match = 0; | |
639 | int max = -1; | |
640 | ||
641 | if (!d) | |
642 | return 0; | |
643 | for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next) | |
644 | { | |
645 | if (f->info.hash == i.base_hash && | |
646 | !strcmp ((const char *) f->info.file, (const char *) i.base)) | |
647 | { | |
648 | if (f->id == id) | |
649 | match = f; | |
650 | if (f->id > max) | |
651 | max = f->id; | |
652 | } | |
653 | } | |
654 | if (count) | |
a8386abc | 655 | *count = max + 1; |
327a7fd4 KP |
656 | return match; |
657 | } | |
658 | ||
659 | /* | |
660 | * Add a file entry to the cache | |
661 | */ | |
662 | static FcGlobalCacheInfo * | |
663 | FcGlobalCacheFileAdd (FcGlobalCache *cache, | |
664 | const FcChar8 *path, | |
665 | int id, | |
666 | time_t time, | |
667 | const FcChar8 *name, | |
668 | FcBool replace) | |
669 | { | |
670 | FcFilePathInfo i = FcFilePathInfoGet (path); | |
671 | FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir, | |
672 | i.dir_len, FcTrue); | |
673 | FcGlobalCacheFile *f, **prev; | |
9dac3c59 | 674 | int size; |
327a7fd4 KP |
675 | |
676 | if (!d) | |
677 | return 0; | |
678 | for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; | |
679 | (f = *prev); | |
680 | prev = &(*prev)->next) | |
681 | { | |
682 | if (f->info.hash == i.base_hash && | |
683 | f->id == id && | |
684 | !strcmp ((const char *) f->info.file, (const char *) i.base)) | |
685 | { | |
686 | break; | |
687 | } | |
688 | } | |
24330d27 KP |
689 | if (*prev) |
690 | { | |
691 | if (!replace) | |
327a7fd4 | 692 | return 0; |
24330d27 | 693 | |
327a7fd4 KP |
694 | f = *prev; |
695 | if (f->info.referenced) | |
24330d27 | 696 | cache->referenced--; |
327a7fd4 | 697 | *prev = f->next; |
9dac3c59 KP |
698 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) + |
699 | strlen ((char *) f->info.file) + 1 + | |
700 | strlen ((char *) f->name) + 1); | |
327a7fd4 | 701 | free (f); |
24330d27 | 702 | } |
9dac3c59 KP |
703 | size = (sizeof (FcGlobalCacheFile) + |
704 | strlen ((char *) i.base) + 1 + | |
705 | strlen ((char *) name) + 1); | |
706 | f = malloc (size); | |
327a7fd4 KP |
707 | if (!f) |
708 | return 0; | |
9dac3c59 | 709 | FcMemAlloc (FC_MEM_CACHE, size); |
327a7fd4 KP |
710 | f->next = *prev; |
711 | *prev = f; | |
712 | f->info.hash = i.base_hash; | |
713 | f->info.file = (FcChar8 *) (f + 1); | |
714 | f->info.time = time; | |
715 | f->info.referenced = FcFalse; | |
716 | f->id = id; | |
717 | f->name = f->info.file + strlen ((char *) i.base) + 1; | |
718 | strcpy ((char *) f->info.file, (const char *) i.base); | |
719 | strcpy ((char *) f->name, (const char *) name); | |
720 | return &f->info; | |
24330d27 KP |
721 | } |
722 | ||
327a7fd4 KP |
723 | FcGlobalCache * |
724 | FcGlobalCacheCreate (void) | |
24330d27 | 725 | { |
327a7fd4 KP |
726 | FcGlobalCache *cache; |
727 | int h; | |
24330d27 | 728 | |
327a7fd4 | 729 | cache = malloc (sizeof (FcGlobalCache)); |
24330d27 KP |
730 | if (!cache) |
731 | return 0; | |
9dac3c59 | 732 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
327a7fd4 | 733 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 KP |
734 | cache->ents[h] = 0; |
735 | cache->entries = 0; | |
736 | cache->referenced = 0; | |
737 | cache->updated = FcFalse; | |
bb356b68 | 738 | cache->broken = FcFalse; |
24330d27 KP |
739 | return cache; |
740 | } | |
741 | ||
742 | void | |
327a7fd4 | 743 | FcGlobalCacheDestroy (FcGlobalCache *cache) |
24330d27 | 744 | { |
327a7fd4 KP |
745 | FcGlobalCacheDir *d, *next; |
746 | int h; | |
24330d27 | 747 | |
327a7fd4 | 748 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 | 749 | { |
327a7fd4 | 750 | for (d = cache->ents[h]; d; d = next) |
24330d27 | 751 | { |
327a7fd4 KP |
752 | next = d->next; |
753 | FcGlobalCacheDirDestroy (d); | |
24330d27 KP |
754 | } |
755 | } | |
9dac3c59 | 756 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
24330d27 KP |
757 | free (cache); |
758 | } | |
759 | ||
327a7fd4 KP |
760 | /* |
761 | * Cache file syntax is quite simple: | |
762 | * | |
763 | * "file_name" id time "font_name" \n | |
764 | */ | |
765 | ||
24330d27 | 766 | void |
327a7fd4 KP |
767 | FcGlobalCacheLoad (FcGlobalCache *cache, |
768 | const FcChar8 *cache_file) | |
24330d27 | 769 | { |
327a7fd4 KP |
770 | FILE *f; |
771 | FcChar8 file_buf[8192], *file; | |
772 | int id; | |
773 | time_t time; | |
774 | FcChar8 name_buf[8192], *name; | |
775 | FcGlobalCacheInfo *info; | |
24330d27 | 776 | |
ccb3e93b | 777 | f = fopen ((char *) cache_file, "r"); |
24330d27 KP |
778 | if (!f) |
779 | return; | |
780 | ||
781 | cache->updated = FcFalse; | |
ccb3e93b KP |
782 | file = 0; |
783 | name = 0; | |
327a7fd4 KP |
784 | while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) && |
785 | FcCacheReadInt (f, &id) && | |
786 | FcCacheReadTime (f, &time) && | |
787 | (name = FcCacheReadString (f, name_buf, sizeof (name_buf)))) | |
24330d27 | 788 | { |
327a7fd4 KP |
789 | if (FcDebug () & FC_DBG_CACHEV) |
790 | printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name); | |
791 | if (!FcStrCmp (name, FC_FONT_FILE_DIR)) | |
c4ab52dc | 792 | info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue); |
327a7fd4 KP |
793 | else |
794 | info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse); | |
795 | if (!info) | |
796 | cache->broken = FcTrue; | |
797 | else | |
798 | cache->entries++; | |
799 | if (FcDebug () & FC_DBG_CACHE_REF) | |
800 | printf ("FcGlobalCacheLoad entry %d %s\n", | |
801 | cache->entries, file); | |
ccb3e93b KP |
802 | if (file != file_buf) |
803 | free (file); | |
804 | if (name != name_buf) | |
805 | free (name); | |
806 | file = 0; | |
807 | name = 0; | |
24330d27 | 808 | } |
ccb3e93b KP |
809 | if (file && file != file_buf) |
810 | free (file); | |
811 | if (name && name != name_buf) | |
812 | free (name); | |
24330d27 KP |
813 | fclose (f); |
814 | } | |
815 | ||
816 | FcBool | |
327a7fd4 KP |
817 | FcGlobalCacheUpdate (FcGlobalCache *cache, |
818 | const FcChar8 *file, | |
819 | int id, | |
820 | const FcChar8 *name) | |
24330d27 | 821 | { |
327a7fd4 KP |
822 | const FcChar8 *match; |
823 | struct stat statb; | |
824 | FcGlobalCacheInfo *info; | |
24330d27 KP |
825 | |
826 | match = file; | |
827 | ||
ccb3e93b | 828 | if (stat ((char *) file, &statb) < 0) |
24330d27 | 829 | return FcFalse; |
327a7fd4 KP |
830 | if (S_ISDIR (statb.st_mode)) |
831 | info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime, | |
c4ab52dc | 832 | FcTrue, FcTrue); |
327a7fd4 KP |
833 | else |
834 | info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime, | |
835 | name, FcTrue); | |
836 | if (info) | |
24330d27 | 837 | { |
327a7fd4 KP |
838 | FcGlobalCacheReferenced (cache, info); |
839 | cache->updated = FcTrue; | |
24330d27 | 840 | } |
327a7fd4 KP |
841 | else |
842 | cache->broken = FcTrue; | |
843 | return info != 0; | |
24330d27 KP |
844 | } |
845 | ||
846 | FcBool | |
327a7fd4 KP |
847 | FcGlobalCacheSave (FcGlobalCache *cache, |
848 | const FcChar8 *cache_file) | |
24330d27 | 849 | { |
327a7fd4 KP |
850 | FILE *f; |
851 | int dir_hash, file_hash; | |
852 | FcGlobalCacheDir *dir; | |
853 | FcGlobalCacheFile *file; | |
854 | FcAtomic *atomic; | |
24330d27 KP |
855 | |
856 | if (!cache->updated && cache->referenced == cache->entries) | |
857 | return FcTrue; | |
858 | ||
327a7fd4 KP |
859 | if (cache->broken) |
860 | return FcFalse; | |
861 | ||
daeed6e0 | 862 | #if defined (HAVE_GETUID) && defined (HAVE_GETEUID) |
a391da8f KP |
863 | /* Set-UID programs can't safely update the cache */ |
864 | if (getuid () != geteuid ()) | |
865 | return FcFalse; | |
daeed6e0 | 866 | #endif |
a391da8f | 867 | |
134f6011 KP |
868 | atomic = FcAtomicCreate (cache_file); |
869 | if (!atomic) | |
24330d27 | 870 | goto bail0; |
134f6011 | 871 | if (!FcAtomicLock (atomic)) |
24330d27 | 872 | goto bail1; |
134f6011 | 873 | f = fopen ((char *) FcAtomicNewFile(atomic), "w"); |
24330d27 KP |
874 | if (!f) |
875 | goto bail2; | |
876 | ||
327a7fd4 | 877 | for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++) |
24330d27 | 878 | { |
327a7fd4 | 879 | for (dir = cache->ents[dir_hash]; dir; dir = dir->next) |
24330d27 | 880 | { |
327a7fd4 | 881 | if (!dir->info.referenced) |
24330d27 | 882 | continue; |
327a7fd4 | 883 | if (!FcCacheWriteString (f, dir->info.file)) |
24330d27 | 884 | goto bail4; |
fa244f3d | 885 | if (PUTC (' ', f) == EOF) |
24330d27 | 886 | goto bail4; |
327a7fd4 | 887 | if (!FcCacheWriteInt (f, 0)) |
24330d27 | 888 | goto bail4; |
fa244f3d | 889 | if (PUTC (' ', f) == EOF) |
24330d27 | 890 | goto bail4; |
327a7fd4 | 891 | if (!FcCacheWriteTime (f, dir->info.time)) |
24330d27 | 892 | goto bail4; |
fa244f3d | 893 | if (PUTC (' ', f) == EOF) |
24330d27 | 894 | goto bail4; |
327a7fd4 | 895 | if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR)) |
24330d27 | 896 | goto bail4; |
fa244f3d | 897 | if (PUTC ('\n', f) == EOF) |
24330d27 | 898 | goto bail4; |
327a7fd4 KP |
899 | |
900 | for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++) | |
901 | { | |
902 | for (file = dir->ents[file_hash]; file; file = file->next) | |
903 | { | |
904 | if (!file->info.referenced) | |
905 | continue; | |
906 | if (!FcCacheWritePath (f, dir->info.file, file->info.file)) | |
907 | goto bail4; | |
fa244f3d | 908 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
909 | goto bail4; |
910 | if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id)) | |
911 | goto bail4; | |
fa244f3d | 912 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
913 | goto bail4; |
914 | if (!FcCacheWriteTime (f, file->info.time)) | |
915 | goto bail4; | |
fa244f3d | 916 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
917 | goto bail4; |
918 | if (!FcCacheWriteString (f, file->name)) | |
919 | goto bail4; | |
fa244f3d | 920 | if (PUTC ('\n', f) == EOF) |
327a7fd4 KP |
921 | goto bail4; |
922 | } | |
923 | } | |
24330d27 KP |
924 | } |
925 | } | |
926 | ||
927 | if (fclose (f) == EOF) | |
928 | goto bail3; | |
929 | ||
134f6011 | 930 | if (!FcAtomicReplaceOrig (atomic)) |
24330d27 KP |
931 | goto bail3; |
932 | ||
134f6011 KP |
933 | FcAtomicUnlock (atomic); |
934 | FcAtomicDestroy (atomic); | |
935 | ||
24330d27 KP |
936 | cache->updated = FcFalse; |
937 | return FcTrue; | |
938 | ||
939 | bail4: | |
940 | fclose (f); | |
941 | bail3: | |
134f6011 | 942 | FcAtomicDeleteNew (atomic); |
24330d27 | 943 | bail2: |
134f6011 | 944 | FcAtomicUnlock (atomic); |
24330d27 | 945 | bail1: |
134f6011 | 946 | FcAtomicDestroy (atomic); |
24330d27 KP |
947 | bail0: |
948 | return FcFalse; | |
949 | } | |
950 | ||
951 | FcBool | |
327a7fd4 | 952 | FcDirCacheValid (const FcChar8 *dir) |
179c3995 | 953 | { |
327a7fd4 | 954 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
179c3995 KP |
955 | struct stat file_stat, dir_stat; |
956 | ||
179c3995 KP |
957 | if (stat ((char *) dir, &dir_stat) < 0) |
958 | { | |
327a7fd4 | 959 | FcStrFree (cache_file); |
179c3995 KP |
960 | return FcFalse; |
961 | } | |
179c3995 | 962 | if (stat ((char *) cache_file, &file_stat) < 0) |
327a7fd4 KP |
963 | { |
964 | FcStrFree (cache_file); | |
179c3995 | 965 | return FcFalse; |
327a7fd4 KP |
966 | } |
967 | FcStrFree (cache_file); | |
179c3995 KP |
968 | /* |
969 | * If the directory has been modified more recently than | |
970 | * the cache file, the cache is not valid | |
971 | */ | |
972 | if (dir_stat.st_mtime - file_stat.st_mtime > 0) | |
973 | return FcFalse; | |
974 | return FcTrue; | |
975 | } | |
976 | ||
977 | FcBool | |
d47c9d6e | 978 | FcDirCacheReadDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config) |
24330d27 | 979 | { |
327a7fd4 | 980 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
24330d27 | 981 | FILE *f; |
ccb3e93b | 982 | FcChar8 *base; |
24330d27 | 983 | int id; |
179c3995 | 984 | int dir_len; |
179c3995 | 985 | FcChar8 file_buf[8192], *file; |
ccb3e93b | 986 | FcChar8 name_buf[8192], *name; |
24330d27 KP |
987 | FcBool ret = FcFalse; |
988 | ||
327a7fd4 KP |
989 | if (!cache_file) |
990 | goto bail0; | |
991 | ||
24330d27 | 992 | if (FcDebug () & FC_DBG_CACHE) |
327a7fd4 | 993 | printf ("FcDirCacheReadDir cache_file \"%s\"\n", cache_file); |
24330d27 | 994 | |
ccb3e93b | 995 | f = fopen ((char *) cache_file, "r"); |
24330d27 KP |
996 | if (!f) |
997 | { | |
998 | if (FcDebug () & FC_DBG_CACHE) | |
24330d27 | 999 | printf (" no cache file\n"); |
327a7fd4 | 1000 | goto bail1; |
24330d27 KP |
1001 | } |
1002 | ||
327a7fd4 | 1003 | if (!FcDirCacheValid (dir)) |
179c3995 KP |
1004 | { |
1005 | if (FcDebug () & FC_DBG_CACHE) | |
179c3995 | 1006 | printf (" cache file older than directory\n"); |
327a7fd4 | 1007 | goto bail2; |
179c3995 KP |
1008 | } |
1009 | ||
ccb3e93b | 1010 | base = (FcChar8 *) strrchr ((char *) cache_file, '/'); |
24330d27 | 1011 | if (!base) |
327a7fd4 | 1012 | goto bail2; |
24330d27 | 1013 | base++; |
179c3995 | 1014 | dir_len = base - cache_file; |
24330d27 | 1015 | |
ccb3e93b KP |
1016 | file = 0; |
1017 | name = 0; | |
327a7fd4 KP |
1018 | while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) && |
1019 | FcCacheReadInt (f, &id) && | |
1020 | (name = FcCacheReadString (f, name_buf, sizeof (name_buf)))) | |
24330d27 | 1021 | { |
327a7fd4 | 1022 | if (!FcCacheFontSetAdd (set, dirs, cache_file, dir_len, |
d47c9d6e | 1023 | file, name, config)) |
327a7fd4 | 1024 | goto bail3; |
ccb3e93b KP |
1025 | if (file != file_buf) |
1026 | free (file); | |
1027 | if (name != name_buf) | |
1028 | free (name); | |
327a7fd4 | 1029 | file = name = 0; |
24330d27 KP |
1030 | } |
1031 | if (FcDebug () & FC_DBG_CACHE) | |
24330d27 | 1032 | printf (" cache loaded\n"); |
24330d27 KP |
1033 | |
1034 | ret = FcTrue; | |
327a7fd4 | 1035 | bail3: |
ccb3e93b KP |
1036 | if (file && file != file_buf) |
1037 | free (file); | |
1038 | if (name && name != name_buf) | |
1039 | free (name); | |
327a7fd4 | 1040 | bail2: |
24330d27 | 1041 | fclose (f); |
327a7fd4 | 1042 | bail1: |
9dac3c59 | 1043 | FcStrFree (cache_file); |
24330d27 KP |
1044 | bail0: |
1045 | return ret; | |
1046 | } | |
1047 | ||
179c3995 KP |
1048 | /* |
1049 | * return the path from the directory containing 'cache' to 'file' | |
1050 | */ | |
1051 | ||
1052 | static const FcChar8 * | |
1053 | FcFileBaseName (const FcChar8 *cache, const FcChar8 *file) | |
1054 | { | |
1055 | const FcChar8 *cache_slash; | |
1056 | ||
daeed6e0 | 1057 | cache_slash = FcStrLastSlash (cache); |
179c3995 KP |
1058 | if (cache_slash && !strncmp ((const char *) cache, (const char *) file, |
1059 | (cache_slash + 1) - cache)) | |
1060 | return file + ((cache_slash + 1) - cache); | |
1061 | return file; | |
1062 | } | |
1063 | ||
24330d27 | 1064 | FcBool |
327a7fd4 | 1065 | FcDirCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir) |
24330d27 | 1066 | { |
327a7fd4 | 1067 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
24330d27 KP |
1068 | FcPattern *font; |
1069 | FILE *f; | |
ccb3e93b KP |
1070 | FcChar8 *name; |
1071 | const FcChar8 *file, *base; | |
24330d27 KP |
1072 | int n; |
1073 | int id; | |
1074 | FcBool ret; | |
179c3995 | 1075 | FcStrList *list; |
24330d27 | 1076 | |
327a7fd4 KP |
1077 | if (!cache_file) |
1078 | goto bail0; | |
24330d27 | 1079 | if (FcDebug () & FC_DBG_CACHE) |
327a7fd4 | 1080 | printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file); |
24330d27 | 1081 | |
ccb3e93b | 1082 | f = fopen ((char *) cache_file, "w"); |
24330d27 KP |
1083 | if (!f) |
1084 | { | |
1085 | if (FcDebug () & FC_DBG_CACHE) | |
1086 | printf (" can't create \"%s\"\n", cache_file); | |
327a7fd4 | 1087 | goto bail1; |
24330d27 | 1088 | } |
179c3995 KP |
1089 | |
1090 | list = FcStrListCreate (dirs); | |
1091 | if (!list) | |
327a7fd4 | 1092 | goto bail2; |
179c3995 KP |
1093 | |
1094 | while ((dir = FcStrListNext (list))) | |
1095 | { | |
1096 | base = FcFileBaseName (cache_file, dir); | |
327a7fd4 KP |
1097 | if (!FcCacheWriteString (f, base)) |
1098 | goto bail3; | |
fa244f3d | 1099 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
1100 | goto bail3; |
1101 | if (!FcCacheWriteInt (f, 0)) | |
1102 | goto bail3; | |
fa244f3d | 1103 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
1104 | goto bail3; |
1105 | if (!FcCacheWriteString (f, FC_FONT_FILE_DIR)) | |
1106 | goto bail3; | |
fa244f3d | 1107 | if (PUTC ('\n', f) == EOF) |
327a7fd4 | 1108 | goto bail3; |
179c3995 KP |
1109 | } |
1110 | ||
24330d27 KP |
1111 | for (n = 0; n < set->nfont; n++) |
1112 | { | |
1113 | font = set->fonts[n]; | |
aae6f7d4 | 1114 | if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch) |
327a7fd4 | 1115 | goto bail3; |
179c3995 | 1116 | base = FcFileBaseName (cache_file, file); |
24330d27 | 1117 | if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch) |
327a7fd4 | 1118 | goto bail3; |
24330d27 KP |
1119 | if (FcDebug () & FC_DBG_CACHEV) |
1120 | printf (" write file \"%s\"\n", base); | |
327a7fd4 KP |
1121 | if (!FcCacheWriteString (f, base)) |
1122 | goto bail3; | |
fa244f3d | 1123 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
1124 | goto bail3; |
1125 | if (!FcCacheWriteInt (f, id)) | |
1126 | goto bail3; | |
fa244f3d | 1127 | if (PUTC (' ', f) == EOF) |
327a7fd4 | 1128 | goto bail3; |
24330d27 KP |
1129 | name = FcNameUnparse (font); |
1130 | if (!name) | |
327a7fd4 KP |
1131 | goto bail3; |
1132 | ret = FcCacheWriteString (f, name); | |
9dac3c59 | 1133 | FcStrFree (name); |
24330d27 | 1134 | if (!ret) |
327a7fd4 | 1135 | goto bail3; |
fa244f3d | 1136 | if (PUTC ('\n', f) == EOF) |
327a7fd4 | 1137 | goto bail3; |
24330d27 | 1138 | } |
179c3995 KP |
1139 | |
1140 | FcStrListDone (list); | |
1141 | ||
24330d27 | 1142 | if (fclose (f) == EOF) |
327a7fd4 | 1143 | goto bail1; |
24330d27 | 1144 | |
9dac3c59 | 1145 | FcStrFree (cache_file); |
327a7fd4 | 1146 | |
24330d27 KP |
1147 | if (FcDebug () & FC_DBG_CACHE) |
1148 | printf (" cache written\n"); | |
1149 | return FcTrue; | |
1150 | ||
327a7fd4 | 1151 | bail3: |
179c3995 | 1152 | FcStrListDone (list); |
327a7fd4 | 1153 | bail2: |
24330d27 | 1154 | fclose (f); |
327a7fd4 | 1155 | bail1: |
ccb3e93b | 1156 | unlink ((char *) cache_file); |
9dac3c59 | 1157 | FcStrFree (cache_file); |
327a7fd4 | 1158 | bail0: |
24330d27 KP |
1159 | return FcFalse; |
1160 | } |