]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
fontconfig library: build fixes and compiler warning fixes
[fontconfig.git] / src / fccache.c
1 /*
2 * $XFree86: $
3 *
4 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
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
27 static unsigned int
28 FcFileCacheHash (const FcChar8 *string)
29 {
30 unsigned int h = 0;
31 FcChar8 c;
32
33 while ((c = *string++))
34 h = (h << 1) ^ c;
35 return h;
36 }
37
38 FcChar8 *
39 FcFileCacheFind (FcFileCache *cache,
40 const FcChar8 *file,
41 int id,
42 int *count)
43 {
44 unsigned int hash;
45 const FcChar8 *match;
46 FcFileCacheEnt *c, *name;
47 int maxid;
48 struct stat statb;
49
50 match = file;
51
52 hash = FcFileCacheHash (match);
53 name = 0;
54 maxid = -1;
55 for (c = cache->ents[hash % FC_FILE_CACHE_HASH_SIZE]; c; c = c->next)
56 {
57 if (c->hash == hash && !strcmp (match, c->file))
58 {
59 if (c->id > maxid)
60 maxid = c->id;
61 if (c->id == id)
62 {
63 if (stat ((char *) file, &statb) < 0)
64 {
65 if (FcDebug () & FC_DBG_CACHE)
66 printf (" file missing\n");
67 return 0;
68 }
69 if (statb.st_mtime != c->time)
70 {
71 if (FcDebug () & FC_DBG_CACHE)
72 printf (" timestamp mismatch (was %d is %d)\n",
73 (int) c->time, (int) statb.st_mtime);
74 return 0;
75 }
76 if (!c->referenced)
77 {
78 cache->referenced++;
79 c->referenced = FcTrue;
80 }
81 name = c;
82 }
83 }
84 }
85 if (!name)
86 return 0;
87 *count = maxid + 1;
88 return name->name;
89 }
90
91 /*
92 * Cache file syntax is quite simple:
93 *
94 * "file_name" id time "font_name" \n
95 */
96
97 static FcChar8 *
98 FcFileCacheReadString (FILE *f, FcChar8 *dest, int len)
99 {
100 int c;
101 FcBool escape;
102 FcChar8 *d;
103 int size;
104 int i;
105
106 while ((c = getc (f)) != EOF)
107 if (c == '"')
108 break;
109 if (c == EOF)
110 return FcFalse;
111 if (len == 0)
112 return FcFalse;
113
114 size = len;
115 i = 0;
116 d = dest;
117 escape = FcFalse;
118 while ((c = getc (f)) != EOF)
119 {
120 if (!escape)
121 {
122 switch (c) {
123 case '"':
124 c = '\0';
125 break;
126 case '\\':
127 escape = FcTrue;
128 continue;
129 }
130 }
131 if (i == size)
132 {
133 FcChar8 *new = malloc (size * 2);
134 if (!new)
135 break;
136 memcpy (new, d, size);
137 size *= 2;
138 if (d != dest)
139 free (d);
140 d = new;
141 }
142 d[i++] = c;
143 if (c == '\0')
144 return d;
145 escape = FcFalse;
146 }
147 if (d != dest)
148 free (d);
149 return 0;
150 }
151
152 static FcBool
153 FcFileCacheReadUlong (FILE *f, unsigned long *dest)
154 {
155 unsigned long t;
156 int c;
157
158 while ((c = getc (f)) != EOF)
159 {
160 if (!isspace (c))
161 break;
162 }
163 if (c == EOF)
164 return FcFalse;
165 t = 0;
166 for (;;)
167 {
168 if (c == EOF || isspace (c))
169 break;
170 if (!isdigit (c))
171 return FcFalse;
172 t = t * 10 + (c - '0');
173 c = getc (f);
174 }
175 *dest = t;
176 return FcTrue;
177 }
178
179 static FcBool
180 FcFileCacheReadInt (FILE *f, int *dest)
181 {
182 unsigned long t;
183 FcBool ret;
184
185 ret = FcFileCacheReadUlong (f, &t);
186 if (ret)
187 *dest = (int) t;
188 return ret;
189 }
190
191 static FcBool
192 FcFileCacheReadTime (FILE *f, time_t *dest)
193 {
194 unsigned long t;
195 FcBool ret;
196
197 ret = FcFileCacheReadUlong (f, &t);
198 if (ret)
199 *dest = (time_t) t;
200 return ret;
201 }
202
203 static FcBool
204 FcFileCacheAdd (FcFileCache *cache,
205 const FcChar8 *file,
206 int id,
207 time_t time,
208 const FcChar8 *name,
209 FcBool replace)
210 {
211 FcFileCacheEnt *c;
212 FcFileCacheEnt **prev, *old;
213 unsigned int hash;
214
215 if (FcDebug () & FC_DBG_CACHE)
216 {
217 printf ("%s face %s/%d as %s\n", replace ? "Replace" : "Add",
218 file, id, name);
219 }
220 hash = FcFileCacheHash (file);
221 for (prev = &cache->ents[hash % FC_FILE_CACHE_HASH_SIZE];
222 (old = *prev);
223 prev = &(*prev)->next)
224 {
225 if (old->hash == hash && old->id == id && !strcmp (old->file, file))
226 break;
227 }
228 if (*prev)
229 {
230 if (!replace)
231 return FcFalse;
232
233 old = *prev;
234 if (old->referenced)
235 cache->referenced--;
236 *prev = old->next;
237 free (old);
238 cache->entries--;
239 }
240
241 c = malloc (sizeof (FcFileCacheEnt) +
242 strlen ((char *) file) + 1 +
243 strlen ((char *) name) + 1);
244 if (!c)
245 return FcFalse;
246 c->next = *prev;
247 *prev = c;
248 c->hash = hash;
249 c->file = (FcChar8 *) (c + 1);
250 c->id = id;
251 c->name = c->file + strlen ((char *) file) + 1;
252 strcpy (c->file, file);
253 c->time = time;
254 c->referenced = replace;
255 strcpy (c->name, name);
256 cache->entries++;
257 return FcTrue;
258 }
259
260 FcFileCache *
261 FcFileCacheCreate (void)
262 {
263 FcFileCache *cache;
264 int h;
265
266 cache = malloc (sizeof (FcFileCache));
267 if (!cache)
268 return 0;
269 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
270 cache->ents[h] = 0;
271 cache->entries = 0;
272 cache->referenced = 0;
273 cache->updated = FcFalse;
274 return cache;
275 }
276
277 void
278 FcFileCacheDestroy (FcFileCache *cache)
279 {
280 FcFileCacheEnt *c, *next;
281 int h;
282
283 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
284 {
285 for (c = cache->ents[h]; c; c = next)
286 {
287 next = c->next;
288 free (c);
289 }
290 }
291 free (cache);
292 }
293
294 void
295 FcFileCacheLoad (FcFileCache *cache,
296 const FcChar8 *cache_file)
297 {
298 FILE *f;
299 FcChar8 file_buf[8192], *file;
300 int id;
301 time_t time;
302 FcChar8 name_buf[8192], *name;
303
304 f = fopen ((char *) cache_file, "r");
305 if (!f)
306 return;
307
308 cache->updated = FcFalse;
309 file = 0;
310 name = 0;
311 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
312 FcFileCacheReadInt (f, &id) &&
313 FcFileCacheReadTime (f, &time) &&
314 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
315 {
316 (void) FcFileCacheAdd (cache, file, id, time, name, FcFalse);
317 if (file != file_buf)
318 free (file);
319 if (name != name_buf)
320 free (name);
321 file = 0;
322 name = 0;
323 }
324 if (file && file != file_buf)
325 free (file);
326 if (name && name != name_buf)
327 free (name);
328 fclose (f);
329 }
330
331 FcBool
332 FcFileCacheUpdate (FcFileCache *cache,
333 const FcChar8 *file,
334 int id,
335 const FcChar8 *name)
336 {
337 const FcChar8 *match;
338 struct stat statb;
339 FcBool ret;
340
341 match = file;
342
343 if (stat ((char *) file, &statb) < 0)
344 return FcFalse;
345 ret = FcFileCacheAdd (cache, match, id,
346 statb.st_mtime, name, FcTrue);
347 if (ret)
348 cache->updated = FcTrue;
349 return ret;
350 }
351
352 static FcBool
353 FcFileCacheWriteString (FILE *f, const FcChar8 *string)
354 {
355 char c;
356
357 if (putc ('"', f) == EOF)
358 return FcFalse;
359 while ((c = *string++))
360 {
361 switch (c) {
362 case '"':
363 case '\\':
364 if (putc ('\\', f) == EOF)
365 return FcFalse;
366 /* fall through */
367 default:
368 if (putc (c, f) == EOF)
369 return FcFalse;
370 }
371 }
372 if (putc ('"', f) == EOF)
373 return FcFalse;
374 return FcTrue;
375 }
376
377 static FcBool
378 FcFileCacheWriteUlong (FILE *f, unsigned long t)
379 {
380 int pow;
381 unsigned long temp, digit;
382
383 temp = t;
384 pow = 1;
385 while (temp >= 10)
386 {
387 temp /= 10;
388 pow *= 10;
389 }
390 temp = t;
391 while (pow)
392 {
393 digit = temp / pow;
394 if (putc ((char) digit + '0', f) == EOF)
395 return FcFalse;
396 temp = temp - pow * digit;
397 pow = pow / 10;
398 }
399 return FcTrue;
400 }
401
402 static FcBool
403 FcFileCacheWriteInt (FILE *f, int i)
404 {
405 return FcFileCacheWriteUlong (f, (unsigned long) i);
406 }
407
408 static FcBool
409 FcFileCacheWriteTime (FILE *f, time_t t)
410 {
411 return FcFileCacheWriteUlong (f, (unsigned long) t);
412 }
413
414 FcBool
415 FcFileCacheSave (FcFileCache *cache,
416 const FcChar8 *cache_file)
417 {
418 FcChar8 *lck;
419 FcChar8 *tmp;
420 FILE *f;
421 int h;
422 FcFileCacheEnt *c;
423
424 if (!cache->updated && cache->referenced == cache->entries)
425 return FcTrue;
426
427 lck = malloc (strlen ((char *) cache_file)*2 + 4);
428 if (!lck)
429 goto bail0;
430 tmp = lck + strlen ((char *) cache_file) + 2;
431 strcpy ((char *) lck, (char *) cache_file);
432 strcat ((char *) lck, "L");
433 strcpy ((char *) tmp, (char *) cache_file);
434 strcat ((char *) tmp, "T");
435 if (link ((char *) lck, (char *) cache_file) < 0 && errno != ENOENT)
436 goto bail1;
437 if (access ((char *) tmp, F_OK) == 0)
438 goto bail2;
439 f = fopen ((char *) tmp, "w");
440 if (!f)
441 goto bail2;
442
443 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
444 {
445 for (c = cache->ents[h]; c; c = c->next)
446 {
447 if (!c->referenced)
448 continue;
449 if (!FcFileCacheWriteString (f, c->file))
450 goto bail4;
451 if (putc (' ', f) == EOF)
452 goto bail4;
453 if (!FcFileCacheWriteInt (f, c->id))
454 goto bail4;
455 if (putc (' ', f) == EOF)
456 goto bail4;
457 if (!FcFileCacheWriteTime (f, c->time))
458 goto bail4;
459 if (putc (' ', f) == EOF)
460 goto bail4;
461 if (!FcFileCacheWriteString (f, c->name))
462 goto bail4;
463 if (putc ('\n', f) == EOF)
464 goto bail4;
465 }
466 }
467
468 if (fclose (f) == EOF)
469 goto bail3;
470
471 if (rename ((char *) tmp, (char *) cache_file) < 0)
472 goto bail3;
473
474 unlink ((char *) lck);
475 cache->updated = FcFalse;
476 return FcTrue;
477
478 bail4:
479 fclose (f);
480 bail3:
481 unlink ((char *) tmp);
482 bail2:
483 unlink ((char *) lck);
484 bail1:
485 free (lck);
486 bail0:
487 return FcFalse;
488 }
489
490 FcBool
491 FcFileCacheReadDir (FcFontSet *set, const FcChar8 *cache_file)
492 {
493 FcPattern *font;
494 FILE *f;
495 FcChar8 *path;
496 FcChar8 *base;
497 FcChar8 file_buf[8192], *file;
498 int id;
499 FcChar8 name_buf[8192], *name;
500 FcBool ret = FcFalse;
501
502 if (FcDebug () & FC_DBG_CACHE)
503 {
504 printf ("FcFileCacheReadDir cache_file \"%s\"\n", cache_file);
505 }
506
507 f = fopen ((char *) cache_file, "r");
508 if (!f)
509 {
510 if (FcDebug () & FC_DBG_CACHE)
511 {
512 printf (" no cache file\n");
513 }
514 goto bail0;
515 }
516
517 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
518 if (!base)
519 goto bail1;
520 base++;
521 path = malloc (base - cache_file + 8192 + 1);
522 if (!path)
523 goto bail1;
524 memcpy (path, cache_file, base - cache_file);
525 base = path + (base - cache_file);
526
527 file = 0;
528 name = 0;
529 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
530 FcFileCacheReadInt (f, &id) &&
531 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
532 {
533 font = FcNameParse (name);
534 if (font)
535 {
536 strcpy (base, file);
537 if (FcDebug () & FC_DBG_CACHEV)
538 {
539 printf (" dir cache file \"%s\"\n", file);
540 }
541 FcPatternAddString (font, FC_FILE, path);
542 if (!FcFontSetAdd (set, font))
543 goto bail2;
544 }
545 if (file != file_buf)
546 free (file);
547 if (name != name_buf)
548 free (name);
549 file = name = 0;
550 }
551 if (FcDebug () & FC_DBG_CACHE)
552 {
553 printf (" cache loaded\n");
554 }
555
556 ret = FcTrue;
557 bail2:
558 free (path);
559 if (file && file != file_buf)
560 free (file);
561 if (name && name != name_buf)
562 free (name);
563 bail1:
564 fclose (f);
565 bail0:
566 return ret;
567 }
568
569 FcBool
570 FcFileCacheWriteDir (FcFontSet *set, const FcChar8 *cache_file)
571 {
572 FcPattern *font;
573 FILE *f;
574 FcChar8 *name;
575 const FcChar8 *file, *base;
576 int n;
577 int id;
578 FcBool ret;
579
580 if (FcDebug () & FC_DBG_CACHE)
581 printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file);
582
583 f = fopen ((char *) cache_file, "w");
584 if (!f)
585 {
586 if (FcDebug () & FC_DBG_CACHE)
587 printf (" can't create \"%s\"\n", cache_file);
588 goto bail0;
589 }
590 for (n = 0; n < set->nfont; n++)
591 {
592 font = set->fonts[n];
593 if (FcPatternGetString (font, FC_FILE, 0, &file) != FcResultMatch)
594 goto bail1;
595 base = (FcChar8 *) strrchr ((char *) file, '/');
596 if (base)
597 base = base + 1;
598 else
599 base = file;
600 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
601 goto bail1;
602 if (FcDebug () & FC_DBG_CACHEV)
603 printf (" write file \"%s\"\n", base);
604 if (!FcFileCacheWriteString (f, base))
605 goto bail1;
606 if (putc (' ', f) == EOF)
607 goto bail1;
608 if (!FcFileCacheWriteInt (f, id))
609 goto bail1;
610 if (putc (' ', f) == EOF)
611 goto bail1;
612 name = FcNameUnparse (font);
613 if (!name)
614 goto bail1;
615 ret = FcFileCacheWriteString (f, name);
616 free (name);
617 if (!ret)
618 goto bail1;
619 if (putc ('\n', f) == EOF)
620 goto bail1;
621 }
622 if (fclose (f) == EOF)
623 goto bail0;
624
625 if (FcDebug () & FC_DBG_CACHE)
626 printf (" cache written\n");
627 return FcTrue;
628
629 bail1:
630 fclose (f);
631 bail0:
632 unlink ((char *) cache_file);
633 return FcFalse;
634 }