]>
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 | ||
212c9f43 | 31 | #define PAGESIZE 8192 |
fa244f3d | 32 | |
212c9f43 | 33 | static FcBool force; |
24330d27 | 34 | |
ccb3e93b | 35 | static FcChar8 * |
212c9f43 | 36 | FcCacheReadString (int fd, FcChar8 *dest, int len) |
24330d27 | 37 | { |
212c9f43 | 38 | FcChar8 c; |
ccb3e93b | 39 | FcBool escape; |
ccb3e93b KP |
40 | int size; |
41 | int i; | |
24330d27 | 42 | |
24330d27 KP |
43 | if (len == 0) |
44 | return FcFalse; | |
45 | ||
ccb3e93b KP |
46 | size = len; |
47 | i = 0; | |
24330d27 | 48 | escape = FcFalse; |
212c9f43 | 49 | while (read (fd, &c, 1) == 1) |
24330d27 KP |
50 | { |
51 | if (!escape) | |
52 | { | |
53 | switch (c) { | |
54 | case '"': | |
ccb3e93b KP |
55 | c = '\0'; |
56 | break; | |
24330d27 KP |
57 | case '\\': |
58 | escape = FcTrue; | |
59 | continue; | |
60 | } | |
61 | } | |
ccb3e93b KP |
62 | if (i == size) |
63 | { | |
212c9f43 PL |
64 | dest[i++] = 0; |
65 | return dest; | |
ccb3e93b | 66 | } |
212c9f43 | 67 | dest[i++] = c; |
ccb3e93b | 68 | if (c == '\0') |
212c9f43 | 69 | return dest; |
24330d27 KP |
70 | escape = FcFalse; |
71 | } | |
ccb3e93b | 72 | return 0; |
24330d27 KP |
73 | } |
74 | ||
75 | static FcBool | |
212c9f43 | 76 | FcCacheWriteString (int fd, const FcChar8 *chars) |
327a7fd4 | 77 | { |
212c9f43 | 78 | if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1) |
327a7fd4 | 79 | return FcFalse; |
327a7fd4 KP |
80 | return FcTrue; |
81 | } | |
82 | ||
212c9f43 | 83 | #if 0 |
327a7fd4 KP |
84 | /* |
85 | * Verify the saved timestamp for a file | |
86 | */ | |
87 | FcBool | |
a8386abc | 88 | FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info) |
327a7fd4 KP |
89 | { |
90 | struct stat statb; | |
91 | ||
a8386abc | 92 | if (stat ((char *) file, &statb) < 0) |
327a7fd4 KP |
93 | { |
94 | if (FcDebug () & FC_DBG_CACHE) | |
a8386abc | 95 | printf (" file %s missing\n", file); |
327a7fd4 KP |
96 | return FcFalse; |
97 | } | |
98 | if (statb.st_mtime != info->time) | |
99 | { | |
100 | if (FcDebug () & FC_DBG_CACHE) | |
101 | printf (" timestamp mismatch (was %d is %d)\n", | |
102 | (int) info->time, (int) statb.st_mtime); | |
103 | return FcFalse; | |
104 | } | |
105 | return FcTrue; | |
106 | } | |
107 | ||
108 | void | |
109 | FcGlobalCacheReferenced (FcGlobalCache *cache, | |
110 | FcGlobalCacheInfo *info) | |
111 | { | |
112 | if (!info->referenced) | |
113 | { | |
114 | info->referenced = FcTrue; | |
115 | cache->referenced++; | |
116 | if (FcDebug () & FC_DBG_CACHE_REF) | |
117 | printf ("Reference %d %s\n", cache->referenced, info->file); | |
118 | } | |
119 | } | |
120 | ||
121 | /* | |
122 | * Break a path into dir/base elements and compute the base hash | |
123 | * and the dir length. This is shared between the functions | |
124 | * which walk the file caches | |
125 | */ | |
126 | ||
127 | typedef struct _FcFilePathInfo { | |
128 | const FcChar8 *dir; | |
129 | int dir_len; | |
130 | const FcChar8 *base; | |
131 | unsigned int base_hash; | |
132 | } FcFilePathInfo; | |
133 | ||
134 | static FcFilePathInfo | |
135 | FcFilePathInfoGet (const FcChar8 *path) | |
136 | { | |
137 | FcFilePathInfo i; | |
138 | FcChar8 *slash; | |
139 | ||
daeed6e0 | 140 | slash = FcStrLastSlash (path); |
327a7fd4 KP |
141 | if (slash) |
142 | { | |
143 | i.dir = path; | |
144 | i.dir_len = slash - path; | |
145 | if (!i.dir_len) | |
146 | i.dir_len = 1; | |
147 | i.base = slash + 1; | |
148 | } | |
149 | else | |
150 | { | |
151 | i.dir = (const FcChar8 *) "."; | |
152 | i.dir_len = 1; | |
153 | i.base = path; | |
154 | } | |
c8d5753c | 155 | i.base_hash = FcCacheHash (i.base, -1); |
327a7fd4 KP |
156 | return i; |
157 | } | |
158 | ||
327a7fd4 KP |
159 | FcGlobalCache * |
160 | FcGlobalCacheCreate (void) | |
24330d27 | 161 | { |
327a7fd4 KP |
162 | FcGlobalCache *cache; |
163 | int h; | |
24330d27 | 164 | |
327a7fd4 | 165 | cache = malloc (sizeof (FcGlobalCache)); |
24330d27 KP |
166 | if (!cache) |
167 | return 0; | |
9dac3c59 | 168 | FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
327a7fd4 | 169 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 KP |
170 | cache->ents[h] = 0; |
171 | cache->entries = 0; | |
172 | cache->referenced = 0; | |
173 | cache->updated = FcFalse; | |
bb356b68 | 174 | cache->broken = FcFalse; |
24330d27 KP |
175 | return cache; |
176 | } | |
177 | ||
178 | void | |
327a7fd4 | 179 | FcGlobalCacheDestroy (FcGlobalCache *cache) |
24330d27 | 180 | { |
327a7fd4 KP |
181 | FcGlobalCacheDir *d, *next; |
182 | int h; | |
24330d27 | 183 | |
327a7fd4 | 184 | for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) |
24330d27 | 185 | { |
327a7fd4 | 186 | for (d = cache->ents[h]; d; d = next) |
24330d27 | 187 | { |
327a7fd4 KP |
188 | next = d->next; |
189 | FcGlobalCacheDirDestroy (d); | |
24330d27 KP |
190 | } |
191 | } | |
9dac3c59 | 192 | FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache)); |
24330d27 KP |
193 | free (cache); |
194 | } | |
195 | ||
196 | void | |
327a7fd4 KP |
197 | FcGlobalCacheLoad (FcGlobalCache *cache, |
198 | const FcChar8 *cache_file) | |
24330d27 | 199 | { |
327a7fd4 KP |
200 | FILE *f; |
201 | FcChar8 file_buf[8192], *file; | |
202 | int id; | |
203 | time_t time; | |
204 | FcChar8 name_buf[8192], *name; | |
205 | FcGlobalCacheInfo *info; | |
24330d27 | 206 | |
ccb3e93b | 207 | f = fopen ((char *) cache_file, "r"); |
24330d27 KP |
208 | if (!f) |
209 | return; | |
210 | ||
211 | cache->updated = FcFalse; | |
ccb3e93b KP |
212 | file = 0; |
213 | name = 0; | |
327a7fd4 KP |
214 | while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) && |
215 | FcCacheReadInt (f, &id) && | |
216 | FcCacheReadTime (f, &time) && | |
217 | (name = FcCacheReadString (f, name_buf, sizeof (name_buf)))) | |
24330d27 | 218 | { |
327a7fd4 KP |
219 | if (FcDebug () & FC_DBG_CACHEV) |
220 | printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name); | |
221 | if (!FcStrCmp (name, FC_FONT_FILE_DIR)) | |
c4ab52dc | 222 | info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue); |
327a7fd4 KP |
223 | else |
224 | info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse); | |
225 | if (!info) | |
226 | cache->broken = FcTrue; | |
227 | else | |
228 | cache->entries++; | |
229 | if (FcDebug () & FC_DBG_CACHE_REF) | |
230 | printf ("FcGlobalCacheLoad entry %d %s\n", | |
231 | cache->entries, file); | |
ccb3e93b KP |
232 | if (file != file_buf) |
233 | free (file); | |
234 | if (name != name_buf) | |
235 | free (name); | |
236 | file = 0; | |
237 | name = 0; | |
24330d27 | 238 | } |
ccb3e93b KP |
239 | if (file && file != file_buf) |
240 | free (file); | |
241 | if (name && name != name_buf) | |
242 | free (name); | |
24330d27 KP |
243 | fclose (f); |
244 | } | |
245 | ||
246 | FcBool | |
327a7fd4 KP |
247 | FcGlobalCacheUpdate (FcGlobalCache *cache, |
248 | const FcChar8 *file, | |
249 | int id, | |
250 | const FcChar8 *name) | |
24330d27 | 251 | { |
327a7fd4 KP |
252 | const FcChar8 *match; |
253 | struct stat statb; | |
254 | FcGlobalCacheInfo *info; | |
24330d27 KP |
255 | |
256 | match = file; | |
257 | ||
ccb3e93b | 258 | if (stat ((char *) file, &statb) < 0) |
24330d27 | 259 | return FcFalse; |
327a7fd4 KP |
260 | if (S_ISDIR (statb.st_mode)) |
261 | info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime, | |
c4ab52dc | 262 | FcTrue, FcTrue); |
327a7fd4 KP |
263 | else |
264 | info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime, | |
265 | name, FcTrue); | |
266 | if (info) | |
24330d27 | 267 | { |
327a7fd4 KP |
268 | FcGlobalCacheReferenced (cache, info); |
269 | cache->updated = FcTrue; | |
24330d27 | 270 | } |
327a7fd4 KP |
271 | else |
272 | cache->broken = FcTrue; | |
273 | return info != 0; | |
24330d27 KP |
274 | } |
275 | ||
276 | FcBool | |
327a7fd4 KP |
277 | FcGlobalCacheSave (FcGlobalCache *cache, |
278 | const FcChar8 *cache_file) | |
24330d27 | 279 | { |
327a7fd4 KP |
280 | FILE *f; |
281 | int dir_hash, file_hash; | |
282 | FcGlobalCacheDir *dir; | |
283 | FcGlobalCacheFile *file; | |
284 | FcAtomic *atomic; | |
24330d27 KP |
285 | |
286 | if (!cache->updated && cache->referenced == cache->entries) | |
287 | return FcTrue; | |
288 | ||
327a7fd4 KP |
289 | if (cache->broken) |
290 | return FcFalse; | |
291 | ||
daeed6e0 | 292 | #if defined (HAVE_GETUID) && defined (HAVE_GETEUID) |
a391da8f KP |
293 | /* Set-UID programs can't safely update the cache */ |
294 | if (getuid () != geteuid ()) | |
295 | return FcFalse; | |
daeed6e0 | 296 | #endif |
a391da8f | 297 | |
134f6011 KP |
298 | atomic = FcAtomicCreate (cache_file); |
299 | if (!atomic) | |
24330d27 | 300 | goto bail0; |
134f6011 | 301 | if (!FcAtomicLock (atomic)) |
24330d27 | 302 | goto bail1; |
134f6011 | 303 | f = fopen ((char *) FcAtomicNewFile(atomic), "w"); |
24330d27 KP |
304 | if (!f) |
305 | goto bail2; | |
306 | ||
327a7fd4 | 307 | for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++) |
24330d27 | 308 | { |
327a7fd4 | 309 | for (dir = cache->ents[dir_hash]; dir; dir = dir->next) |
24330d27 | 310 | { |
327a7fd4 | 311 | if (!dir->info.referenced) |
24330d27 | 312 | continue; |
327a7fd4 | 313 | if (!FcCacheWriteString (f, dir->info.file)) |
24330d27 | 314 | goto bail4; |
fa244f3d | 315 | if (PUTC (' ', f) == EOF) |
24330d27 | 316 | goto bail4; |
327a7fd4 | 317 | if (!FcCacheWriteInt (f, 0)) |
24330d27 | 318 | goto bail4; |
fa244f3d | 319 | if (PUTC (' ', f) == EOF) |
24330d27 | 320 | goto bail4; |
327a7fd4 | 321 | if (!FcCacheWriteTime (f, dir->info.time)) |
24330d27 | 322 | goto bail4; |
fa244f3d | 323 | if (PUTC (' ', f) == EOF) |
24330d27 | 324 | goto bail4; |
327a7fd4 | 325 | if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR)) |
24330d27 | 326 | goto bail4; |
fa244f3d | 327 | if (PUTC ('\n', f) == EOF) |
24330d27 | 328 | goto bail4; |
327a7fd4 KP |
329 | |
330 | for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++) | |
331 | { | |
332 | for (file = dir->ents[file_hash]; file; file = file->next) | |
333 | { | |
334 | if (!file->info.referenced) | |
335 | continue; | |
336 | if (!FcCacheWritePath (f, dir->info.file, file->info.file)) | |
337 | goto bail4; | |
fa244f3d | 338 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
339 | goto bail4; |
340 | if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id)) | |
341 | goto bail4; | |
fa244f3d | 342 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
343 | goto bail4; |
344 | if (!FcCacheWriteTime (f, file->info.time)) | |
345 | goto bail4; | |
fa244f3d | 346 | if (PUTC (' ', f) == EOF) |
327a7fd4 KP |
347 | goto bail4; |
348 | if (!FcCacheWriteString (f, file->name)) | |
349 | goto bail4; | |
fa244f3d | 350 | if (PUTC ('\n', f) == EOF) |
327a7fd4 KP |
351 | goto bail4; |
352 | } | |
353 | } | |
24330d27 KP |
354 | } |
355 | } | |
356 | ||
357 | if (fclose (f) == EOF) | |
358 | goto bail3; | |
359 | ||
134f6011 | 360 | if (!FcAtomicReplaceOrig (atomic)) |
24330d27 KP |
361 | goto bail3; |
362 | ||
134f6011 KP |
363 | FcAtomicUnlock (atomic); |
364 | FcAtomicDestroy (atomic); | |
365 | ||
24330d27 KP |
366 | cache->updated = FcFalse; |
367 | return FcTrue; | |
368 | ||
369 | bail4: | |
370 | fclose (f); | |
371 | bail3: | |
134f6011 | 372 | FcAtomicDeleteNew (atomic); |
24330d27 | 373 | bail2: |
134f6011 | 374 | FcAtomicUnlock (atomic); |
24330d27 | 375 | bail1: |
134f6011 | 376 | FcAtomicDestroy (atomic); |
24330d27 KP |
377 | bail0: |
378 | return FcFalse; | |
379 | } | |
212c9f43 | 380 | #endif |
24330d27 | 381 | |
212c9f43 PL |
382 | /* |
383 | * Find the next presumably-mmapable offset after the current file | |
384 | * pointer. | |
385 | */ | |
4262e0b3 PL |
386 | static int |
387 | FcCacheNextOffset(off_t w) | |
179c3995 | 388 | { |
212c9f43 PL |
389 | if (w % PAGESIZE == 0) |
390 | return w; | |
391 | else | |
392 | return ((w / PAGESIZE)+1)*PAGESIZE; | |
179c3995 KP |
393 | } |
394 | ||
212c9f43 PL |
395 | /* get the current arch name */ |
396 | /* caller is responsible for freeing returned pointer */ | |
397 | static char * | |
398 | FcCacheGetCurrentArch (void) | |
399 | { | |
400 | struct utsname b; | |
401 | char * current_arch_machine_name; | |
402 | ||
403 | if (uname(&b) == -1) | |
404 | return FcFalse; | |
405 | current_arch_machine_name = strdup(b.machine); | |
406 | /* if (getenv ("FAKE_ARCH")) // testing purposes | |
407 | current_arch_machine_name = strdup(getenv("FAKE_ARCH")); */ | |
408 | return current_arch_machine_name; | |
409 | } | |
410 | ||
411 | /* return the address of the segment for the provided arch, | |
412 | * or -1 if arch not found */ | |
413 | static off_t | |
414 | FcCacheSkipToArch (int fd, const char * arch) | |
415 | { | |
416 | char candidate_arch_machine_name[64], bytes_to_skip[7]; | |
417 | off_t current_arch_start = 0; | |
418 | ||
419 | /* skip arches that are not the current arch */ | |
420 | while (1) | |
421 | { | |
422 | long bs; | |
423 | ||
424 | lseek (fd, current_arch_start, SEEK_SET); | |
425 | if (FcCacheReadString (fd, candidate_arch_machine_name, | |
426 | sizeof (candidate_arch_machine_name)) == 0) | |
427 | break; | |
428 | if (FcCacheReadString (fd, bytes_to_skip, 7) == 0) | |
429 | break; | |
430 | bs = a64l(bytes_to_skip); | |
431 | if (bs == 0) | |
432 | break; | |
433 | ||
434 | if (strcmp (candidate_arch_machine_name, arch)==0) | |
435 | break; | |
436 | current_arch_start += bs; | |
437 | } | |
438 | ||
439 | if (strcmp (candidate_arch_machine_name, arch)!=0) | |
440 | return -1; | |
441 | ||
442 | return current_arch_start; | |
443 | } | |
444 | ||
445 | /* Cuts out the segment at the file pointer (moves everything else | |
446 | * down to cover it), and leaves the file pointer at the end of the | |
447 | * file. */ | |
448 | #define BUF_SIZE 8192 | |
449 | ||
450 | static FcBool | |
451 | FcCacheMoveDown (int fd, off_t start) | |
452 | { | |
453 | char * buf = malloc (BUF_SIZE); | |
454 | char candidate_arch_machine_name[64], bytes_to_skip[7]; | |
4262e0b3 | 455 | long bs; |
212c9f43 PL |
456 | int c, bytes_skipped; |
457 | ||
458 | if (!buf) | |
459 | return FcFalse; | |
460 | ||
461 | lseek (fd, start, SEEK_SET); | |
462 | if (FcCacheReadString (fd, candidate_arch_machine_name, | |
463 | sizeof (candidate_arch_machine_name)) == 0) | |
464 | goto done; | |
465 | if (FcCacheReadString (fd, bytes_to_skip, 7) == 0) | |
466 | goto done; | |
467 | ||
468 | bs = a64l(bytes_to_skip); | |
469 | if (bs == 0) | |
470 | goto done; | |
471 | ||
472 | bytes_skipped = 0; | |
473 | do | |
474 | { | |
475 | lseek (fd, start+bs+bytes_skipped, SEEK_SET); | |
476 | if ((c = read (fd, buf, BUF_SIZE)) <= 0) | |
477 | break; | |
478 | lseek (fd, start+bytes_skipped, SEEK_SET); | |
479 | if (write (fd, buf, c) < 0) | |
480 | goto bail; | |
481 | bytes_skipped += c; | |
482 | } | |
483 | while (c > 0); | |
484 | lseek (fd, start+bytes_skipped, SEEK_SET); | |
485 | ||
486 | done: | |
487 | free (buf); | |
488 | return FcTrue; | |
489 | ||
490 | bail: | |
491 | free (buf); | |
492 | return FcFalse; | |
493 | } | |
494 | ||
4262e0b3 PL |
495 | static int |
496 | FcCacheReadDirs (FcStrList *list, FcFontSet * set) | |
497 | { | |
498 | DIR *d; | |
499 | struct dirent *e; | |
500 | int ret = 0; | |
501 | FcChar8 *dir; | |
502 | FcChar8 *file, *base; | |
503 | FcStrSet *subdirs; | |
504 | FcStrList *sublist; | |
505 | struct stat statb; | |
506 | ||
507 | /* | |
508 | * Now scan all of the directories into separate databases | |
509 | * and write out the results | |
510 | */ | |
511 | while ((dir = FcStrListNext (list))) | |
512 | { | |
513 | /* freed below */ | |
514 | file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1); | |
515 | if (!file) | |
516 | return FcFalse; | |
517 | ||
518 | strcpy ((char *) file, (char *) dir); | |
519 | strcat ((char *) file, "/"); | |
520 | base = file + strlen ((char *) file); | |
521 | ||
522 | subdirs = FcStrSetCreate (); | |
523 | if (!subdirs) | |
524 | { | |
525 | fprintf (stderr, "Can't create directory set\n"); | |
526 | ret++; | |
527 | free (file); | |
528 | continue; | |
529 | } | |
530 | ||
531 | if (access ((char *) dir, X_OK) < 0) | |
532 | { | |
533 | switch (errno) { | |
534 | case ENOENT: | |
535 | case ENOTDIR: | |
536 | case EACCES: | |
537 | break; | |
538 | default: | |
539 | fprintf (stderr, "\"%s\": ", dir); | |
540 | perror (""); | |
541 | ret++; | |
542 | } | |
543 | FcStrSetDestroy (subdirs); | |
544 | free (file); | |
545 | continue; | |
546 | } | |
547 | if (stat ((char *) dir, &statb) == -1) | |
548 | { | |
549 | fprintf (stderr, "\"%s\": ", dir); | |
550 | perror (""); | |
551 | FcStrSetDestroy (subdirs); | |
552 | ret++; | |
553 | free (file); | |
554 | continue; | |
555 | } | |
556 | if (!S_ISDIR (statb.st_mode)) | |
557 | { | |
558 | fprintf (stderr, "\"%s\": not a directory, skipping\n", dir); | |
559 | FcStrSetDestroy (subdirs); | |
560 | free (file); | |
561 | continue; | |
562 | } | |
563 | d = opendir ((char *) dir); | |
564 | if (!d) | |
565 | { | |
566 | FcStrSetDestroy (subdirs); | |
567 | free (file); | |
568 | continue; | |
569 | } | |
570 | while ((e = readdir (d))) | |
571 | { | |
572 | if (e->d_name[0] != '.' && strlen (e->d_name) < FC_MAX_FILE_LEN) | |
573 | { | |
574 | strcpy ((char *) base, (char *) e->d_name); | |
575 | if (FcFileIsDir (file) && !FcStrSetAdd (subdirs, file)) | |
576 | ret++; | |
577 | } | |
578 | } | |
579 | closedir (d); | |
580 | if (1 || FcDirCacheValid (dir)) | |
581 | { | |
582 | FcDirCacheRead (set, dir); | |
583 | } | |
584 | else | |
585 | { | |
586 | ret++; | |
587 | #if 0 // (implement per-dir loading) | |
588 | if (verbose) | |
589 | printf ("caching, %d fonts, %d dirs\n", | |
590 | set->nfont, nsubdirs (subdirs)); | |
591 | ||
592 | if (!FcDirSave (set, dir)) | |
593 | { | |
594 | fprintf (stderr, "Can't save cache in \"%s\"\n", dir); | |
595 | ret++; | |
596 | } | |
597 | #endif | |
598 | } | |
599 | sublist = FcStrListCreate (subdirs); | |
600 | FcStrSetDestroy (subdirs); | |
601 | if (!sublist) | |
602 | { | |
603 | fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir); | |
604 | ret++; | |
605 | free (file); | |
606 | continue; | |
607 | } | |
608 | ret += FcCacheReadDirs (sublist, set); | |
609 | free (file); | |
610 | } | |
611 | FcStrListDone (list); | |
612 | return ret; | |
613 | } | |
614 | ||
615 | FcFontSet * | |
616 | FcCacheRead (FcConfig *config) | |
617 | { | |
618 | FcFontSet * s = FcFontSetCreate(); | |
619 | if (!s) | |
620 | return 0; | |
621 | ||
622 | if (force) | |
623 | goto bail; | |
624 | ||
625 | if (FcCacheReadDirs (FcConfigGetConfigDirs (config), s)) | |
626 | goto bail; | |
627 | ||
628 | return s; | |
629 | ||
630 | bail: | |
631 | FcFontSetDestroy (s); | |
632 | return 0; | |
633 | } | |
634 | ||
212c9f43 PL |
635 | /* read serialized state from the cache file */ |
636 | FcBool | |
4262e0b3 | 637 | FcDirCacheRead (FcFontSet * set, const FcChar8 *dir) |
212c9f43 | 638 | { |
4262e0b3 PL |
639 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
640 | int fd; | |
212c9f43 | 641 | FcCache metadata; |
4262e0b3 | 642 | void * current_dir_block; |
212c9f43 PL |
643 | char * current_arch_machine_name; |
644 | char candidate_arch_machine_name[64], bytes_in_block[7]; | |
645 | off_t current_arch_start = 0; | |
646 | ||
647 | if (force) | |
4262e0b3 PL |
648 | goto bail; |
649 | if (!cache_file) | |
650 | goto bail; | |
212c9f43 | 651 | |
4262e0b3 PL |
652 | current_arch_machine_name = FcCacheGetCurrentArch(); |
653 | fd = open(cache_file, O_RDONLY); | |
212c9f43 | 654 | if (fd == -1) |
4262e0b3 | 655 | goto bail0; |
212c9f43 | 656 | |
212c9f43 PL |
657 | current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); |
658 | if (current_arch_start < 0) | |
4262e0b3 | 659 | goto bail1; |
212c9f43 PL |
660 | |
661 | lseek (fd, current_arch_start, SEEK_SET); | |
662 | if (FcCacheReadString (fd, candidate_arch_machine_name, | |
663 | sizeof (candidate_arch_machine_name)) == 0) | |
4262e0b3 | 664 | goto bail1; |
212c9f43 | 665 | if (FcCacheReadString (fd, bytes_in_block, 7) == 0) |
4262e0b3 | 666 | goto bail1; |
212c9f43 PL |
667 | |
668 | // sanity check for endianness issues | |
669 | read(fd, &metadata, sizeof(FcCache)); | |
670 | if (metadata.magic != FC_CACHE_MAGIC) | |
4262e0b3 | 671 | goto bail1; |
212c9f43 | 672 | |
4262e0b3 PL |
673 | if (metadata.count) |
674 | { | |
675 | off_t pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR)); | |
676 | current_dir_block = mmap (0, metadata.count, | |
677 | PROT_READ, MAP_SHARED, fd, pos); | |
678 | if (current_dir_block == MAP_FAILED) | |
679 | perror(""); | |
680 | ||
681 | if (!FcFontSetUnserialize (metadata, set, current_dir_block)) | |
682 | goto bail1; | |
683 | } | |
684 | ||
212c9f43 PL |
685 | close(fd); |
686 | free (current_arch_machine_name); | |
4262e0b3 | 687 | free (cache_file); |
212c9f43 PL |
688 | return FcTrue; |
689 | ||
690 | bail1: | |
212c9f43 | 691 | close(fd); |
4262e0b3 | 692 | bail0: |
212c9f43 | 693 | free (current_arch_machine_name); |
4262e0b3 PL |
694 | bail: |
695 | free (cache_file); | |
212c9f43 PL |
696 | return FcFalse; |
697 | } | |
698 | ||
699 | /* write serialized state to the cache file */ | |
700 | FcBool | |
4262e0b3 | 701 | FcDirCacheWrite (int bank, FcFontSet *set, const FcChar8 *dir) |
212c9f43 | 702 | { |
4262e0b3 PL |
703 | FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE); |
704 | int fd, bytes_to_write, metadata_bytes; | |
212c9f43 PL |
705 | FcCache metadata; |
706 | off_t current_arch_start = 0, truncate_to; | |
707 | char * current_arch_machine_name, bytes_written[7] = "dedbef"; | |
4262e0b3 | 708 | void * current_dir_block, *final_dir_block; |
212c9f43 | 709 | |
4262e0b3 PL |
710 | if (!cache_file) |
711 | goto bail; | |
712 | ||
713 | FcFontSetNewBank(); | |
714 | bytes_to_write = FcFontSetNeededBytes (set); | |
715 | metadata_bytes = FcCacheNextOffset (sizeof (FcCache)); | |
212c9f43 | 716 | |
4262e0b3 PL |
717 | if (!bytes_to_write) |
718 | { | |
719 | unlink (cache_file); | |
720 | free (cache_file); | |
721 | return FcTrue; | |
722 | } | |
723 | ||
724 | current_dir_block = malloc (bytes_to_write); | |
725 | memset (&metadata, 0, sizeof(FcCache)); | |
726 | metadata.count = bytes_to_write; | |
727 | metadata.bank = bank; | |
728 | if (!current_dir_block) | |
729 | goto bail; | |
730 | final_dir_block = FcFontSetDistributeBytes (&metadata, current_dir_block); | |
731 | ||
732 | if (!FcFontSetSerialize (bank, set)) | |
212c9f43 PL |
733 | return FcFalse; |
734 | ||
4262e0b3 PL |
735 | if (FcDebug () & FC_DBG_CACHE) |
736 | printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file); | |
737 | ||
738 | fd = open(cache_file, O_RDWR | O_CREAT, 0666); | |
212c9f43 PL |
739 | if (fd == -1) |
740 | return FcFalse; | |
741 | ||
742 | current_arch_machine_name = FcCacheGetCurrentArch(); | |
743 | current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); | |
744 | if (current_arch_start < 0) | |
4262e0b3 | 745 | current_arch_start = FcCacheNextOffset (lseek(fd, 0, SEEK_END)); |
212c9f43 PL |
746 | |
747 | if (!FcCacheMoveDown(fd, current_arch_start)) | |
4262e0b3 | 748 | goto bail1; |
212c9f43 PL |
749 | |
750 | current_arch_start = lseek(fd, 0, SEEK_CUR); | |
751 | if (ftruncate (fd, current_arch_start) == -1) | |
4262e0b3 | 752 | goto bail1; |
212c9f43 PL |
753 | |
754 | /* reserve space for arch, count & metadata */ | |
755 | if (!FcCacheWriteString (fd, current_arch_machine_name)) | |
4262e0b3 | 756 | goto bail1; |
212c9f43 PL |
757 | |
758 | /* now write the address of the next offset */ | |
4262e0b3 PL |
759 | truncate_to = FcCacheNextOffset(current_arch_start + bytes_to_write + metadata_bytes) - |
760 | current_arch_start; | |
212c9f43 PL |
761 | strcpy (bytes_written, l64a(truncate_to)); |
762 | if (!FcCacheWriteString (fd, bytes_written)) | |
4262e0b3 | 763 | goto bail1; |
212c9f43 | 764 | |
4262e0b3 PL |
765 | metadata.magic = FC_CACHE_MAGIC; |
766 | write (fd, &metadata, sizeof(FcCache)); | |
767 | lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_END)), SEEK_SET); | |
768 | write (fd, current_dir_block, bytes_to_write); | |
769 | ||
770 | /* this actually serves to pad out the cache file */ | |
212c9f43 | 771 | if (ftruncate (fd, current_arch_start + truncate_to) == -1) |
4262e0b3 | 772 | goto bail1; |
212c9f43 PL |
773 | |
774 | close(fd); | |
775 | return FcTrue; | |
776 | ||
4262e0b3 PL |
777 | bail1: |
778 | free (current_dir_block); | |
212c9f43 | 779 | free (current_arch_machine_name); |
4262e0b3 PL |
780 | bail: |
781 | unlink (cache_file); | |
782 | free (cache_file); | |
212c9f43 PL |
783 | return FcFalse; |
784 | } | |
785 | ||
786 | /* if true, ignore the cache file */ | |
787 | void | |
788 | FcCacheForce (FcBool f) | |
789 | { | |
790 | force = f; | |
791 | } | |
4262e0b3 PL |
792 | |
793 | static int banks_ptr = 0, banks_alloc = 0; | |
794 | static int * bankId = 0; | |
795 | ||
796 | int | |
797 | FcCacheBankCount (void) | |
798 | { | |
799 | return banks_ptr; | |
800 | } | |
801 | ||
802 | FcBool | |
803 | FcCacheHaveBank (int bank) | |
804 | { | |
805 | int i; | |
806 | ||
807 | if (bank < FC_BANK_FIRST) | |
808 | return FcTrue; | |
809 | ||
810 | for (i = 0; i < banks_ptr; i++) | |
811 | if (bankId[i] == bank) | |
812 | return FcTrue; | |
813 | ||
814 | return FcFalse; | |
815 | } | |
816 | ||
817 | int | |
818 | FcCacheBankToIndex (int bank) | |
819 | { | |
820 | static int lastBank = FC_BANK_DYNAMIC, lastIndex = -1; | |
821 | int i; | |
822 | int * b; | |
823 | ||
824 | if (bank == lastBank) | |
825 | return lastIndex; | |
826 | ||
827 | for (i = 0; i < banks_ptr; i++) | |
828 | if (bankId[i] == bank) | |
829 | return i; | |
830 | ||
831 | if (banks_ptr <= banks_alloc) | |
832 | { | |
833 | b = realloc (bankId, banks_alloc + 4); | |
834 | if (!b) | |
835 | return -1; | |
836 | ||
837 | bankId = b; | |
838 | banks_alloc += 4; | |
839 | } | |
840 | ||
841 | i = banks_ptr++; | |
842 | bankId[i] = bank; | |
843 | return i; | |
844 | } |