]> git.wh0rd.org Git - 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 }