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