]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
Port Xft1 to fontconfig
[fontconfig.git] / src / fccache.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fccache.c,v 1.3 2002/02/19 07:50:43 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 FcChar8 *lck;
420 FcChar8 *tmp;
421 FILE *f;
422 int h;
423 FcFileCacheEnt *c;
424
425 if (!cache->updated && cache->referenced == cache->entries)
426 return FcTrue;
427
428 lck = malloc (strlen ((char *) cache_file)*2 + 4);
429 if (!lck)
430 goto bail0;
431 tmp = lck + strlen ((char *) cache_file) + 2;
432 strcpy ((char *) lck, (char *) cache_file);
433 strcat ((char *) lck, "L");
434 strcpy ((char *) tmp, (char *) cache_file);
435 strcat ((char *) tmp, "T");
436 if (link ((char *) lck, (char *) cache_file) < 0 && errno != ENOENT)
437 goto bail1;
438 if (access ((char *) tmp, F_OK) == 0)
439 goto bail2;
440 f = fopen ((char *) tmp, "w");
441 if (!f)
442 goto bail2;
443
444 for (h = 0; h < FC_FILE_CACHE_HASH_SIZE; h++)
445 {
446 for (c = cache->ents[h]; c; c = c->next)
447 {
448 if (!c->referenced)
449 continue;
450 if (!FcFileCacheWriteString (f, c->file))
451 goto bail4;
452 if (putc (' ', f) == EOF)
453 goto bail4;
454 if (!FcFileCacheWriteInt (f, c->id))
455 goto bail4;
456 if (putc (' ', f) == EOF)
457 goto bail4;
458 if (!FcFileCacheWriteTime (f, c->time))
459 goto bail4;
460 if (putc (' ', f) == EOF)
461 goto bail4;
462 if (!FcFileCacheWriteString (f, c->name))
463 goto bail4;
464 if (putc ('\n', f) == EOF)
465 goto bail4;
466 }
467 }
468
469 if (fclose (f) == EOF)
470 goto bail3;
471
472 if (rename ((char *) tmp, (char *) cache_file) < 0)
473 goto bail3;
474
475 unlink ((char *) lck);
476 cache->updated = FcFalse;
477 return FcTrue;
478
479 bail4:
480 fclose (f);
481 bail3:
482 unlink ((char *) tmp);
483 bail2:
484 unlink ((char *) lck);
485 bail1:
486 free (lck);
487 bail0:
488 return FcFalse;
489 }
490
491 FcBool
492 FcFileCacheReadDir (FcFontSet *set, const FcChar8 *cache_file)
493 {
494 FcPattern *font;
495 FILE *f;
496 FcChar8 *path;
497 FcChar8 *base;
498 FcChar8 file_buf[8192], *file;
499 int id;
500 FcChar8 name_buf[8192], *name;
501 FcBool ret = FcFalse;
502
503 if (FcDebug () & FC_DBG_CACHE)
504 {
505 printf ("FcFileCacheReadDir cache_file \"%s\"\n", cache_file);
506 }
507
508 f = fopen ((char *) cache_file, "r");
509 if (!f)
510 {
511 if (FcDebug () & FC_DBG_CACHE)
512 {
513 printf (" no cache file\n");
514 }
515 goto bail0;
516 }
517
518 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
519 if (!base)
520 goto bail1;
521 base++;
522 path = malloc (base - cache_file + 8192 + 1);
523 if (!path)
524 goto bail1;
525 memcpy (path, cache_file, base - cache_file);
526 base = path + (base - cache_file);
527
528 file = 0;
529 name = 0;
530 while ((file = FcFileCacheReadString (f, file_buf, sizeof (file_buf))) &&
531 FcFileCacheReadInt (f, &id) &&
532 (name = FcFileCacheReadString (f, name_buf, sizeof (name_buf))))
533 {
534 font = FcNameParse (name);
535 if (font)
536 {
537 strcpy ((char *) base, (const char *) file);
538 if (FcDebug () & FC_DBG_CACHEV)
539 {
540 printf (" dir cache file \"%s\"\n", file);
541 }
542 FcPatternAddString (font, FC_FILE, path);
543 if (!FcFontSetAdd (set, font))
544 goto bail2;
545 }
546 if (file != file_buf)
547 free (file);
548 if (name != name_buf)
549 free (name);
550 file = name = 0;
551 }
552 if (FcDebug () & FC_DBG_CACHE)
553 {
554 printf (" cache loaded\n");
555 }
556
557 ret = FcTrue;
558 bail2:
559 free (path);
560 if (file && file != file_buf)
561 free (file);
562 if (name && name != name_buf)
563 free (name);
564 bail1:
565 fclose (f);
566 bail0:
567 return ret;
568 }
569
570 FcBool
571 FcFileCacheWriteDir (FcFontSet *set, const FcChar8 *cache_file)
572 {
573 FcPattern *font;
574 FILE *f;
575 FcChar8 *name;
576 const FcChar8 *file, *base;
577 int n;
578 int id;
579 FcBool ret;
580
581 if (FcDebug () & FC_DBG_CACHE)
582 printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file);
583
584 f = fopen ((char *) cache_file, "w");
585 if (!f)
586 {
587 if (FcDebug () & FC_DBG_CACHE)
588 printf (" can't create \"%s\"\n", cache_file);
589 goto bail0;
590 }
591 for (n = 0; n < set->nfont; n++)
592 {
593 font = set->fonts[n];
594 if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
595 goto bail1;
596 base = (FcChar8 *) strrchr ((char *) file, '/');
597 if (base)
598 base = base + 1;
599 else
600 base = file;
601 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
602 goto bail1;
603 if (FcDebug () & FC_DBG_CACHEV)
604 printf (" write file \"%s\"\n", base);
605 if (!FcFileCacheWriteString (f, base))
606 goto bail1;
607 if (putc (' ', f) == EOF)
608 goto bail1;
609 if (!FcFileCacheWriteInt (f, id))
610 goto bail1;
611 if (putc (' ', f) == EOF)
612 goto bail1;
613 name = FcNameUnparse (font);
614 if (!name)
615 goto bail1;
616 ret = FcFileCacheWriteString (f, name);
617 free (name);
618 if (!ret)
619 goto bail1;
620 if (putc ('\n', f) == EOF)
621 goto bail1;
622 }
623 if (fclose (f) == EOF)
624 goto bail0;
625
626 if (FcDebug () & FC_DBG_CACHE)
627 printf (" cache written\n");
628 return FcTrue;
629
630 bail1:
631 fclose (f);
632 bail0:
633 unlink ((char *) cache_file);
634 return FcFalse;
635 }