File: | src/fccache.c |
Location: | line 431, column 40 |
Description: | Dereference of undefined pointer value |
1 | /* | ||
2 | * Copyright © 2000 Keith Packard | ||
3 | * Copyright © 2005 Patrick Lam | ||
4 | * | ||
5 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
6 | * documentation for any purpose is hereby granted without fee, provided that | ||
7 | * the above copyright notice appear in all copies and that both that | ||
8 | * copyright notice and this permission notice appear in supporting | ||
9 | * documentation, and that the name of the author(s) not be used in | ||
10 | * advertising or publicity pertaining to distribution of the software without | ||
11 | * specific, written prior permission. The authors make no | ||
12 | * representations about the suitability of this software for any purpose. It | ||
13 | * is provided "as is" without express or implied warranty. | ||
14 | * | ||
15 | * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
16 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
17 | * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
18 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
19 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
20 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
21 | * PERFORMANCE OF THIS SOFTWARE. | ||
22 | */ | ||
23 | #ifdef HAVE_CONFIG_H1 | ||
24 | #include "config.h" | ||
25 | #endif | ||
26 | #include "fcint.h" | ||
27 | #include "fcarch.h" | ||
28 | #include <stdio.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <fcntl.h> | ||
31 | #include <dirent.h> | ||
32 | #include <string.h> | ||
33 | #include <sys/types.h> | ||
34 | #include <time.h> | ||
35 | #include <assert.h> | ||
36 | #if defined(HAVE_MMAP1) || defined(__CYGWIN__) | ||
37 | # include <unistd.h> | ||
38 | # include <sys/mman.h> | ||
39 | #endif | ||
40 | |||
41 | #ifndef O_BINARY0 | ||
42 | #define O_BINARY0 0 | ||
43 | #endif | ||
44 | |||
45 | |||
46 | struct MD5Context { | ||
47 | FcChar32 buf[4]; | ||
48 | FcChar32 bits[2]; | ||
49 | unsigned char in[64]; | ||
50 | }; | ||
51 | |||
52 | static void MD5Init(struct MD5Context *ctx); | ||
53 | static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len); | ||
54 | static void MD5Final(unsigned char digest[16], struct MD5Context *ctx); | ||
55 | static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]); | ||
56 | |||
57 | #define CACHEBASE_LEN(1 + 32 + 1 + sizeof ("be" "32d8") + sizeof (".cache-" "3")) (1 + 32 + 1 + sizeof (FC_ARCHITECTURE"be" "32d8") + sizeof (FC_CACHE_SUFFIX".cache-" "3")) | ||
58 | |||
59 | static FcBool | ||
60 | FcCacheIsMmapSafe (int fd) | ||
61 | { | ||
62 | static FcBool is_initialized = FcFalse0; | ||
63 | static FcBool is_env_available = FcFalse0; | ||
64 | static FcBool use_mmap = FcFalse0; | ||
65 | |||
66 | if (!is_initialized) | ||
67 | { | ||
68 | const char *env; | ||
69 | |||
70 | env = getenv ("FONTCONFIG_USE_MMAP"); | ||
71 | if (env) | ||
72 | { | ||
73 | if (FcNameBool ((const FcChar8 *)env, &use_mmap)) | ||
74 | is_env_available = FcTrue1; | ||
75 | } | ||
76 | is_initialized = FcTrue1; | ||
77 | } | ||
78 | if (is_env_available) | ||
79 | return use_mmap; | ||
80 | |||
81 | return FcIsFsMmapSafe (fd); | ||
82 | } | ||
83 | |||
84 | static const char bin2hex[] = { '0', '1', '2', '3', | ||
85 | '4', '5', '6', '7', | ||
86 | '8', '9', 'a', 'b', | ||
87 | 'c', 'd', 'e', 'f' }; | ||
88 | |||
89 | static FcChar8 * | ||
90 | FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN(1 + 32 + 1 + sizeof ("be" "32d8") + sizeof (".cache-" "3"))]) | ||
91 | { | ||
92 | unsigned char hash[16]; | ||
93 | FcChar8 *hex_hash; | ||
94 | int cnt; | ||
95 | struct MD5Context ctx; | ||
96 | |||
97 | MD5Init (&ctx); | ||
98 | MD5Update (&ctx, (const unsigned char *)dir, strlen ((const char *) dir)); | ||
99 | |||
100 | MD5Final (hash, &ctx); | ||
101 | |||
102 | cache_base[0] = '/'; | ||
103 | hex_hash = cache_base + 1; | ||
104 | for (cnt = 0; cnt < 16; ++cnt) | ||
105 | { | ||
106 | hex_hash[2*cnt ] = bin2hex[hash[cnt] >> 4]; | ||
107 | hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf]; | ||
108 | } | ||
109 | hex_hash[2*cnt] = 0; | ||
110 | strcat ((char *) cache_base, "-" FC_ARCHITECTURE"be" "32d8" FC_CACHE_SUFFIX".cache-" "3"); | ||
111 | |||
112 | return cache_base; | ||
113 | } | ||
114 | |||
115 | FcBool | ||
116 | FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config) | ||
117 | { | ||
118 | FcChar8 *cache_hashed = NULL((void*)0); | ||
119 | FcChar8 cache_base[CACHEBASE_LEN(1 + 32 + 1 + sizeof ("be" "32d8") + sizeof (".cache-" "3"))]; | ||
120 | FcStrList *list; | ||
121 | FcChar8 *cache_dir; | ||
122 | |||
123 | FcDirCacheBasename (dir, cache_base); | ||
124 | |||
125 | list = FcStrListCreateIA__FcStrListCreate (config->cacheDirs); | ||
126 | if (!list) | ||
127 | return FcFalse0; | ||
128 | |||
129 | while ((cache_dir = FcStrListNextIA__FcStrListNext (list))) | ||
130 | { | ||
131 | cache_hashed = FcStrPlusIA__FcStrPlus (cache_dir, cache_base); | ||
132 | if (!cache_hashed) | ||
133 | break; | ||
134 | (void) unlink ((char *) cache_hashed); | ||
135 | FcStrFreeIA__FcStrFree (cache_hashed); | ||
136 | } | ||
137 | FcStrListDoneIA__FcStrListDone (list); | ||
138 | /* return FcFalse if something went wrong */ | ||
139 | if (cache_dir) | ||
140 | return FcFalse0; | ||
141 | return FcTrue1; | ||
142 | } | ||
143 | |||
144 | static int | ||
145 | FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat) | ||
146 | { | ||
147 | int fd; | ||
148 | |||
149 | #ifdef _WIN32 | ||
150 | if (FcStat (cache_file, file_stat) < 0) | ||
151 | return -1; | ||
152 | #endif | ||
153 | fd = open((char *) cache_file, O_RDONLY00 | O_BINARY0); | ||
154 | if (fd < 0) | ||
155 | return fd; | ||
156 | #ifndef _WIN32 | ||
157 | if (fstat (fd, file_stat) < 0) | ||
158 | { | ||
159 | close (fd); | ||
160 | return -1; | ||
161 | } | ||
162 | #endif | ||
163 | return fd; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * Look for a cache file for the specified dir. Attempt | ||
168 | * to use each one we find, stopping when the callback | ||
169 | * indicates success | ||
170 | */ | ||
171 | static FcBool | ||
172 | FcDirCacheProcess (FcConfig *config, const FcChar8 *dir, | ||
173 | FcBool (*callback) (int fd, struct stat *fd_stat, | ||
174 | struct stat *dir_stat, void *closure), | ||
175 | void *closure, FcChar8 **cache_file_ret) | ||
176 | { | ||
177 | int fd = -1; | ||
178 | FcChar8 cache_base[CACHEBASE_LEN(1 + 32 + 1 + sizeof ("be" "32d8") + sizeof (".cache-" "3"))]; | ||
179 | FcStrList *list; | ||
180 | FcChar8 *cache_dir; | ||
181 | struct stat file_stat, dir_stat; | ||
182 | FcBool ret = FcFalse0; | ||
183 | |||
184 | if (FcStatChecksum (dir, &dir_stat) < 0) | ||
185 | return FcFalse0; | ||
186 | |||
187 | FcDirCacheBasename (dir, cache_base); | ||
188 | |||
189 | list = FcStrListCreateIA__FcStrListCreate (config->cacheDirs); | ||
190 | if (!list) | ||
191 | return FcFalse0; | ||
192 | |||
193 | while ((cache_dir = FcStrListNextIA__FcStrListNext (list))) | ||
194 | { | ||
195 | FcChar8 *cache_hashed = FcStrPlusIA__FcStrPlus (cache_dir, cache_base); | ||
196 | if (!cache_hashed) | ||
197 | break; | ||
198 | fd = FcDirCacheOpenFile (cache_hashed, &file_stat); | ||
199 | if (fd >= 0) { | ||
200 | ret = (*callback) (fd, &file_stat, &dir_stat, closure); | ||
201 | close (fd); | ||
202 | if (ret) | ||
203 | { | ||
204 | if (cache_file_ret) | ||
205 | *cache_file_ret = cache_hashed; | ||
206 | else | ||
207 | FcStrFreeIA__FcStrFree (cache_hashed); | ||
208 | break; | ||
209 | } | ||
210 | } | ||
211 | FcStrFreeIA__FcStrFree (cache_hashed); | ||
212 | } | ||
213 | FcStrListDoneIA__FcStrListDone (list); | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | #define FC_CACHE_MIN_MMAP1024 1024 | ||
219 | |||
220 | /* | ||
221 | * Skip list element, make sure the 'next' pointer is the last thing | ||
222 | * in the structure, it will be allocated large enough to hold all | ||
223 | * of the necessary pointers | ||
224 | */ | ||
225 | |||
226 | typedef struct _FcCacheSkip FcCacheSkip; | ||
227 | |||
228 | struct _FcCacheSkip { | ||
229 | FcCache *cache; | ||
230 | int ref; | ||
231 | intptr_t size; | ||
232 | dev_t cache_dev; | ||
233 | ino_t cache_ino; | ||
234 | time_t cache_mtime; | ||
235 | FcCacheSkip *next[1]; | ||
236 | }; | ||
237 | |||
238 | /* | ||
239 | * The head of the skip list; pointers for every possible level | ||
240 | * in the skip list, plus the largest level in the list | ||
241 | */ | ||
242 | |||
243 | #define FC_CACHE_MAX_LEVEL16 16 | ||
244 | |||
245 | static FcCacheSkip *fcCacheChains[FC_CACHE_MAX_LEVEL16]; | ||
246 | static int fcCacheMaxLevel; | ||
247 | |||
248 | |||
249 | static int32_t | ||
250 | FcRandom(void) | ||
251 | { | ||
252 | int32_t result; | ||
253 | |||
254 | #if HAVE_RANDOM_R1 | ||
255 | static struct random_data fcrandbuf; | ||
256 | static char statebuf[256]; | ||
257 | static FcBool initialized = FcFalse0; | ||
258 | |||
259 | if (initialized != FcTrue1) | ||
260 | { | ||
261 | initstate_r(time(NULL((void*)0)), statebuf, 256, &fcrandbuf); | ||
262 | initialized = FcTrue1; | ||
263 | } | ||
264 | |||
265 | random_r(&fcrandbuf, &result); | ||
266 | #elif HAVE_RANDOM1 | ||
267 | static char statebuf[256]; | ||
268 | char *state; | ||
269 | static FcBool initialized = FcFalse0; | ||
270 | |||
271 | if (initialized != FcTrue1) | ||
272 | { | ||
273 | state = initstate(time(NULL((void*)0)), statebuf, 256); | ||
274 | initialized = FcTrue1; | ||
275 | } | ||
276 | else | ||
277 | state = setstate(statebuf); | ||
278 | |||
279 | result = random(); | ||
280 | |||
281 | setstate(state); | ||
282 | #elif HAVE_LRAND481 | ||
283 | result = lrand48(); | ||
284 | #elif HAVE_RAND_R1 | ||
285 | static unsigned int seed = time(NULL((void*)0)); | ||
286 | |||
287 | result = rand_r(&seed); | ||
288 | #elif HAVE_RAND1 | ||
289 | static FcBool initialized = FcFalse0; | ||
290 | |||
291 | if (initialized != FcTrue1) | ||
292 | { | ||
293 | srand(time(NULL((void*)0))); | ||
294 | initialized = FcTrue1; | ||
295 | } | ||
296 | result = rand(); | ||
297 | #else | ||
298 | # error no random number generator function available. | ||
299 | #endif | ||
300 | |||
301 | return result; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Generate a random level number, distributed | ||
306 | * so that each level is 1/4 as likely as the one before | ||
307 | * | ||
308 | * Note that level numbers run 1 <= level <= MAX_LEVEL | ||
309 | */ | ||
310 | static int | ||
311 | random_level (void) | ||
312 | { | ||
313 | /* tricky bit -- each bit is '1' 75% of the time */ | ||
314 | long int bits = FcRandom () | FcRandom (); | ||
315 | int level = 0; | ||
316 | |||
317 | while (++level < FC_CACHE_MAX_LEVEL16) | ||
318 | { | ||
319 | if (bits & 1) | ||
320 | break; | ||
321 | bits >>= 1; | ||
322 | } | ||
323 | return level; | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * Insert cache into the list | ||
328 | */ | ||
329 | static FcBool | ||
330 | FcCacheInsert (FcCache *cache, struct stat *cache_stat) | ||
331 | { | ||
332 | FcCacheSkip **update[FC_CACHE_MAX_LEVEL16]; | ||
333 | FcCacheSkip *s, **next; | ||
334 | int i, level; | ||
335 | |||
336 | /* | ||
337 | * Find links along each chain | ||
338 | */ | ||
339 | next = fcCacheChains; | ||
340 | for (i = fcCacheMaxLevel; --i >= 0; ) | ||
341 | { | ||
342 | for (; (s = next[i]); next = s->next) | ||
343 | if (s->cache > cache) | ||
344 | break; | ||
345 | update[i] = &next[i]; | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * Create new list element | ||
350 | */ | ||
351 | level = random_level (); | ||
352 | if (level > fcCacheMaxLevel) | ||
353 | { | ||
354 | level = fcCacheMaxLevel + 1; | ||
355 | update[fcCacheMaxLevel] = &fcCacheChains[fcCacheMaxLevel]; | ||
356 | fcCacheMaxLevel = level; | ||
357 | } | ||
358 | |||
359 | s = malloc (sizeof (FcCacheSkip) + (level - 1) * sizeof (FcCacheSkip *)); | ||
360 | if (!s) | ||
361 | return FcFalse0; | ||
362 | |||
363 | s->cache = cache; | ||
364 | s->size = cache->size; | ||
365 | s->ref = 1; | ||
366 | if (cache_stat) | ||
367 | { | ||
368 | s->cache_dev = cache_stat->st_dev; | ||
369 | s->cache_ino = cache_stat->st_ino; | ||
370 | s->cache_mtime = cache_stat->st_mtimest_mtim.tv_sec; | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | s->cache_dev = 0; | ||
375 | s->cache_ino = 0; | ||
376 | s->cache_mtime = 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * Insert into all fcCacheChains | ||
381 | */ | ||
382 | for (i = 0; i < level; i++) | ||
383 | { | ||
384 | s->next[i] = *update[i]; | ||
385 | *update[i] = s; | ||
386 | } | ||
387 | return FcTrue1; | ||
388 | } | ||
389 | |||
390 | static FcCacheSkip * | ||
391 | FcCacheFindByAddr (void *object) | ||
392 | { | ||
393 | int i; | ||
394 | FcCacheSkip **next = fcCacheChains; | ||
395 | FcCacheSkip *s; | ||
396 | |||
397 | /* | ||
398 | * Walk chain pointers one level at a time | ||
399 | */ | ||
400 | for (i = fcCacheMaxLevel; --i >= 0;) | ||
401 | while (next[i] && (char *) object >= ((char *) next[i]->cache + next[i]->size)) | ||
402 | next = next[i]->next; | ||
403 | /* | ||
404 | * Here we are | ||
405 | */ | ||
406 | s = next[0]; | ||
407 | if (s && (char *) object < ((char *) s->cache + s->size)) | ||
408 | return s; | ||
409 | return NULL((void*)0); | ||
410 | } | ||
411 | |||
412 | static void | ||
413 | FcCacheRemove (FcCache *cache) | ||
414 | { | ||
415 | FcCacheSkip **update[FC_CACHE_MAX_LEVEL16]; | ||
416 | FcCacheSkip *s, **next; | ||
417 | int i; | ||
418 | |||
419 | /* | ||
420 | * Find links along each chain | ||
421 | */ | ||
422 | next = fcCacheChains; | ||
423 | for (i = fcCacheMaxLevel; --i >= 0; ) | ||
| |||
| |||
424 | { | ||
425 | for (; (s = next[i]); next = s->next) | ||
| |||
426 | if (s->cache >= cache) | ||
427 | break; | ||
428 | update[i] = &next[i]; | ||
429 | } | ||
430 | s = next[0]; | ||
431 | for (i = 0; i < fcCacheMaxLevel && *update[i] == s; i++) | ||
| |||
432 | *update[i] = s->next[i]; | ||
433 | while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL((void*)0)) | ||
434 | fcCacheMaxLevel--; | ||
435 | free (s); | ||
436 | } | ||
437 | |||
438 | static FcCache * | ||
439 | FcCacheFindByStat (struct stat *cache_stat) | ||
440 | { | ||
441 | FcCacheSkip *s; | ||
442 | |||
443 | for (s = fcCacheChains[0]; s; s = s->next[0]) | ||
444 | if (s->cache_dev == cache_stat->st_dev && | ||
445 | s->cache_ino == cache_stat->st_ino && | ||
446 | s->cache_mtime == cache_stat->st_mtimest_mtim.tv_sec) | ||
447 | { | ||
448 | s->ref++; | ||
449 | return s->cache; | ||
450 | } | ||
451 | return NULL((void*)0); | ||
452 | } | ||
453 | |||
454 | static void | ||
455 | FcDirCacheDispose (FcCache *cache) | ||
456 | { | ||
457 | switch (cache->magic) { | ||
458 | case FC_CACHE_MAGIC_ALLOC0xFC02FC05: | ||
459 | free (cache); | ||
460 | break; | ||
461 | case FC_CACHE_MAGIC_MMAP0xFC02FC04: | ||
462 | #if defined(HAVE_MMAP1) || defined(__CYGWIN__) | ||
463 | munmap (cache, cache->size); | ||
464 | #elif defined(_WIN32) | ||
465 | UnmapViewOfFile (cache); | ||
466 | #endif | ||
467 | break; | ||
468 | } | ||
469 | FcCacheRemove (cache); | ||
470 | } | ||
471 | |||
472 | void | ||
473 | FcCacheObjectReference (void *object) | ||
474 | { | ||
475 | FcCacheSkip *skip = FcCacheFindByAddr (object); | ||
476 | |||
477 | if (skip) | ||
478 | skip->ref++; | ||
479 | } | ||
480 | |||
481 | void | ||
482 | FcCacheObjectDereference (void *object) | ||
483 | { | ||
484 | FcCacheSkip *skip = FcCacheFindByAddr (object); | ||
485 | |||
486 | if (skip) | ||
487 | { | ||
488 | skip->ref--; | ||
489 | if (skip->ref <= 0) | ||
490 | FcDirCacheDispose (skip->cache); | ||
491 | } | ||
492 | } | ||
493 | |||
494 | void | ||
495 | FcCacheFini (void) | ||
496 | { | ||
497 | int i; | ||
498 | |||
499 | for (i = 0; i < FC_CACHE_MAX_LEVEL16; i++) | ||
500 | assert (fcCacheChains[i] == NULL)((fcCacheChains[i] == ((void*)0)) ? (void) (0) : __assert_fail ("fcCacheChains[i] == ((void*)0)", "fccache.c", 500, __PRETTY_FUNCTION__ )); | ||
501 | assert (fcCacheMaxLevel == 0)((fcCacheMaxLevel == 0) ? (void) (0) : __assert_fail ("fcCacheMaxLevel == 0" , "fccache.c", 501, __PRETTY_FUNCTION__)); | ||
502 | } | ||
503 | |||
504 | static FcBool | ||
505 | FcCacheTimeValid (FcCache *cache, struct stat *dir_stat) | ||
506 | { | ||
507 | struct stat dir_static; | ||
508 | |||
509 | if (!dir_stat) | ||
510 | { | ||
511 | if (FcStatChecksum (FcCacheDir (cache)((FcChar8 *) ((intptr_t) (cache) + ((cache)->dir))), &dir_static) < 0) | ||
512 | return FcFalse0; | ||
513 | dir_stat = &dir_static; | ||
514 | } | ||
515 | if (FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
516 | printf ("FcCacheTimeValid dir \"%s\" cache checksum %d dir checksum %d\n", | ||
517 | FcCacheDir (cache)((FcChar8 *) ((intptr_t) (cache) + ((cache)->dir))), cache->checksum, (int) dir_stat->st_mtimest_mtim.tv_sec); | ||
518 | return cache->checksum == (int) dir_stat->st_mtimest_mtim.tv_sec; | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Map a cache file into memory | ||
523 | */ | ||
524 | static FcCache * | ||
525 | FcDirCacheMapFd (int fd, struct stat *fd_stat, struct stat *dir_stat) | ||
526 | { | ||
527 | FcCache *cache; | ||
528 | FcBool allocated = FcFalse0; | ||
529 | |||
530 | if (fd_stat->st_size < sizeof (FcCache)) | ||
531 | return NULL((void*)0); | ||
532 | cache = FcCacheFindByStat (fd_stat); | ||
533 | if (cache) | ||
534 | { | ||
535 | if (FcCacheTimeValid (cache, dir_stat)) | ||
536 | return cache; | ||
537 | FcDirCacheUnload (cache); | ||
538 | cache = NULL((void*)0); | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Large cache files are mmap'ed, smaller cache files are read. This | ||
543 | * balances the system cost of mmap against per-process memory usage. | ||
544 | */ | ||
545 | if (FcCacheIsMmapSafe (fd) && fd_stat->st_size >= FC_CACHE_MIN_MMAP1024) | ||
546 | { | ||
547 | #if defined(HAVE_MMAP1) || defined(__CYGWIN__) | ||
548 | cache = mmap (0, fd_stat->st_size, PROT_READ0x1, MAP_SHARED0x001, fd, 0); | ||
549 | #ifdef HAVE_POSIX_FADVISE1 | ||
550 | posix_fadvise (fd, 0, fd_stat->st_size, POSIX_FADV_WILLNEED3); | ||
551 | #endif | ||
552 | if (cache == MAP_FAILED((void *) -1)) | ||
553 | cache = NULL((void*)0); | ||
554 | #elif defined(_WIN32) | ||
555 | { | ||
556 | HANDLE hFileMap; | ||
557 | |||
558 | cache = NULL((void*)0); | ||
559 | hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL((void*)0), | ||
560 | PAGE_READONLY, 0, 0, NULL((void*)0)); | ||
561 | if (hFileMap != NULL((void*)0)) | ||
562 | { | ||
563 | cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, | ||
564 | fd_stat->st_size); | ||
565 | CloseHandle (hFileMap); | ||
566 | } | ||
567 | } | ||
568 | #endif | ||
569 | } | ||
570 | if (!cache) | ||
571 | { | ||
572 | cache = malloc (fd_stat->st_size); | ||
573 | if (!cache) | ||
574 | return NULL((void*)0); | ||
575 | |||
576 | if (read (fd, cache, fd_stat->st_size) != fd_stat->st_size) | ||
577 | { | ||
578 | free (cache); | ||
579 | return NULL((void*)0); | ||
580 | } | ||
581 | allocated = FcTrue1; | ||
582 | } | ||
583 | if (cache->magic != FC_CACHE_MAGIC_MMAP0xFC02FC04 || | ||
584 | cache->version < FC_CACHE_CONTENT_VERSION3 || | ||
585 | cache->size != fd_stat->st_size || | ||
586 | !FcCacheTimeValid (cache, dir_stat) || | ||
587 | !FcCacheInsert (cache, fd_stat)) | ||
588 | { | ||
589 | if (allocated) | ||
590 | free (cache); | ||
591 | else | ||
592 | { | ||
593 | #if defined(HAVE_MMAP1) || defined(__CYGWIN__) | ||
594 | munmap (cache, fd_stat->st_size); | ||
595 | #elif defined(_WIN32) | ||
596 | UnmapViewOfFile (cache); | ||
597 | #endif | ||
598 | } | ||
599 | return NULL((void*)0); | ||
600 | } | ||
601 | |||
602 | /* Mark allocated caches so they're freed rather than unmapped */ | ||
603 | if (allocated) | ||
604 | cache->magic = FC_CACHE_MAGIC_ALLOC0xFC02FC05; | ||
605 | |||
606 | return cache; | ||
607 | } | ||
608 | |||
609 | void | ||
610 | FcDirCacheReference (FcCache *cache, int nref) | ||
611 | { | ||
612 | FcCacheSkip *skip = FcCacheFindByAddr (cache); | ||
613 | |||
614 | if (skip) | ||
615 | skip->ref += nref; | ||
616 | } | ||
617 | |||
618 | void | ||
619 | FcDirCacheUnload (FcCache *cache) | ||
620 | { | ||
621 | FcCacheObjectDereference (cache); | ||
622 | } | ||
623 | |||
624 | static FcBool | ||
625 | FcDirCacheMapHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure) | ||
626 | { | ||
627 | FcCache *cache = FcDirCacheMapFd (fd, fd_stat, dir_stat); | ||
628 | |||
629 | if (!cache) | ||
630 | return FcFalse0; | ||
631 | *((FcCache **) closure) = cache; | ||
632 | return FcTrue1; | ||
633 | } | ||
634 | |||
635 | FcCache * | ||
636 | FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file) | ||
637 | { | ||
638 | FcCache *cache = NULL((void*)0); | ||
639 | |||
640 | if (!FcDirCacheProcess (config, dir, | ||
641 | FcDirCacheMapHelper, | ||
642 | &cache, cache_file)) | ||
643 | return NULL((void*)0); | ||
644 | return cache; | ||
645 | } | ||
646 | |||
647 | FcCache * | ||
648 | FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat) | ||
649 | { | ||
650 | int fd; | ||
651 | FcCache *cache; | ||
652 | struct stat my_file_stat; | ||
653 | |||
654 | if (!file_stat) | ||
655 | file_stat = &my_file_stat; | ||
656 | fd = FcDirCacheOpenFile (cache_file, file_stat); | ||
657 | if (fd < 0) | ||
658 | return NULL((void*)0); | ||
659 | cache = FcDirCacheMapFd (fd, file_stat, NULL((void*)0)); | ||
660 | close (fd); | ||
661 | return cache; | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * Validate a cache file by reading the header and checking | ||
666 | * the magic number and the size field | ||
667 | */ | ||
668 | static FcBool | ||
669 | FcDirCacheValidateHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure) | ||
670 | { | ||
671 | FcBool ret = FcTrue1; | ||
672 | FcCache c; | ||
673 | |||
674 | if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache)) | ||
675 | ret = FcFalse0; | ||
676 | else if (c.magic != FC_CACHE_MAGIC_MMAP0xFC02FC04) | ||
677 | ret = FcFalse0; | ||
678 | else if (c.version < FC_CACHE_CONTENT_VERSION3) | ||
679 | ret = FcFalse0; | ||
680 | else if (fd_stat->st_size != c.size) | ||
681 | ret = FcFalse0; | ||
682 | else if (c.checksum != (int) dir_stat->st_mtimest_mtim.tv_sec) | ||
683 | ret = FcFalse0; | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | static FcBool | ||
688 | FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config) | ||
689 | { | ||
690 | return FcDirCacheProcess (config, dir, | ||
691 | FcDirCacheValidateHelper, | ||
692 | NULL((void*)0), NULL((void*)0)); | ||
693 | } | ||
694 | |||
695 | FcBool | ||
696 | FcDirCacheValid (const FcChar8 *dir) | ||
697 | { | ||
698 | FcConfig *config; | ||
699 | |||
700 | config = FcConfigGetCurrentIA__FcConfigGetCurrent (); | ||
701 | if (!config) | ||
702 | return FcFalse0; | ||
703 | |||
704 | return FcDirCacheValidConfig (dir, config); | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | * Build a cache structure from the given contents | ||
709 | */ | ||
710 | FcCache * | ||
711 | FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcStrSet *dirs) | ||
712 | { | ||
713 | FcSerialize *serialize = FcSerializeCreate (); | ||
714 | FcCache *cache; | ||
715 | int i; | ||
716 | FcChar8 *dir_serialize; | ||
717 | intptr_t *dirs_serialize; | ||
718 | FcFontSet *set_serialize; | ||
719 | |||
720 | if (!serialize) | ||
721 | return NULL((void*)0); | ||
722 | /* | ||
723 | * Space for cache structure | ||
724 | */ | ||
725 | FcSerializeReserve (serialize, sizeof (FcCache)); | ||
726 | /* | ||
727 | * Directory name | ||
728 | */ | ||
729 | if (!FcStrSerializeAlloc (serialize, dir)) | ||
730 | goto bail1; | ||
731 | /* | ||
732 | * Subdirs | ||
733 | */ | ||
734 | FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *)); | ||
735 | for (i = 0; i < dirs->num; i++) | ||
736 | if (!FcStrSerializeAlloc (serialize, dirs->strs[i])) | ||
737 | goto bail1; | ||
738 | |||
739 | /* | ||
740 | * Patterns | ||
741 | */ | ||
742 | if (!FcFontSetSerializeAlloc (serialize, set)) | ||
743 | goto bail1; | ||
744 | |||
745 | /* Serialize layout complete. Now allocate space and fill it */ | ||
746 | cache = malloc (serialize->size); | ||
747 | if (!cache) | ||
748 | goto bail1; | ||
749 | /* shut up valgrind */ | ||
750 | memset (cache, 0, serialize->size); | ||
751 | |||
752 | serialize->linear = cache; | ||
753 | |||
754 | cache->magic = FC_CACHE_MAGIC_ALLOC0xFC02FC05; | ||
755 | cache->version = FC_CACHE_CONTENT_VERSION3; | ||
756 | cache->size = serialize->size; | ||
757 | cache->checksum = (int) dir_stat->st_mtimest_mtim.tv_sec; | ||
758 | |||
759 | /* | ||
760 | * Serialize directory name | ||
761 | */ | ||
762 | dir_serialize = FcStrSerialize (serialize, dir); | ||
763 | if (!dir_serialize) | ||
764 | goto bail2; | ||
765 | cache->dir = FcPtrToOffset (cache, dir_serialize)((intptr_t) (dir_serialize) - (intptr_t) (cache)); | ||
766 | |||
767 | /* | ||
768 | * Serialize sub dirs | ||
769 | */ | ||
770 | dirs_serialize = FcSerializePtr (serialize, dirs); | ||
771 | if (!dirs_serialize) | ||
772 | goto bail2; | ||
773 | cache->dirs = FcPtrToOffset (cache, dirs_serialize)((intptr_t) (dirs_serialize) - (intptr_t) (cache)); | ||
774 | cache->dirs_count = dirs->num; | ||
775 | for (i = 0; i < dirs->num; i++) | ||
776 | { | ||
777 | FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]); | ||
778 | if (!d_serialize) | ||
779 | goto bail2; | ||
780 | dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize)((intptr_t) (d_serialize) - (intptr_t) (dirs_serialize)); | ||
781 | } | ||
782 | |||
783 | /* | ||
784 | * Serialize font set | ||
785 | */ | ||
786 | set_serialize = FcFontSetSerialize (serialize, set); | ||
787 | if (!set_serialize) | ||
788 | goto bail2; | ||
789 | cache->set = FcPtrToOffset (cache, set_serialize)((intptr_t) (set_serialize) - (intptr_t) (cache)); | ||
790 | |||
791 | FcSerializeDestroy (serialize); | ||
792 | |||
793 | FcCacheInsert (cache, NULL((void*)0)); | ||
794 | |||
795 | return cache; | ||
796 | |||
797 | bail2: | ||
798 | free (cache); | ||
799 | bail1: | ||
800 | FcSerializeDestroy (serialize); | ||
801 | return NULL((void*)0); | ||
802 | } | ||
803 | |||
804 | |||
805 | #ifdef _WIN32 | ||
806 | #define mkdir(path,mode) _mkdir(path) | ||
807 | #endif | ||
808 | |||
809 | static FcBool | ||
810 | FcMakeDirectory (const FcChar8 *dir) | ||
811 | { | ||
812 | FcChar8 *parent; | ||
813 | FcBool ret; | ||
814 | |||
815 | if (strlen ((char *) dir) == 0) | ||
816 | return FcFalse0; | ||
817 | |||
818 | parent = FcStrDirnameIA__FcStrDirname (dir); | ||
819 | if (!parent) | ||
820 | return FcFalse0; | ||
821 | if (access ((char *) parent, F_OK0) == 0) | ||
822 | ret = mkdir ((char *) dir, 0755) == 0 && chmod ((char *) dir, 0755) == 0; | ||
823 | else if (access ((char *) parent, F_OK0) == -1) | ||
824 | ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0755) == 0) && chmod ((char *) dir, 0755) == 0; | ||
825 | else | ||
826 | ret = FcFalse0; | ||
827 | FcStrFreeIA__FcStrFree (parent); | ||
828 | return ret; | ||
829 | } | ||
830 | |||
831 | /* write serialized state to the cache file */ | ||
832 | FcBool | ||
833 | FcDirCacheWrite (FcCache *cache, FcConfig *config) | ||
834 | { | ||
835 | FcChar8 *dir = FcCacheDir (cache)((FcChar8 *) ((intptr_t) (cache) + ((cache)->dir))); | ||
836 | FcChar8 cache_base[CACHEBASE_LEN(1 + 32 + 1 + sizeof ("be" "32d8") + sizeof (".cache-" "3"))]; | ||
837 | FcChar8 *cache_hashed; | ||
838 | int fd; | ||
839 | FcAtomic *atomic; | ||
840 | FcStrList *list; | ||
841 | FcChar8 *cache_dir = NULL((void*)0); | ||
842 | FcChar8 *test_dir; | ||
843 | FcCacheSkip *skip; | ||
844 | struct stat cache_stat; | ||
845 | int magic; | ||
846 | int written; | ||
847 | |||
848 | /* | ||
849 | * Write it to the first directory in the list which is writable | ||
850 | */ | ||
851 | |||
852 | list = FcStrListCreateIA__FcStrListCreate (config->cacheDirs); | ||
853 | if (!list) | ||
854 | return FcFalse0; | ||
855 | while ((test_dir = FcStrListNextIA__FcStrListNext (list))) { | ||
856 | if (access ((char *) test_dir, W_OK2|X_OK1) == 0) | ||
857 | { | ||
858 | cache_dir = test_dir; | ||
859 | break; | ||
860 | } | ||
861 | else | ||
862 | { | ||
863 | /* | ||
864 | * If the directory doesn't exist, try to create it | ||
865 | */ | ||
866 | if (access ((char *) test_dir, F_OK0) == -1) { | ||
867 | if (FcMakeDirectory (test_dir)) | ||
868 | { | ||
869 | cache_dir = test_dir; | ||
870 | /* Create CACHEDIR.TAG */ | ||
871 | FcDirCacheCreateTagFile (cache_dir); | ||
872 | break; | ||
873 | } | ||
874 | } | ||
875 | /* | ||
876 | * Otherwise, try making it writable | ||
877 | */ | ||
878 | else if (chmod ((char *) test_dir, 0755) == 0) | ||
879 | { | ||
880 | cache_dir = test_dir; | ||
881 | /* Try to create CACHEDIR.TAG too */ | ||
882 | FcDirCacheCreateTagFile (cache_dir); | ||
883 | break; | ||
884 | } | ||
885 | } | ||
886 | } | ||
887 | FcStrListDoneIA__FcStrListDone (list); | ||
888 | if (!cache_dir) | ||
889 | return FcFalse0; | ||
890 | |||
891 | FcDirCacheBasename (dir, cache_base); | ||
892 | cache_hashed = FcStrPlusIA__FcStrPlus (cache_dir, cache_base); | ||
893 | if (!cache_hashed) | ||
894 | return FcFalse0; | ||
895 | |||
896 | if (FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
897 | printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n", | ||
898 | dir, cache_hashed); | ||
899 | |||
900 | atomic = FcAtomicCreateIA__FcAtomicCreate ((FcChar8 *)cache_hashed); | ||
901 | if (!atomic) | ||
902 | goto bail1; | ||
903 | |||
904 | if (!FcAtomicLockIA__FcAtomicLock (atomic)) | ||
905 | goto bail3; | ||
906 | |||
907 | fd = open((char *)FcAtomicNewFileIA__FcAtomicNewFile (atomic), O_RDWR02 | O_CREAT0100 | O_BINARY0, 0666); | ||
908 | if (fd == -1) | ||
909 | goto bail4; | ||
910 | |||
911 | /* Temporarily switch magic to MMAP while writing to file */ | ||
912 | magic = cache->magic; | ||
913 | if (magic != FC_CACHE_MAGIC_MMAP0xFC02FC04) | ||
914 | cache->magic = FC_CACHE_MAGIC_MMAP0xFC02FC04; | ||
915 | |||
916 | /* | ||
917 | * Write cache contents to file | ||
918 | */ | ||
919 | written = write (fd, cache, cache->size); | ||
920 | |||
921 | /* Switch magic back */ | ||
922 | if (magic != FC_CACHE_MAGIC_MMAP0xFC02FC04) | ||
923 | cache->magic = magic; | ||
924 | |||
925 | if (written != cache->size) | ||
926 | { | ||
927 | perror ("write cache"); | ||
928 | goto bail5; | ||
929 | } | ||
930 | |||
931 | close(fd); | ||
932 | if (!FcAtomicReplaceOrigIA__FcAtomicReplaceOrig(atomic)) | ||
933 | goto bail4; | ||
934 | |||
935 | /* If the file is small, update the cache chain entry such that the | ||
936 | * new cache file is not read again. If it's large, we don't do that | ||
937 | * such that we reload it, using mmap, which is shared across processes. | ||
938 | */ | ||
939 | if (cache->size < FC_CACHE_MIN_MMAP1024 && | ||
940 | (skip = FcCacheFindByAddr (cache)) && | ||
941 | FcStat (cache_hashed, &cache_stat)) | ||
942 | { | ||
943 | skip->cache_dev = cache_stat.st_dev; | ||
944 | skip->cache_ino = cache_stat.st_ino; | ||
945 | skip->cache_mtime = cache_stat.st_mtimest_mtim.tv_sec; | ||
946 | } | ||
947 | |||
948 | FcStrFreeIA__FcStrFree (cache_hashed); | ||
949 | FcAtomicUnlockIA__FcAtomicUnlock (atomic); | ||
950 | FcAtomicDestroyIA__FcAtomicDestroy (atomic); | ||
951 | return FcTrue1; | ||
952 | |||
953 | bail5: | ||
954 | close (fd); | ||
955 | bail4: | ||
956 | FcAtomicUnlockIA__FcAtomicUnlock (atomic); | ||
957 | bail3: | ||
958 | FcAtomicDestroyIA__FcAtomicDestroy (atomic); | ||
959 | bail1: | ||
960 | FcStrFreeIA__FcStrFree (cache_hashed); | ||
961 | return FcFalse0; | ||
962 | } | ||
963 | |||
964 | FcBool | ||
965 | FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose) | ||
966 | { | ||
967 | DIR *d; | ||
968 | struct dirent *ent; | ||
969 | FcChar8 *dir_base; | ||
970 | FcBool ret = FcTrue1; | ||
971 | FcBool remove; | ||
972 | FcCache *cache; | ||
973 | struct stat target_stat; | ||
974 | |||
975 | dir_base = FcStrPlusIA__FcStrPlus (cache_dir, (FcChar8 *) FC_DIR_SEPARATOR_S"/"); | ||
976 | if (!dir_base) | ||
977 | { | ||
978 | fprintf (stderrstderr, "Fontconfig error: %s: out of memory\n", cache_dir); | ||
979 | return FcFalse0; | ||
980 | } | ||
981 | if (access ((char *) cache_dir, W_OK2) != 0) | ||
982 | { | ||
983 | if (verbose || FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
984 | printf ("%s: not cleaning %s cache directory\n", cache_dir, | ||
985 | access ((char *) cache_dir, F_OK0) == 0 ? "unwritable" : "non-existent"); | ||
986 | goto bail0; | ||
987 | } | ||
988 | if (verbose || FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
989 | printf ("%s: cleaning cache directory\n", cache_dir); | ||
990 | d = opendir ((char *) cache_dir); | ||
991 | if (!d) | ||
992 | { | ||
993 | perror ((char *) cache_dir); | ||
994 | ret = FcFalse0; | ||
995 | goto bail0; | ||
996 | } | ||
997 | while ((ent = readdir (d))) | ||
998 | { | ||
999 | FcChar8 *file_name; | ||
1000 | const FcChar8 *target_dir; | ||
1001 | |||
1002 | if (ent->d_name[0] == '.') | ||
1003 | continue; | ||
1004 | /* skip cache files for different architectures and */ | ||
1005 | /* files which are not cache files at all */ | ||
1006 | if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE"be" "32d8" FC_CACHE_SUFFIX".cache-" "3") || | ||
1007 | strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE"be" "32d8" FC_CACHE_SUFFIX".cache-" "3")) | ||
1008 | continue; | ||
1009 | |||
1010 | file_name = FcStrPlusIA__FcStrPlus (dir_base, (FcChar8 *) ent->d_name); | ||
1011 | if (!file_name) | ||
1012 | { | ||
1013 | fprintf (stderrstderr, "Fontconfig error: %s: allocation failure\n", cache_dir); | ||
1014 | ret = FcFalse0; | ||
1015 | break; | ||
1016 | } | ||
1017 | remove = FcFalse0; | ||
1018 | cache = FcDirCacheLoadFile (file_name, NULL((void*)0)); | ||
1019 | if (!cache) | ||
1020 | { | ||
1021 | if (verbose || FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
1022 | printf ("%s: invalid cache file: %s\n", cache_dir, ent->d_name); | ||
1023 | remove = FcTrue1; | ||
1024 | } | ||
1025 | else | ||
1026 | { | ||
1027 | target_dir = FcCacheDir (cache)((FcChar8 *) ((intptr_t) (cache) + ((cache)->dir))); | ||
1028 | if (stat ((char *) target_dir, &target_stat) < 0) | ||
1029 | { | ||
1030 | if (verbose || FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
1031 | printf ("%s: %s: missing directory: %s \n", | ||
1032 | cache_dir, ent->d_name, target_dir); | ||
1033 | remove = FcTrue1; | ||
1034 | } | ||
1035 | } | ||
1036 | if (remove) | ||
1037 | { | ||
1038 | if (unlink ((char *) file_name) < 0) | ||
1039 | { | ||
1040 | perror ((char *) file_name); | ||
1041 | ret = FcFalse0; | ||
1042 | } | ||
1043 | } | ||
1044 | FcDirCacheUnload (cache); | ||
1045 | FcStrFreeIA__FcStrFree (file_name); | ||
1046 | } | ||
1047 | |||
1048 | closedir (d); | ||
1049 | bail0: | ||
1050 | FcStrFreeIA__FcStrFree (dir_base); | ||
1051 | |||
1052 | return ret; | ||
1053 | } | ||
1054 | |||
1055 | /* | ||
1056 | * Hokey little macro trick to permit the definitions of C functions | ||
1057 | * with the same name as CPP macros | ||
1058 | */ | ||
1059 | #define args1(x)(x) (x) | ||
1060 | #define args2(x,y)(x,y) (x,y) | ||
1061 | |||
1062 | const FcChar8 * | ||
1063 | FcCacheDir args1(const FcCache *c)(const FcCache *c) | ||
1064 | { | ||
1065 | return FcCacheDir (c)((FcChar8 *) ((intptr_t) (c) + ((c)->dir))); | ||
1066 | } | ||
1067 | |||
1068 | FcFontSet * | ||
1069 | FcCacheCopySet args1(const FcCache *c)(const FcCache *c) | ||
1070 | { | ||
1071 | FcFontSet *old = FcCacheSet (c)((FcFontSet *) ((intptr_t) (c) + ((c)->set))); | ||
1072 | FcFontSet *new = FcFontSetCreateIA__FcFontSetCreate (); | ||
1073 | int i; | ||
1074 | |||
1075 | if (!new) | ||
1076 | return NULL((void*)0); | ||
1077 | for (i = 0; i < old->nfont; i++) | ||
1078 | { | ||
1079 | FcPattern *font = FcFontSetFont (old, i)(((((intptr_t) ((old)->fonts)) & 1) != 0) ? ((FcPattern *) ((intptr_t) (old) + ((((intptr_t) ((((((intptr_t) ((old)-> fonts)) & 1) != 0) ? ((FcPattern * *) ((intptr_t) (old) + ((((intptr_t) ((old)->fonts)) & ~1)))) : (old)->fonts )[i])) & ~1)))) : old->fonts[i]); | ||
1080 | |||
1081 | FcPatternReferenceIA__FcPatternReference (font); | ||
1082 | if (!FcFontSetAddIA__FcFontSetAdd (new, font)) | ||
1083 | { | ||
1084 | FcFontSetDestroyIA__FcFontSetDestroy (new); | ||
1085 | return NULL((void*)0); | ||
1086 | } | ||
1087 | } | ||
1088 | return new; | ||
1089 | } | ||
1090 | |||
1091 | const FcChar8 * | ||
1092 | FcCacheSubdir args2(const FcCache *c, int i)(const FcCache *c,int i) | ||
1093 | { | ||
1094 | return FcCacheSubdir (c, i)((FcChar8 *) ((intptr_t) (((intptr_t *) ((intptr_t) (c) + ((c )->dirs)))) + (((intptr_t *) ((intptr_t) (c) + ((c)->dirs )))[i]))); | ||
1095 | } | ||
1096 | |||
1097 | int | ||
1098 | FcCacheNumSubdir args1(const FcCache *c)(const FcCache *c) | ||
1099 | { | ||
1100 | return c->dirs_count; | ||
1101 | } | ||
1102 | |||
1103 | int | ||
1104 | FcCacheNumFont args1(const FcCache *c)(const FcCache *c) | ||
1105 | { | ||
1106 | return FcCacheSet(c)((FcFontSet *) ((intptr_t) (c) + ((c)->set)))->nfont; | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * This code implements the MD5 message-digest algorithm. | ||
1111 | * The algorithm is due to Ron Rivest. This code was | ||
1112 | * written by Colin Plumb in 1993, no copyright is claimed. | ||
1113 | * This code is in the public domain; do with it what you wish. | ||
1114 | * | ||
1115 | * Equivalent code is available from RSA Data Security, Inc. | ||
1116 | * This code has been tested against that, and is equivalent, | ||
1117 | * except that you don't need to include two pages of legalese | ||
1118 | * with every copy. | ||
1119 | * | ||
1120 | * To compute the message digest of a chunk of bytes, declare an | ||
1121 | * MD5Context structure, pass it to MD5Init, call MD5Update as | ||
1122 | * needed on buffers full of bytes, and then call MD5Final, which | ||
1123 | * will fill a supplied 16-byte array with the digest. | ||
1124 | */ | ||
1125 | |||
1126 | #ifndef HIGHFIRST | ||
1127 | #define byteReverse(buf, len) /* Nothing */ | ||
1128 | #else | ||
1129 | /* | ||
1130 | * Note: this code is harmless on little-endian machines. | ||
1131 | */ | ||
1132 | void byteReverse(unsigned char *buf, unsigned longs) | ||
1133 | { | ||
1134 | FcChar32 t; | ||
1135 | do { | ||
1136 | t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | | ||
1137 | ((unsigned) buf[1] << 8 | buf[0]); | ||
1138 | *(FcChar32 *) buf = t; | ||
1139 | buf += 4; | ||
1140 | } while (--longs); | ||
1141 | } | ||
1142 | #endif | ||
1143 | |||
1144 | /* | ||
1145 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious | ||
1146 | * initialization constants. | ||
1147 | */ | ||
1148 | static void MD5Init(struct MD5Context *ctx) | ||
1149 | { | ||
1150 | ctx->buf[0] = 0x67452301; | ||
1151 | ctx->buf[1] = 0xefcdab89; | ||
1152 | ctx->buf[2] = 0x98badcfe; | ||
1153 | ctx->buf[3] = 0x10325476; | ||
1154 | |||
1155 | ctx->bits[0] = 0; | ||
1156 | ctx->bits[1] = 0; | ||
1157 | } | ||
1158 | |||
1159 | /* | ||
1160 | * Update context to reflect the concatenation of another buffer full | ||
1161 | * of bytes. | ||
1162 | */ | ||
1163 | static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len) | ||
1164 | { | ||
1165 | FcChar32 t; | ||
1166 | |||
1167 | /* Update bitcount */ | ||
1168 | |||
1169 | t = ctx->bits[0]; | ||
1170 | if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t) | ||
1171 | ctx->bits[1]++; /* Carry from low to high */ | ||
1172 | ctx->bits[1] += len >> 29; | ||
1173 | |||
1174 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ | ||
1175 | |||
1176 | /* Handle any leading odd-sized chunks */ | ||
1177 | |||
1178 | if (t) { | ||
1179 | unsigned char *p = (unsigned char *) ctx->in + t; | ||
1180 | |||
1181 | t = 64 - t; | ||
1182 | if (len < t) { | ||
1183 | memcpy(p, buf, len); | ||
1184 | return; | ||
1185 | } | ||
1186 | memcpy(p, buf, t); | ||
1187 | byteReverse(ctx->in, 16); | ||
1188 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | ||
1189 | buf += t; | ||
1190 | len -= t; | ||
1191 | } | ||
1192 | /* Process data in 64-byte chunks */ | ||
1193 | |||
1194 | while (len >= 64) { | ||
1195 | memcpy(ctx->in, buf, 64); | ||
1196 | byteReverse(ctx->in, 16); | ||
1197 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | ||
1198 | buf += 64; | ||
1199 | len -= 64; | ||
1200 | } | ||
1201 | |||
1202 | /* Handle any remaining bytes of data. */ | ||
1203 | |||
1204 | memcpy(ctx->in, buf, len); | ||
1205 | } | ||
1206 | |||
1207 | /* | ||
1208 | * Final wrapup - pad to 64-byte boundary with the bit pattern | ||
1209 | * 1 0* (64-bit count of bits processed, MSB-first) | ||
1210 | */ | ||
1211 | static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) | ||
1212 | { | ||
1213 | unsigned count; | ||
1214 | unsigned char *p; | ||
1215 | |||
1216 | /* Compute number of bytes mod 64 */ | ||
1217 | count = (ctx->bits[0] >> 3) & 0x3F; | ||
1218 | |||
1219 | /* Set the first char of padding to 0x80. This is safe since there is | ||
1220 | always at least one byte free */ | ||
1221 | p = ctx->in + count; | ||
1222 | *p++ = 0x80; | ||
1223 | |||
1224 | /* Bytes of padding needed to make 64 bytes */ | ||
1225 | count = 64 - 1 - count; | ||
1226 | |||
1227 | /* Pad out to 56 mod 64 */ | ||
1228 | if (count < 8) { | ||
1229 | /* Two lots of padding: Pad the first block to 64 bytes */ | ||
1230 | memset(p, 0, count); | ||
1231 | byteReverse(ctx->in, 16); | ||
1232 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | ||
1233 | |||
1234 | /* Now fill the next block with 56 bytes */ | ||
1235 | memset(ctx->in, 0, 56); | ||
1236 | } else { | ||
1237 | /* Pad block to 56 bytes */ | ||
1238 | memset(p, 0, count - 8); | ||
1239 | } | ||
1240 | byteReverse(ctx->in, 14); | ||
1241 | |||
1242 | /* Append length in bits and transform */ | ||
1243 | ((FcChar32 *) ctx->in)[14] = ctx->bits[0]; | ||
1244 | ((FcChar32 *) ctx->in)[15] = ctx->bits[1]; | ||
1245 | |||
1246 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | ||
1247 | byteReverse((unsigned char *) ctx->buf, 4); | ||
1248 | memcpy(digest, ctx->buf, 16); | ||
1249 | memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ | ||
1250 | } | ||
1251 | |||
1252 | |||
1253 | /* The four core functions - F1 is optimized somewhat */ | ||
1254 | |||
1255 | /* #define F1(x, y, z) (x & y | ~x & z) */ | ||
1256 | #define F1(x, y, z)(z ^ (x & (y ^ z))) (z ^ (x & (y ^ z))) | ||
1257 | #define F2(x, y, z)(y ^ (z & (x ^ y))) F1(z, x, y)(y ^ (z & (x ^ y))) | ||
1258 | #define F3(x, y, z)(x ^ y ^ z) (x ^ y ^ z) | ||
1259 | #define F4(x, y, z)(y ^ (x | ~z)) (y ^ (x | ~z)) | ||
1260 | |||
1261 | /* This is the central step in the MD5 algorithm. */ | ||
1262 | #define MD5STEP(f, w, x, y, z, data, s)( w += f(x, y, z) + data, w = w<<s | w>>(32 -s), w += x ) \ | ||
1263 | ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) | ||
1264 | |||
1265 | /* | ||
1266 | * The core of the MD5 algorithm, this alters an existing MD5 hash to | ||
1267 | * reflect the addition of 16 longwords of new data. MD5Update blocks | ||
1268 | * the data and converts bytes into longwords for this routine. | ||
1269 | */ | ||
1270 | static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]) | ||
1271 | { | ||
1272 | register FcChar32 a, b, c, d; | ||
1273 | |||
1274 | a = buf[0]; | ||
1275 | b = buf[1]; | ||
1276 | c = buf[2]; | ||
1277 | d = buf[3]; | ||
1278 | |||
1279 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7)( a += (d ^ (b & (c ^ d))) + in[0] + 0xd76aa478, a = a<< 7 | a>>(32 -7), a += b ); | ||
1280 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12)( d += (c ^ (a & (b ^ c))) + in[1] + 0xe8c7b756, d = d<< 12 | d>>(32 -12), d += a ); | ||
1281 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17)( c += (b ^ (d & (a ^ b))) + in[2] + 0x242070db, c = c<< 17 | c>>(32 -17), c += d ); | ||
1282 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22)( b += (a ^ (c & (d ^ a))) + in[3] + 0xc1bdceee, b = b<< 22 | b>>(32 -22), b += c ); | ||
1283 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7)( a += (d ^ (b & (c ^ d))) + in[4] + 0xf57c0faf, a = a<< 7 | a>>(32 -7), a += b ); | ||
1284 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12)( d += (c ^ (a & (b ^ c))) + in[5] + 0x4787c62a, d = d<< 12 | d>>(32 -12), d += a ); | ||
1285 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17)( c += (b ^ (d & (a ^ b))) + in[6] + 0xa8304613, c = c<< 17 | c>>(32 -17), c += d ); | ||
1286 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22)( b += (a ^ (c & (d ^ a))) + in[7] + 0xfd469501, b = b<< 22 | b>>(32 -22), b += c ); | ||
1287 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7)( a += (d ^ (b & (c ^ d))) + in[8] + 0x698098d8, a = a<< 7 | a>>(32 -7), a += b ); | ||
1288 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12)( d += (c ^ (a & (b ^ c))) + in[9] + 0x8b44f7af, d = d<< 12 | d>>(32 -12), d += a ); | ||
1289 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17)( c += (b ^ (d & (a ^ b))) + in[10] + 0xffff5bb1, c = c<< 17 | c>>(32 -17), c += d ); | ||
1290 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22)( b += (a ^ (c & (d ^ a))) + in[11] + 0x895cd7be, b = b<< 22 | b>>(32 -22), b += c ); | ||
1291 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7)( a += (d ^ (b & (c ^ d))) + in[12] + 0x6b901122, a = a<< 7 | a>>(32 -7), a += b ); | ||
1292 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12)( d += (c ^ (a & (b ^ c))) + in[13] + 0xfd987193, d = d<< 12 | d>>(32 -12), d += a ); | ||
1293 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17)( c += (b ^ (d & (a ^ b))) + in[14] + 0xa679438e, c = c<< 17 | c>>(32 -17), c += d ); | ||
1294 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22)( b += (a ^ (c & (d ^ a))) + in[15] + 0x49b40821, b = b<< 22 | b>>(32 -22), b += c ); | ||
1295 | |||
1296 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5)( a += (c ^ (d & (b ^ c))) + in[1] + 0xf61e2562, a = a<< 5 | a>>(32 -5), a += b ); | ||
1297 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9)( d += (b ^ (c & (a ^ b))) + in[6] + 0xc040b340, d = d<< 9 | d>>(32 -9), d += a ); | ||
1298 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14)( c += (a ^ (b & (d ^ a))) + in[11] + 0x265e5a51, c = c<< 14 | c>>(32 -14), c += d ); | ||
1299 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20)( b += (d ^ (a & (c ^ d))) + in[0] + 0xe9b6c7aa, b = b<< 20 | b>>(32 -20), b += c ); | ||
1300 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5)( a += (c ^ (d & (b ^ c))) + in[5] + 0xd62f105d, a = a<< 5 | a>>(32 -5), a += b ); | ||
1301 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9)( d += (b ^ (c & (a ^ b))) + in[10] + 0x02441453, d = d<< 9 | d>>(32 -9), d += a ); | ||
1302 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14)( c += (a ^ (b & (d ^ a))) + in[15] + 0xd8a1e681, c = c<< 14 | c>>(32 -14), c += d ); | ||
1303 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20)( b += (d ^ (a & (c ^ d))) + in[4] + 0xe7d3fbc8, b = b<< 20 | b>>(32 -20), b += c ); | ||
1304 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5)( a += (c ^ (d & (b ^ c))) + in[9] + 0x21e1cde6, a = a<< 5 | a>>(32 -5), a += b ); | ||
1305 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9)( d += (b ^ (c & (a ^ b))) + in[14] + 0xc33707d6, d = d<< 9 | d>>(32 -9), d += a ); | ||
1306 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14)( c += (a ^ (b & (d ^ a))) + in[3] + 0xf4d50d87, c = c<< 14 | c>>(32 -14), c += d ); | ||
1307 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20)( b += (d ^ (a & (c ^ d))) + in[8] + 0x455a14ed, b = b<< 20 | b>>(32 -20), b += c ); | ||
1308 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5)( a += (c ^ (d & (b ^ c))) + in[13] + 0xa9e3e905, a = a<< 5 | a>>(32 -5), a += b ); | ||
1309 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9)( d += (b ^ (c & (a ^ b))) + in[2] + 0xfcefa3f8, d = d<< 9 | d>>(32 -9), d += a ); | ||
1310 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14)( c += (a ^ (b & (d ^ a))) + in[7] + 0x676f02d9, c = c<< 14 | c>>(32 -14), c += d ); | ||
1311 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20)( b += (d ^ (a & (c ^ d))) + in[12] + 0x8d2a4c8a, b = b<< 20 | b>>(32 -20), b += c ); | ||
1312 | |||
1313 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4)( a += (b ^ c ^ d) + in[5] + 0xfffa3942, a = a<<4 | a>> (32 -4), a += b ); | ||
1314 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11)( d += (a ^ b ^ c) + in[8] + 0x8771f681, d = d<<11 | d>> (32 -11), d += a ); | ||
1315 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16)( c += (d ^ a ^ b) + in[11] + 0x6d9d6122, c = c<<16 | c >>(32 -16), c += d ); | ||
1316 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23)( b += (c ^ d ^ a) + in[14] + 0xfde5380c, b = b<<23 | b >>(32 -23), b += c ); | ||
1317 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4)( a += (b ^ c ^ d) + in[1] + 0xa4beea44, a = a<<4 | a>> (32 -4), a += b ); | ||
1318 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11)( d += (a ^ b ^ c) + in[4] + 0x4bdecfa9, d = d<<11 | d>> (32 -11), d += a ); | ||
1319 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16)( c += (d ^ a ^ b) + in[7] + 0xf6bb4b60, c = c<<16 | c>> (32 -16), c += d ); | ||
1320 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23)( b += (c ^ d ^ a) + in[10] + 0xbebfbc70, b = b<<23 | b >>(32 -23), b += c ); | ||
1321 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4)( a += (b ^ c ^ d) + in[13] + 0x289b7ec6, a = a<<4 | a>> (32 -4), a += b ); | ||
1322 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11)( d += (a ^ b ^ c) + in[0] + 0xeaa127fa, d = d<<11 | d>> (32 -11), d += a ); | ||
1323 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16)( c += (d ^ a ^ b) + in[3] + 0xd4ef3085, c = c<<16 | c>> (32 -16), c += d ); | ||
1324 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23)( b += (c ^ d ^ a) + in[6] + 0x04881d05, b = b<<23 | b>> (32 -23), b += c ); | ||
1325 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4)( a += (b ^ c ^ d) + in[9] + 0xd9d4d039, a = a<<4 | a>> (32 -4), a += b ); | ||
1326 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11)( d += (a ^ b ^ c) + in[12] + 0xe6db99e5, d = d<<11 | d >>(32 -11), d += a ); | ||
1327 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16)( c += (d ^ a ^ b) + in[15] + 0x1fa27cf8, c = c<<16 | c >>(32 -16), c += d ); | ||
1328 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23)( b += (c ^ d ^ a) + in[2] + 0xc4ac5665, b = b<<23 | b>> (32 -23), b += c ); | ||
1329 | |||
1330 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6)( a += (c ^ (b | ~d)) + in[0] + 0xf4292244, a = a<<6 | a >>(32 -6), a += b ); | ||
1331 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10)( d += (b ^ (a | ~c)) + in[7] + 0x432aff97, d = d<<10 | d>>(32 -10), d += a ); | ||
1332 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15)( c += (a ^ (d | ~b)) + in[14] + 0xab9423a7, c = c<<15 | c>>(32 -15), c += d ); | ||
1333 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21)( b += (d ^ (c | ~a)) + in[5] + 0xfc93a039, b = b<<21 | b>>(32 -21), b += c ); | ||
1334 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6)( a += (c ^ (b | ~d)) + in[12] + 0x655b59c3, a = a<<6 | a>>(32 -6), a += b ); | ||
1335 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10)( d += (b ^ (a | ~c)) + in[3] + 0x8f0ccc92, d = d<<10 | d>>(32 -10), d += a ); | ||
1336 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15)( c += (a ^ (d | ~b)) + in[10] + 0xffeff47d, c = c<<15 | c>>(32 -15), c += d ); | ||
1337 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21)( b += (d ^ (c | ~a)) + in[1] + 0x85845dd1, b = b<<21 | b>>(32 -21), b += c ); | ||
1338 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6)( a += (c ^ (b | ~d)) + in[8] + 0x6fa87e4f, a = a<<6 | a >>(32 -6), a += b ); | ||
1339 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10)( d += (b ^ (a | ~c)) + in[15] + 0xfe2ce6e0, d = d<<10 | d>>(32 -10), d += a ); | ||
1340 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15)( c += (a ^ (d | ~b)) + in[6] + 0xa3014314, c = c<<15 | c>>(32 -15), c += d ); | ||
1341 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21)( b += (d ^ (c | ~a)) + in[13] + 0x4e0811a1, b = b<<21 | b>>(32 -21), b += c ); | ||
1342 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6)( a += (c ^ (b | ~d)) + in[4] + 0xf7537e82, a = a<<6 | a >>(32 -6), a += b ); | ||
1343 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10)( d += (b ^ (a | ~c)) + in[11] + 0xbd3af235, d = d<<10 | d>>(32 -10), d += a ); | ||
1344 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15)( c += (a ^ (d | ~b)) + in[2] + 0x2ad7d2bb, c = c<<15 | c>>(32 -15), c += d ); | ||
1345 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21)( b += (d ^ (c | ~a)) + in[9] + 0xeb86d391, b = b<<21 | b>>(32 -21), b += c ); | ||
1346 | |||
1347 | buf[0] += a; | ||
1348 | buf[1] += b; | ||
1349 | buf[2] += c; | ||
1350 | buf[3] += d; | ||
1351 | } | ||
1352 | |||
1353 | FcBool | ||
1354 | FcDirCacheCreateTagFile (const FcChar8 *cache_dir) | ||
1355 | { | ||
1356 | FcChar8 *cache_tag; | ||
1357 | int fd; | ||
1358 | FILE *fp; | ||
1359 | FcAtomic *atomic; | ||
1360 | static const FcChar8 cache_tag_contents[] = | ||
1361 | "Signature: 8a477f597d28d172789f06886806bc55\n" | ||
1362 | "# This file is a cache directory tag created by fontconfig.\n" | ||
1363 | "# For information about cache directory tags, see:\n" | ||
1364 | "# http://www.brynosaurus.com/cachedir/\n"; | ||
1365 | static size_t cache_tag_contents_size = sizeof (cache_tag_contents) - 1; | ||
1366 | FcBool ret = FcFalse0; | ||
1367 | |||
1368 | if (!cache_dir) | ||
1369 | return FcFalse0; | ||
1370 | |||
1371 | if (access ((char *) cache_dir, W_OK2|X_OK1) == 0) | ||
1372 | { | ||
1373 | /* Create CACHEDIR.TAG */ | ||
1374 | cache_tag = FcStrPlusIA__FcStrPlus (cache_dir, (const FcChar8 *) FC_DIR_SEPARATOR_S"/" "CACHEDIR.TAG"); | ||
1375 | if (!cache_tag) | ||
1376 | return FcFalse0; | ||
1377 | atomic = FcAtomicCreateIA__FcAtomicCreate ((FcChar8 *)cache_tag); | ||
1378 | if (!atomic) | ||
1379 | goto bail1; | ||
1380 | if (!FcAtomicLockIA__FcAtomicLock (atomic)) | ||
1381 | goto bail2; | ||
1382 | fd = open((char *)FcAtomicNewFileIA__FcAtomicNewFile (atomic), O_RDWR02 | O_CREAT0100, 0644); | ||
1383 | if (fd == -1) | ||
1384 | goto bail3; | ||
1385 | fp = fdopen(fd, "wb"); | ||
1386 | if (fp == NULL((void*)0)) | ||
1387 | goto bail3; | ||
1388 | |||
1389 | fwrite(cache_tag_contents, cache_tag_contents_size, sizeof (FcChar8), fp); | ||
1390 | fclose(fp); | ||
1391 | |||
1392 | if (!FcAtomicReplaceOrigIA__FcAtomicReplaceOrig(atomic)) | ||
1393 | goto bail3; | ||
1394 | |||
1395 | ret = FcTrue1; | ||
1396 | bail3: | ||
1397 | FcAtomicUnlockIA__FcAtomicUnlock (atomic); | ||
1398 | bail2: | ||
1399 | FcAtomicDestroyIA__FcAtomicDestroy (atomic); | ||
1400 | bail1: | ||
1401 | FcStrFreeIA__FcStrFree (cache_tag); | ||
1402 | } | ||
1403 | |||
1404 | if (FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | ||
1405 | { | ||
1406 | if (ret) | ||
1407 | printf ("Created CACHEDIR.TAG at %s\n", cache_dir); | ||
1408 | else | ||
1409 | printf ("Unable to create CACHEDIR.TAG at %s\n", cache_dir); | ||
1410 | } | ||
1411 | |||
1412 | return ret; | ||
1413 | } | ||
1414 | |||
1415 | void | ||
1416 | FcCacheCreateTagFile (const FcConfig *config) | ||
1417 | { | ||
1418 | FcChar8 *cache_dir = NULL((void*)0); | ||
1419 | FcStrList *list; | ||
1420 | |||
1421 | list = FcConfigGetCacheDirsIA__FcConfigGetCacheDirs (config); | ||
1422 | if (!list) | ||
1423 | return; | ||
1424 | |||
1425 | while ((cache_dir = FcStrListNextIA__FcStrListNext (list))) | ||
1426 | { | ||
1427 | if (FcDirCacheCreateTagFile (cache_dir)) | ||
1428 | break; | ||
1429 | } | ||
1430 | FcStrListDoneIA__FcStrListDone (list); | ||
1431 | } | ||
1432 | |||
1433 | #define __fccache__ | ||
1434 | #include "fcaliastail.h" | ||
1435 | #undef __fccache__ |