Fix autoconf build process for fontconfig
[platform/upstream/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 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                 FcPatternAddString (font, FC_FILE, path);
598                 if (!FcFontSetAdd (set, font))
599                     goto bail2;
600             }
601         }
602         if (path != path_buf)
603             free (path);
604         if (file != file_buf)
605             free (file);
606         if (name != name_buf)
607             free (name);
608         path = file = name = 0;
609     }
610     if (FcDebug () & FC_DBG_CACHE)
611     {
612         printf (" cache loaded\n");
613     }
614     
615     ret = FcTrue;
616 bail2:
617     if (path && path != path_buf)
618         free (path);
619     if (file && file != file_buf)
620         free (file);
621     if (name && name != name_buf)
622         free (name);
623 bail1:
624     fclose (f);
625 bail0:
626     return ret;
627 }
628
629 /*
630  * return the path from the directory containing 'cache' to 'file'
631  */
632
633 static const FcChar8 *
634 FcFileBaseName (const FcChar8 *cache, const FcChar8 *file)
635 {
636     const FcChar8   *cache_slash;
637
638     cache_slash = (const FcChar8 *) strrchr ((const char *) cache, '/');
639     if (cache_slash && !strncmp ((const char *) cache, (const char *) file,
640                                  (cache_slash + 1) - cache))
641         return file + ((cache_slash + 1) - cache);
642     return file;
643 }
644
645 FcBool
646 FcFileCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *cache_file)
647 {
648     FcPattern       *font;
649     FILE            *f;
650     FcChar8         *name;
651     const FcChar8   *file, *base;
652     int             n;
653     int             id;
654     FcBool          ret;
655     FcStrList       *list;
656     FcChar8         *dir;
657
658     if (FcDebug () & FC_DBG_CACHE)
659         printf ("FcFileCacheWriteDir cache_file \"%s\"\n", cache_file);
660     
661     f = fopen ((char *) cache_file, "w");
662     if (!f)
663     {
664         if (FcDebug () & FC_DBG_CACHE)
665             printf (" can't create \"%s\"\n", cache_file);
666         goto bail0;
667     }
668     
669     list = FcStrListCreate (dirs);
670     if (!list)
671         goto bail1;
672     
673     while ((dir = FcStrListNext (list)))
674     {
675         base = FcFileBaseName (cache_file, dir);
676         if (!FcFileCacheWriteString (f, base))
677             goto bail2;
678         if (putc (' ', f) == EOF)
679             goto bail2;
680         if (!FcFileCacheWriteInt (f, 0))
681             goto bail2;
682         if (putc (' ', f) == EOF)
683             goto bail2;
684         if (!FcFileCacheWriteString (f, FC_FONT_FILE_DIR))
685             goto bail2;
686         if (putc ('\n', f) == EOF)
687             goto bail2;
688     }
689     
690     for (n = 0; n < set->nfont; n++)
691     {
692         font = set->fonts[n];
693         if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
694             goto bail2;
695         base = FcFileBaseName (cache_file, file);
696         if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
697             goto bail2;
698         if (FcDebug () & FC_DBG_CACHEV)
699             printf (" write file \"%s\"\n", base);
700         if (!FcFileCacheWriteString (f, base))
701             goto bail2;
702         if (putc (' ', f) == EOF)
703             goto bail2;
704         if (!FcFileCacheWriteInt (f, id))
705             goto bail2;
706         if (putc (' ', f) == EOF)
707             goto bail2;
708         name = FcNameUnparse (font);
709         if (!name)
710             goto bail2;
711         ret = FcFileCacheWriteString (f, name);
712         free (name);
713         if (!ret)
714             goto bail2;
715         if (putc ('\n', f) == EOF)
716             goto bail2;
717     }
718     
719     FcStrListDone (list);
720
721     if (fclose (f) == EOF)
722         goto bail0;
723     
724     if (FcDebug () & FC_DBG_CACHE)
725         printf (" cache written\n");
726     return FcTrue;
727     
728 bail2:
729     FcStrListDone (list);
730 bail1:
731     fclose (f);
732 bail0:
733     unlink ((char *) cache_file);
734     return FcFalse;
735 }