]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
Add ref counting to font config patterns so that FcFontSort return values
[fontconfig.git] / src / fccache.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fccache.c,v 1.7 2002/05/21 17:06:22 keithp Exp $
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 ((const char *) match, (const char *) 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 ((const char *) old->file,
226 (const char *) file))
227 break;
228 }
229 if (*prev)
230 {
231 if (!replace)
232 return FcFalse;
233
234 old = *prev;
235 if (old->referenced)
236 cache->referenced--;
237 *prev = old->next;
238 free (old);
239 cache->entries--;
240 }
241
242 c = malloc (sizeof (FcFileCacheEnt) +
243 strlen ((char *) file) + 1 +
244 strlen ((char *) name) + 1);
245 if (!c)
246 return FcFalse;
247 c->next = *prev;
248 *prev = c;
249 c->hash = hash;
250 c->file = (FcChar8 *) (c + 1);
251 c->id = id;
252 c->name = c->file + strlen ((char *) file) + 1;
253 strcpy ((char *) c->file, (const char *) file);
254 c->time = time;
255 c->referenced = replace;
256 strcpy ((char *) c->name, (const char *) name);
257 cache->entries++;
258 return FcTrue;
259 }
260
261 FcFileCache *
262 FcFileCacheCreate (void)
263 {
264 FcFileCache *cache;
265 int h;
266
267 cache = malloc (sizeof (FcFileCache));
268 if (!cache)
269 return 0;
270 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
271 cache->ents[h] = 0;
272 cache->entries = 0;
273 cache->referenced = 0;
274 cache->updated = FcFalse;
275 return cache;
276 }
277
278 void
279 FcFileCacheDestroy (FcFileCache *cache)
280 {
281 FcFileCacheEnt *c, *next;
282 int h;
283
284 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
285 {
286 for (c = cache->ents[h]; c; c = next)
287 {
288 next = c->next;
289 free (c);
290 }
291 }
292 free (cache);
293 }
294
295 void
296 FcFileCacheLoad (FcFileCache *cache,
297 const FcChar8 *cache_file)
298 {
299 FILE *f;
300 FcChar8 file_buf[8192], *file;
301 int id;
302 time_t time;
303 FcChar8 name_buf[8192], *name;
304
305 f = fopen ((char *) cache_file, "r");
306 if (!f)
307 return;
308
309 cache->updated = FcFalse;
310 file = 0;
311 name = 0;
312 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
313 FcFileCacheReadInt (f, &id) &&
314 FcFileCacheReadTime (f, &time) &&
315 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
316 {
317 (void) FcFileCacheAdd (cache, file, id, time, name, FcFalse);
318 if (file != file_buf)
319 free (file);
320 if (name != name_buf)
321 free (name);
322 file = 0;
323 name = 0;
324 }
325 if (file && file != file_buf)
326 free (file);
327 if (name && name != name_buf)
328 free (name);
329 fclose (f);
330 }
331
332 FcBool
333 FcFileCacheUpdate (FcFileCache *cache,
334 const FcChar8 *file,
335 int id,
336 const FcChar8 *name)
337 {
338 const FcChar8 *match;
339 struct stat statb;
340 FcBool ret;
341
342 match = file;
343
344 if (stat ((char *) file, &statb) < 0)
345 return FcFalse;
346 ret = FcFileCacheAdd (cache, match, id,
347 statb.st_mtime, name, FcTrue);
348 if (ret)
349 cache->updated = FcTrue;
350 return ret;
351 }
352
353 static FcBool
354 FcFileCacheWriteString (FILE *f, const FcChar8 *string)
355 {
356 char c;
357
358 if (putc ('"', f) == EOF)
359 return FcFalse;
360 while ((c = *string++))
361 {
362 switch (c) {
363 case '"':
364 case '\\':
365 if (putc ('\\', f) == EOF)
366 return FcFalse;
367 /* fall through */
368 default:
369 if (putc (c, f) == EOF)
370 return FcFalse;
371 }
372 }
373 if (putc ('"', f) == EOF)
374 return FcFalse;
375 return FcTrue;
376 }
377
378 static FcBool
379 FcFileCacheWriteUlong (FILE *f, unsigned long t)
380 {
381 int pow;
382 unsigned long temp, digit;
383
384 temp = t;
385 pow = 1;
386 while (temp >= 10)
387 {
388 temp /= 10;
389 pow *= 10;
390 }
391 temp = t;
392 while (pow)
393 {
394 digit = temp / pow;
395 if (putc ((char) digit + '0', f) == EOF)
396 return FcFalse;
397 temp = temp - pow * digit;
398 pow = pow / 10;
399 }
400 return FcTrue;
401 }
402
403 static FcBool
404 FcFileCacheWriteInt (FILE *f, int i)
405 {
406 return FcFileCacheWriteUlong (f, (unsigned long) i);
407 }
408
409 static FcBool
410 FcFileCacheWriteTime (FILE *f, time_t t)
411 {
412 return FcFileCacheWriteUlong (f, (unsigned long) t);
413 }
414
415 FcBool
416 FcFileCacheSave (FcFileCache *cache,
417 const FcChar8 *cache_file)
418 {
419 FILE *f;
420 int h;
421 FcFileCacheEnt *c;
422 FcAtomic *atomic;
423
424 if (!cache->updated && cache->referenced == cache->entries)
425 return FcTrue;
426
427 /* Set-UID programs can't safely update the cache */
428 if (getuid () != geteuid ())
429 return FcFalse;
430
431 atomic = FcAtomicCreate (cache_file);
432 if (!atomic)
433 goto bail0;
434 if (!FcAtomicLock (atomic))
435 goto bail1;
436 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
437 if (!f)
438 goto bail2;
439
440 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
441 {
442 for (c = cache->ents[h]; c; c = c->next)
443 {
444 if (!c->referenced)
445 continue;
446 if (!FcFileCacheWriteString (f, c->file))
447 goto bail4;
448 if (putc (' ', f) == EOF)
449 goto bail4;
450 if (!FcFileCacheWriteInt (f, c->id))
451 goto bail4;
452 if (putc (' ', f) == EOF)
453 goto bail4;
454 if (!FcFileCacheWriteTime (f, c->time))
455 goto bail4;
456 if (putc (' ', f) == EOF)
457 goto bail4;
458 if (!FcFileCacheWriteString (f, c->name))
459 goto bail4;
460 if (putc ('\n', f) == EOF)
461 goto bail4;
462 }
463 }
464
465 if (fclose (f) == EOF)
466 goto bail3;
467
468 if (!FcAtomicReplaceOrig (atomic))
469 goto bail3;
470
471 FcAtomicUnlock (atomic);
472 FcAtomicDestroy (atomic);
473
474 cache->updated = FcFalse;
475 return FcTrue;
476
477 bail4:
478 fclose (f);
479 bail3:
480 FcAtomicDeleteNew (atomic);
481 bail2:
482 FcAtomicUnlock (atomic);
483 bail1:
484 FcAtomicDestroy (atomic);
485 bail0:
486 return FcFalse;
487 }
488
489 FcBool
490 FcFileCacheValid (const FcChar8 *cache_file)
491 {
492 FcChar8 *dir = FcStrDirname (cache_file);
493 struct stat file_stat, dir_stat;
494
495 if (!dir)
496 return FcFalse;
497 if (stat ((char *) dir, &dir_stat) < 0)
498 {
499 FcStrFree (dir);
500 return FcFalse;
501 }
502 FcStrFree (dir);
503 if (stat ((char *) cache_file, &file_stat) < 0)
504 return FcFalse;
505 /*
506 * If the directory has been modified more recently than
507 * the cache file, the cache is not valid
508 */
509 if (dir_stat.st_mtime - file_stat.st_mtime > 0)
510 return FcFalse;
511 return FcTrue;
512 }
513
514 FcBool
515 FcFileCacheReadDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *cache_file)
516 {
517 FcPattern *font;
518 FILE *f;
519 FcChar8 *base;
520 int id;
521 int dir_len;
522 int file_len;
523 FcChar8 file_buf[8192], *file;
524 FcChar8 name_buf[8192], *name;
525 FcChar8 path_buf[8192], *path;
526 FcBool ret = FcFalse;
527
528 if (FcDebug () & FC_DBG_CACHE)
529 {
530 printf ("FcFileCacheReadDir cache_file \"%s\"\n", cache_file);
531 }
532
533 f = fopen ((char *) cache_file, "r");
534 if (!f)
535 {
536 if (FcDebug () & FC_DBG_CACHE)
537 {
538 printf (" no cache file\n");
539 }
540 goto bail0;
541 }
542
543 if (!FcFileCacheValid (cache_file))
544 {
545 if (FcDebug () & FC_DBG_CACHE)
546 {
547 printf (" cache file older than directory\n");
548 }
549 goto bail1;
550 }
551
552 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
553 if (!base)
554 goto bail1;
555 base++;
556 dir_len = base - cache_file;
557 if (dir_len < sizeof (path_buf))
558 strncpy ((char *) path_buf, (const char *) cache_file, dir_len);
559
560 file = 0;
561 name = 0;
562 path = 0;
563 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
564 FcFileCacheReadInt (f, &id) &&
565 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
566 {
567 file_len = strlen ((const char *) file);
568 if (dir_len + file_len + 1 > sizeof (path_buf))
569 {
570 path = malloc (dir_len + file_len + 1);
571 if (!path)
572 goto bail2;
573 strncpy ((char *) path, (const char *) cache_file, dir_len);
574 }
575 else
576 path = path_buf;
577
578 strcpy ((char *) path + dir_len, (const char *) file);
579 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
580 {
581 if (FcDebug () & FC_DBG_CACHEV)
582 {
583 printf (" dir cache dir \"%s\"\n", path);
584 }
585 if (!FcStrSetAdd (dirs, path))
586 goto bail2;
587 }
588 else
589 {
590 font = FcNameParse (name);
591 if (font)
592 {
593 if (FcDebug () & FC_DBG_CACHEV)
594 {
595 printf (" dir cache file \"%s\"\n", file);
596 }
597 if (!FcPatternAddString (font, FC_FILE, path))
598 {
599 FcPatternDestroy (font);
600 goto bail2;
601 }
602 if (!FcFontSetAdd (set, font))
603 {
604 FcPatternDestroy (font);
605 goto bail2;
606 }
607 }
608 }
609 if (path != path_buf)
610 free (path);
611 if (file != file_buf)
612 free (file);
613 if (name != name_buf)
614 free (name);
615 path = file = name = 0;
616 }
617 if (FcDebug () & FC_DBG_CACHE)
618 {
619 printf (" cache loaded\n");
620 }
621
622 ret = FcTrue;
623 bail2:
624 if (path && path != path_buf)
625 free (path);
626 if (file && file != file_buf)
627 free (file);
628 if (name && name != name_buf)
629 free (name);
630 bail1:
631 fclose (f);
632 bail0:
633 return ret;
634 }
635
636 /*
637 * return the path from the directory containing 'cache' to 'file'
638 */
639
640 static const FcChar8 *
641 FcFileBaseName (const FcChar8 *cache, const FcChar8 *file)
642 {
643 const FcChar8 *cache_slash;
644
645 cache_slash = (const FcChar8 *) strrchr ((const char *) cache, '/');
646 if (cache_slash && !strncmp ((const char *) cache, (const char *) file,
647 (cache_slash + 1) - cache))
648 return file + ((cache_slash + 1) - cache);
649 return file;
650 }
651
652 FcBool
653 FcFileCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *cache_file)
654 {
655 FcPattern *font;
656 FILE *f;
657 FcChar8 *name;
658 const FcChar8 *file, *base;
659 int n;
660 int id;
661 FcBool ret;
662 FcStrList *list;
663 FcChar8 *dir;
664
665 if (FcDebug () & FC_DBG_CACHE)
666 printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file);
667
668 f = fopen ((char *) cache_file, "w");
669 if (!f)
670 {
671 if (FcDebug () & FC_DBG_CACHE)
672 printf (" can't create \"%s\"\n", cache_file);
673 goto bail0;
674 }
675
676 list = FcStrListCreate (dirs);
677 if (!list)
678 goto bail1;
679
680 while ((dir = FcStrListNext (list)))
681 {
682 base = FcFileBaseName (cache_file, dir);
683 if (!FcFileCacheWriteString (f, base))
684 goto bail2;
685 if (putc (' ', f) == EOF)
686 goto bail2;
687 if (!FcFileCacheWriteInt (f, 0))
688 goto bail2;
689 if (putc (' ', f) == EOF)
690 goto bail2;
691 if (!FcFileCacheWriteString (f, FC_FONT_FILE_DIR))
692 goto bail2;
693 if (putc ('\n', f) == EOF)
694 goto bail2;
695 }
696
697 for (n = 0; n < set->nfont; n++)
698 {
699 font = set->fonts[n];
700 if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
701 goto bail2;
702 base = FcFileBaseName (cache_file, file);
703 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
704 goto bail2;
705 if (FcDebug () & FC_DBG_CACHEV)
706 printf (" write file \"%s\"\n", base);
707 if (!FcFileCacheWriteString (f, base))
708 goto bail2;
709 if (putc (' ', f) == EOF)
710 goto bail2;
711 if (!FcFileCacheWriteInt (f, id))
712 goto bail2;
713 if (putc (' ', f) == EOF)
714 goto bail2;
715 name = FcNameUnparse (font);
716 if (!name)
717 goto bail2;
718 ret = FcFileCacheWriteString (f, name);
719 free (name);
720 if (!ret)
721 goto bail2;
722 if (putc ('\n', f) == EOF)
723 goto bail2;
724 }
725
726 FcStrListDone (list);
727
728 if (fclose (f) == EOF)
729 goto bail0;
730
731 if (FcDebug () & FC_DBG_CACHE)
732 printf (" cache written\n");
733 return FcTrue;
734
735 bail2:
736 FcStrListDone (list);
737 bail1:
738 fclose (f);
739 bail0:
740 unlink ((char *) cache_file);
741 return FcFalse;
742 }