]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
2ee4b1ae7bdc89f2504370e42476b72be9a287b9
[fontconfig.git] / src / fccache.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fccache.c,v 1.4 2002/03/01 01:00:54 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 FcFileCacheReadDir (FcFontSet *set, const FcChar8 *cache_file)
491 {
492 FcPattern *font;
493 FILE *f;
494 FcChar8 *path;
495 FcChar8 *base;
496 FcChar8 file_buf[8192], *file;
497 int id;
498 FcChar8 name_buf[8192], *name;
499 FcBool ret = FcFalse;
500
501 if (FcDebug () & FC_DBG_CACHE)
502 {
503 printf ("FcFileCacheReadDir cache_file \"%s\"\n", cache_file);
504 }
505
506 f = fopen ((char *) cache_file, "r");
507 if (!f)
508 {
509 if (FcDebug () & FC_DBG_CACHE)
510 {
511 printf (" no cache file\n");
512 }
513 goto bail0;
514 }
515
516 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
517 if (!base)
518 goto bail1;
519 base++;
520 path = malloc (base - cache_file + 8192 + 1);
521 if (!path)
522 goto bail1;
523 memcpy (path, cache_file, base - cache_file);
524 base = path + (base - cache_file);
525
526 file = 0;
527 name = 0;
528 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
529 FcFileCacheReadInt (f, &id) &&
530 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
531 {
532 font = FcNameParse (name);
533 if (font)
534 {
535 strcpy ((char *) base, (const char *) file);
536 if (FcDebug () & FC_DBG_CACHEV)
537 {
538 printf (" dir cache file \"%s\"\n", file);
539 }
540 FcPatternAddString (font, FC_FILE, path);
541 if (!FcFontSetAdd (set, font))
542 goto bail2;
543 }
544 if (file != file_buf)
545 free (file);
546 if (name != name_buf)
547 free (name);
548 file = name = 0;
549 }
550 if (FcDebug () & FC_DBG_CACHE)
551 {
552 printf (" cache loaded\n");
553 }
554
555 ret = FcTrue;
556 bail2:
557 free (path);
558 if (file && file != file_buf)
559 free (file);
560 if (name && name != name_buf)
561 free (name);
562 bail1:
563 fclose (f);
564 bail0:
565 return ret;
566 }
567
568 FcBool
569 FcFileCacheWriteDir (FcFontSet *set, const FcChar8 *cache_file)
570 {
571 FcPattern *font;
572 FILE *f;
573 FcChar8 *name;
574 const FcChar8 *file, *base;
575 int n;
576 int id;
577 FcBool ret;
578
579 if (FcDebug () & FC_DBG_CACHE)
580 printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file);
581
582 f = fopen ((char *) cache_file, "w");
583 if (!f)
584 {
585 if (FcDebug () & FC_DBG_CACHE)
586 printf (" can't create \"%s\"\n", cache_file);
587 goto bail0;
588 }
589 for (n = 0; n < set->nfont; n++)
590 {
591 font = set->fonts[n];
592 if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
593 goto bail1;
594 base = (FcChar8 *) strrchr ((char *) file, '/');
595 if (base)
596 base = base + 1;
597 else
598 base = file;
599 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
600 goto bail1;
601 if (FcDebug () & FC_DBG_CACHEV)
602 printf (" write file \"%s\"\n", base);
603 if (!FcFileCacheWriteString (f, base))
604 goto bail1;
605 if (putc (' ', f) == EOF)
606 goto bail1;
607 if (!FcFileCacheWriteInt (f, id))
608 goto bail1;
609 if (putc (' ', f) == EOF)
610 goto bail1;
611 name = FcNameUnparse (font);
612 if (!name)
613 goto bail1;
614 ret = FcFileCacheWriteString (f, name);
615 free (name);
616 if (!ret)
617 goto bail1;
618 if (putc ('\n', f) == EOF)
619 goto bail1;
620 }
621 if (fclose (f) == EOF)
622 goto bail0;
623
624 if (FcDebug () & FC_DBG_CACHE)
625 printf (" cache written\n");
626 return FcTrue;
627
628 bail1:
629 fclose (f);
630 bail0:
631 unlink ((char *) cache_file);
632 return FcFalse;
633 }