source: trunk/poppler/fontconfig-2.3.2-os2/src/fccache.c @ 2

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 15 years ago

First import

File size: 27.0 KB
Line 
1/*
2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
3 *
4 * Copyright © 2000 Keith Packard
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/*
28 * POSIX has broken stdio so that getc must do thread-safe locking,
29 * this is a serious performance problem for applications doing large
30 * amounts of IO with getc (as is done here).  If available, use
31 * the getc_unlocked varient instead.
32 */
33 
34#if defined(getc_unlocked) || defined(_IO_getc_unlocked)
35#define GETC(f) getc_unlocked(f)
36#define PUTC(c,f) putc_unlocked(c,f)
37#else
38#define GETC(f) getc(f)
39#define PUTC(c,f) putc(c,f)
40#endif
41
42#define FC_DBG_CACHE_REF    1024
43
44static FcChar8 *
45FcCacheReadString (FILE *f, FcChar8 *dest, int len)
46{
47    int         c;
48    FcBool      escape;
49    FcChar8     *d;
50    int         size;
51    int         i;
52
53    while ((c = GETC (f)) != EOF)
54        if (c == '"')
55            break;
56    if (c == EOF)
57        return FcFalse;
58    if (len == 0)
59        return FcFalse;
60   
61    size = len;
62    i = 0;
63    d = dest;
64    escape = FcFalse;
65    while ((c = GETC (f)) != EOF)
66    {
67        if (!escape)
68        {
69            switch (c) {
70            case '"':
71                c = '\0';
72                break;
73            case '\\':
74                escape = FcTrue;
75                continue;
76            }
77        }
78        if (i == size)
79        {
80            FcChar8 *new = malloc (size * 2);   /* freed in caller */
81            if (!new)
82                break;
83            memcpy (new, d, size);
84            size *= 2;
85            if (d != dest)
86                free (d);
87            d = new;
88        }
89        d[i++] = c;
90        if (c == '\0')
91            return d;
92        escape = FcFalse;
93    }
94    if (d != dest)
95        free (d);
96    return 0;
97}
98
99static FcBool
100FcCacheReadUlong (FILE *f, unsigned long *dest)
101{
102    unsigned long   t;
103    int             c;
104
105    while ((c = GETC (f)) != EOF)
106    {
107        if (!isspace (c))
108            break;
109    }
110    if (c == EOF)
111        return FcFalse;
112    t = 0;
113    for (;;)
114    {
115        if (c == EOF || isspace (c))
116            break;
117        if (!isdigit (c))
118            return FcFalse;
119        t = t * 10 + (c - '0');
120        c = GETC (f);
121    }
122    *dest = t;
123    return FcTrue;
124}
125
126static FcBool
127FcCacheReadInt (FILE *f, int *dest)
128{
129    unsigned long   t;
130    FcBool          ret;
131
132    ret = FcCacheReadUlong (f, &t);
133    if (ret)
134        *dest = (int) t;
135    return ret;
136}
137
138static FcBool
139FcCacheReadTime (FILE *f, time_t *dest)
140{
141    unsigned long   t;
142    FcBool          ret;
143
144    ret = FcCacheReadUlong (f, &t);
145    if (ret)
146        *dest = (time_t) t;
147    return ret;
148}
149
150static FcBool
151FcCacheWriteChars (FILE *f, const FcChar8 *chars)
152{
153    FcChar8    c;
154    while ((c = *chars++))
155    {
156        switch (c) {
157        case '"':
158        case '\\':
159            if (PUTC ('\\', f) == EOF)
160                return FcFalse;
161            /* fall through */
162        default:
163            if (PUTC (c, f) == EOF)
164                return FcFalse;
165        }
166    }
167    return FcTrue;
168}
169
170static FcBool
171FcCacheWriteString (FILE *f, const FcChar8 *string)
172{
173
174    if (PUTC ('"', f) == EOF)
175        return FcFalse;
176    if (!FcCacheWriteChars (f, string))
177        return FcFalse;
178    if (PUTC ('"', f) == EOF)
179        return FcFalse;
180    return FcTrue;
181}
182
183static FcBool
184FcCacheWritePath (FILE *f, const FcChar8 *dir, const FcChar8 *file)
185{
186    if (PUTC ('"', f) == EOF)
187        return FcFalse;
188    if (dir)
189        if (!FcCacheWriteChars (f, dir))
190            return FcFalse;
191#if defined(_WIN32) || defined(__OS2__)
192    if (dir &&
193        dir[strlen((const char *) dir) - 1] != '/' &&
194        dir[strlen((const char *) dir) - 1] != '\\')
195    {
196        if (!FcCacheWriteChars (f, "\\"))
197            return FcFalse;
198    }
199#else
200    if (dir && dir[strlen((const char *) dir) - 1] != '/')
201        if (PUTC ('/', f) == EOF)
202            return FcFalse;
203#endif
204    if (!FcCacheWriteChars (f, file))
205        return FcFalse;
206    if (PUTC ('"', f) == EOF)
207        return FcFalse;
208    return FcTrue;
209}
210
211static FcBool
212FcCacheWriteUlong (FILE *f, unsigned long t)
213{
214    int     pow;
215    unsigned long   temp, digit;
216
217    temp = t;
218    pow = 1;
219    while (temp >= 10)
220    {
221        temp /= 10;
222        pow *= 10;
223    }
224    temp = t;
225    while (pow)
226    {
227        digit = temp / pow;
228        if (PUTC ((char) digit + '0', f) == EOF)
229            return FcFalse;
230        temp = temp - pow * digit;
231        pow = pow / 10;
232    }
233    return FcTrue;
234}
235
236static FcBool
237FcCacheWriteInt (FILE *f, int i)
238{
239    return FcCacheWriteUlong (f, (unsigned long) i);
240}
241
242static FcBool
243FcCacheWriteTime (FILE *f, time_t t)
244{
245    return FcCacheWriteUlong (f, (unsigned long) t);
246}
247
248static FcBool
249FcCacheFontSetAdd (FcFontSet        *set,
250                   FcStrSet         *dirs,
251                   const FcChar8    *dir,
252                   int              dir_len,
253                   const FcChar8    *file,
254                   const FcChar8    *name,
255                   FcConfig         *config)
256{
257    FcChar8     path_buf[8192], *path;
258    int         len;
259    FcBool      ret = FcFalse;
260    FcPattern   *font;
261    FcPattern   *frozen;
262
263    path = path_buf;
264    len = (dir_len + 1 + strlen ((const char *) file) + 1);
265    if (len > sizeof (path_buf))
266    {
267        path = malloc (len);    /* freed down below */
268        if (!path)
269            return FcFalse;
270    }
271    strncpy ((char *) path, (const char *) dir, dir_len);
272#if defined(_WIN32) || defined(__OS2__)
273    if (dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\' )
274        path[dir_len++] = '\\';
275#else
276    if (dir[dir_len - 1] != '/')
277        path[dir_len++] = '/';
278#endif
279    strcpy ((char *) path + dir_len, (const char *) file);
280    if (config && !FcConfigAcceptFilename (config, path))
281        ret = FcTrue;
282    else if (!FcStrCmp (name, FC_FONT_FILE_DIR))
283    {
284        if (FcDebug () & FC_DBG_CACHEV)
285            printf ( " dir cache dir \"%s\"\n", path);
286        ret = FcStrSetAdd (dirs, path);
287    }
288    else if (!FcStrCmp (name, FC_FONT_FILE_INVALID))
289    {
290        ret = FcTrue;
291    }
292    else
293    {
294        font = FcNameParse (name);
295        if (font)
296        {
297            FcChar8 *family;
298           
299            if (FcDebug () & FC_DBG_CACHEV)
300                printf ( " dir cache file \"%s\"\n", file);
301            ret = FcPatternAddString (font, FC_FILE, path);
302            /*
303             * Make sure the pattern has the file name as well as
304             * already containing at least one family name.
305             */
306            if (ret && 
307                FcPatternGetString (font, FC_FAMILY, 0, &family) == FcResultMatch &&
308                (!config || FcConfigAcceptFont (config, font)))
309            {
310                frozen = FcPatternFreeze (font);
311                ret = (frozen != 0);
312                if (ret)
313                   ret = FcFontSetAdd (set, frozen);
314            }
315            FcPatternDestroy (font);
316        }
317    }
318    if (path != path_buf) free (path);
319    return ret;
320   
321}
322
323static unsigned int
324FcCacheHash (const FcChar8 *string, int len)
325{
326    unsigned int    h = 0;
327    FcChar8         c;
328
329    while (len-- && (c = *string++))
330        h = (h << 1) ^ c;
331    return h;
332}
333
334/*
335 * Verify the saved timestamp for a file
336 */
337FcBool
338FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info)
339{
340    struct stat     statb;
341
342    if (stat ((char *) file, &statb) < 0)
343    {
344        if (FcDebug () & FC_DBG_CACHE)
345            printf ( " file %s missing\n", file);
346        return FcFalse;
347    }
348    if (statb.st_mtime != info->time)
349    {
350        if (FcDebug () & FC_DBG_CACHE)
351            printf ( " timestamp mismatch (was %d is %d)\n",
352                    (int) info->time, (int) statb.st_mtime);
353        return FcFalse;
354    }
355    return FcTrue;
356}
357
358void
359FcGlobalCacheReferenced (FcGlobalCache      *cache,
360                         FcGlobalCacheInfo  *info)
361{
362    if (!info->referenced)
363    {
364        info->referenced = FcTrue;
365        cache->referenced++;
366        if (FcDebug () & FC_DBG_CACHE_REF)
367            printf ( "Reference %d %s\n", cache->referenced, info->file);
368    }
369}
370
371/*
372 * Break a path into dir/base elements and compute the base hash
373 * and the dir length.  This is shared between the functions
374 * which walk the file caches
375 */
376
377typedef struct _FcFilePathInfo {
378    const FcChar8   *dir;
379    int             dir_len;
380    const FcChar8   *base;
381    unsigned int    base_hash;
382} FcFilePathInfo;
383
384static FcFilePathInfo
385FcFilePathInfoGet (const FcChar8    *path)
386{
387    FcFilePathInfo  i;
388    FcChar8         *slash;
389
390    slash = FcStrLastSlash (path);
391    if (slash)
392    {
393        i.dir = path;
394        i.dir_len = slash - path;
395        if (!i.dir_len)
396            i.dir_len = 1;
397        i.base = slash + 1;
398    }
399    else
400    {
401        i.dir = (const FcChar8 *) ".";
402        i.dir_len = 1;
403        i.base = path;
404    }
405    i.base_hash = FcCacheHash (i.base, -1);
406    return i;
407}
408
409FcGlobalCacheDir *
410FcGlobalCacheDirGet (FcGlobalCache  *cache,
411                     const FcChar8  *dir,
412                     int            len,
413                     FcBool         create_missing)
414{
415    unsigned int        hash = FcCacheHash (dir, len);
416    FcGlobalCacheDir    *d, **prev;
417
418    for (prev = &cache->ents[hash % FC_GLOBAL_CACHE_DIR_HASH_SIZE];
419         (d = *prev);
420         prev = &(*prev)->next)
421    {
422        if (d->info.hash == hash && d->len == len &&
423            !strncmp ((const char *) d->info.file,
424                      (const char *) dir, len))
425            break;
426    }
427    if (!(d = *prev))
428    {
429        int     i;
430        if (!create_missing)
431            return 0;
432        d = malloc (sizeof (FcGlobalCacheDir) + len + 1);
433        if (!d)
434            return 0;
435        FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + len + 1);
436        d->next = *prev;
437        *prev = d;
438        d->info.hash = hash;
439        d->info.file = (FcChar8 *) (d + 1);
440        strncpy ((char *) d->info.file, (const char *) dir, len);
441        d->info.file[len] = '\0';
442        d->info.time = 0;
443        d->info.referenced = FcFalse;
444        d->len = len;
445        for (i = 0; i < FC_GLOBAL_CACHE_FILE_HASH_SIZE; i++)
446            d->ents[i] = 0;
447        d->subdirs = 0;
448    }
449    return d;
450}
451
452static FcGlobalCacheInfo *
453FcGlobalCacheDirAdd (FcGlobalCache  *cache,
454                     const FcChar8  *dir,
455                     time_t         time,
456                     FcBool         replace,
457                     FcBool         create_missing)
458{
459    FcGlobalCacheDir    *d;
460    FcFilePathInfo      i;
461    FcGlobalCacheSubdir *subdir;
462    FcGlobalCacheDir    *parent;
463
464    i = FcFilePathInfoGet (dir);
465    parent = FcGlobalCacheDirGet (cache, i.dir, i.dir_len, create_missing);
466    /*
467     * Tricky here -- directories containing fonts.cache-1 files
468     * need entries only when the parent doesn't have a cache file.
469     * That is, when the parent already exists in the cache, is
470     * referenced and has a "real" timestamp.  The time of 0 is
471     * special and marks directories which got stuck in the
472     * global cache for this very reason.  Yes, it could
473     * use a separate boolean field, and probably should.
474     */
475    if (!parent || (!create_missing && 
476                    (!parent->info.referenced ||
477                    (parent->info.time == 0))))
478        return 0;
479    /*
480     * Add this directory to the cache
481     */
482    d = FcGlobalCacheDirGet (cache, dir, strlen ((const char *) dir), FcTrue);
483    if (!d)
484        return 0;
485    d->info.time = time;
486    /*
487     * Add this directory to the subdirectory list of the parent
488     */
489    subdir = malloc (sizeof (FcGlobalCacheSubdir));
490    if (!subdir)
491        return 0;
492    FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
493    subdir->ent = d;
494    subdir->next = parent->subdirs;
495    parent->subdirs = subdir;
496    return &d->info;
497}
498
499static void
500FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
501{
502    FcGlobalCacheFile   *f, *next;
503    int                 h;
504    FcGlobalCacheSubdir *s, *nexts;
505
506    for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
507        for (f = d->ents[h]; f; f = next)
508        {
509            next = f->next;
510            FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
511                       strlen ((char *) f->info.file) + 1 +
512                       strlen ((char *) f->name) + 1);
513            free (f);
514        }
515    for (s = d->subdirs; s; s = nexts)
516    {
517        nexts = s->next;
518        FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
519        free (s);
520    }
521    FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1);
522    free (d);
523}
524
525/*
526 * If the parent is in the global cache and referenced, add
527 * an entry for 'dir' to the global cache.  This is used
528 * for directories with fonts.cache files
529 */
530
531void
532FcGlobalCacheReferenceSubdir (FcGlobalCache *cache,
533                              const FcChar8 *dir)
534{
535    FcGlobalCacheInfo   *info;
536    info = FcGlobalCacheDirAdd (cache, dir, 0, FcFalse, FcFalse);
537    if (info && !info->referenced)
538    {
539        info->referenced = FcTrue;
540        cache->referenced++;
541    }
542}
543
544/*
545 * Check to see if the global cache contains valid data for 'dir'.
546 * If so, scan the global cache for files and directories in 'dir'.
547 * else, return False.
548 */
549FcBool
550FcGlobalCacheScanDir (FcFontSet         *set,
551                      FcStrSet          *dirs,
552                      FcGlobalCache     *cache,
553                      const FcChar8     *dir,
554                      FcConfig          *config)
555{
556    FcGlobalCacheDir    *d = FcGlobalCacheDirGet (cache, dir,
557                                                  strlen ((const char *) dir),
558                                                  FcFalse);
559    FcGlobalCacheFile   *f;
560    int                 h;
561    int                 dir_len;
562    FcGlobalCacheSubdir *subdir;
563    FcBool              any_in_cache = FcFalse;
564
565    if (FcDebug() & FC_DBG_CACHE)
566        printf ( "FcGlobalCacheScanDir %s\n", dir);
567   
568    if (!d)
569    {
570        if (FcDebug () & FC_DBG_CACHE)
571            printf ( "\tNo dir cache entry\n");
572        return FcFalse;
573    }
574
575    /*
576     * See if the timestamp recorded in the global cache
577     * matches the directory time, if not, return False
578     */
579    if (!FcGlobalCacheCheckTime (d->info.file, &d->info))
580    {
581        if (FcDebug () & FC_DBG_CACHE)
582            printf ( "\tdir cache entry time mismatch\n");
583        return FcFalse;
584    }
585
586    /*
587     * Add files from 'dir' to the fontset
588     */
589    dir_len = strlen ((const char *) dir);
590    for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
591        for (f = d->ents[h]; f; f = f->next)
592        {
593            if (FcDebug() & FC_DBG_CACHEV)
594                printf ( "FcGlobalCacheScanDir add file %s\n", f->info.file);
595            any_in_cache = FcTrue;
596            if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
597                                    f->info.file, f->name, config))
598            {
599                cache->broken = FcTrue;
600                return FcFalse;
601            }
602            FcGlobalCacheReferenced (cache, &f->info);
603        }
604    /*
605     * Add directories in 'dir' to 'dirs'
606     */
607    for (subdir = d->subdirs; subdir; subdir = subdir->next)
608    {
609        FcFilePathInfo  info = FcFilePathInfoGet (subdir->ent->info.file);
610       
611        any_in_cache = FcTrue;
612        if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
613                                info.base, FC_FONT_FILE_DIR, config))
614        {
615            cache->broken = FcTrue;
616            return FcFalse;
617        }
618        if (FcDebug() & FC_DBG_CACHEV)
619          printf ( "FcGlobalCacheScanDir add dir/file %s\n", subdir->ent->info.file);
620        FcGlobalCacheReferenced (cache, &subdir->ent->info);
621    }
622
623    if (FcDebug() & FC_DBG_CACHEV)
624      printf ( "FcGlobalCacheScanDir add dir/file-2 %s\n", d->info.file);
625
626    FcGlobalCacheReferenced (cache, &d->info);
627
628    /*
629     * To recover from a bug in previous versions of fontconfig,
630     * return FcFalse if no entries in the cache were found
631     * for this directory.  This will cause any empty directories
632     * to get rescanned every time fontconfig is initialized.  This
633     * might get removed at some point when the older cache files are
634     * presumably fixed.
635     */
636    return any_in_cache;
637}
638
639/*
640 * Locate the cache entry for a particular file
641 */
642FcGlobalCacheFile *
643FcGlobalCacheFileGet (FcGlobalCache *cache,
644                      const FcChar8 *file,
645                      int           id,
646                      int           *count)
647{
648    FcFilePathInfo      i = FcFilePathInfoGet (file);
649    FcGlobalCacheDir    *d = FcGlobalCacheDirGet (cache, i.dir, 
650                                                  i.dir_len, FcFalse);
651    FcGlobalCacheFile   *f, *match = 0;
652    int                 max = -1;
653
654    if (!d)
655        return 0;
656    for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next)
657    {
658        if (f->info.hash == i.base_hash &&
659            !strcmp ((const char *) f->info.file, (const char *) i.base))
660        {
661            if (f->id == id)
662                match = f;
663            if (f->id > max)
664                max = f->id;
665        }
666    }
667    if (count)
668        *count = max + 1;
669    return match;
670}
671   
672/*
673 * Add a file entry to the cache
674 */
675static FcGlobalCacheInfo *
676FcGlobalCacheFileAdd (FcGlobalCache *cache,
677                      const FcChar8 *path,
678                      int           id,
679                      time_t        time,
680                      const FcChar8 *name,
681                      FcBool        replace)
682{
683    FcFilePathInfo      i = FcFilePathInfoGet (path);
684    FcGlobalCacheDir    *d = FcGlobalCacheDirGet (cache, i.dir, 
685                                                  i.dir_len, FcTrue);
686    FcGlobalCacheFile   *f, **prev;
687    int                 size;
688
689    if (!d)
690        return 0;
691    for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE];
692         (f = *prev);
693         prev = &(*prev)->next)
694    {
695        if (f->info.hash == i.base_hash && 
696            f->id == id &&
697            !strcmp ((const char *) f->info.file, (const char *) i.base))
698        {
699            break;
700        }
701    }
702    if (*prev)
703    {
704        if (!replace)
705            return 0;
706
707        f = *prev;
708        if (f->info.referenced)
709            cache->referenced--;
710        *prev = f->next;
711        FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
712                   strlen ((char *) f->info.file) + 1 +
713                   strlen ((char *) f->name) + 1);
714        free (f);
715    }
716    size = (sizeof (FcGlobalCacheFile) +
717            strlen ((char *) i.base) + 1 +
718            strlen ((char *) name) + 1);
719    f = malloc (size);
720    if (!f)
721        return 0;
722    FcMemAlloc (FC_MEM_CACHE, size);
723    f->next = *prev;
724    *prev = f;
725    f->info.hash = i.base_hash;
726    f->info.file = (FcChar8 *) (f + 1);
727    f->info.time = time;
728    f->info.referenced = FcFalse;
729    f->id = id;
730    f->name = f->info.file + strlen ((char *) i.base) + 1;
731    strcpy ((char *) f->info.file, (const char *) i.base);
732    strcpy ((char *) f->name, (const char *) name);
733    return &f->info;
734}
735
736FcGlobalCache *
737FcGlobalCacheCreate (void)
738{
739    FcGlobalCache   *cache;
740    int             h;
741
742    cache = malloc (sizeof (FcGlobalCache));
743    if (!cache)
744        return 0;
745    FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
746    for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
747        cache->ents[h] = 0;
748    cache->entries = 0;
749    cache->referenced = 0;
750    cache->updated = FcFalse;
751    cache->broken = FcFalse;
752    return cache;
753}
754
755void
756FcGlobalCacheDestroy (FcGlobalCache *cache)
757{
758    FcGlobalCacheDir    *d, *next;
759    int                 h;
760
761    for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
762    {
763        for (d = cache->ents[h]; d; d = next)
764        {
765            next = d->next;
766            FcGlobalCacheDirDestroy (d);
767        }
768    }
769    FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
770    free (cache);
771}
772
773/*
774 * Cache file syntax is quite simple:
775 *
776 * "file_name" id time "font_name" \n
777 */
778 
779void
780FcGlobalCacheLoad (FcGlobalCache    *cache,
781                   const FcChar8    *cache_file)
782{
783    FILE                *f;
784    FcChar8             file_buf[8192], *file;
785    int                 id;
786    time_t              time;
787    FcChar8             name_buf[8192], *name;
788    FcGlobalCacheInfo   *info;
789
790    f = fopen ((char *) cache_file, "r");
791    if (!f)
792        return;
793
794    cache->updated = FcFalse;
795    file = 0;
796    name = 0;
797    while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
798           FcCacheReadInt (f, &id) &&
799           FcCacheReadTime (f, &time) &&
800           (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
801    {
802        if (FcDebug () & FC_DBG_CACHEV)
803            printf ( "FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
804        if (!FcStrCmp (name, FC_FONT_FILE_DIR))
805            info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue);
806        else
807            info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
808        if (!info)
809            cache->broken = FcTrue;
810        else
811            cache->entries++;
812        if (FcDebug () & FC_DBG_CACHE_REF)
813            printf ( "FcGlobalCacheLoad entry %d %s\n",
814                    cache->entries, file);
815        if (file != file_buf)
816            free (file);
817        if (name != name_buf)
818            free (name);
819        file = 0;
820        name = 0;
821    }
822    if (file && file != file_buf)
823        free (file);
824    if (name && name != name_buf)
825        free (name);
826    fclose (f);
827}
828
829FcBool
830FcGlobalCacheUpdate (FcGlobalCache  *cache,
831                     const FcChar8  *file,
832                     int            id,
833                     const FcChar8  *name)
834{
835    const FcChar8       *match;
836    struct stat         statb;
837    FcGlobalCacheInfo   *info;
838
839    match = file;
840
841    if (stat ((char *) file, &statb) < 0)
842        return FcFalse;
843    if (S_ISDIR (statb.st_mode))
844        info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime, 
845                                    FcTrue, FcTrue);
846    else
847        info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime, 
848                                    name, FcTrue);
849    if (info)
850    {
851      if (FcDebug() & FC_DBG_CACHEV)
852        printf ( "FcGlobalCacheUpdate dir/file %s\n", info->file);
853
854        FcGlobalCacheReferenced (cache, info);
855        cache->updated = FcTrue;
856    }
857    else
858        cache->broken = FcTrue;
859    return info != 0;
860}
861
862FcBool
863FcGlobalCacheSave (FcGlobalCache    *cache,
864                   const FcChar8    *cache_file)
865{
866    FILE                *f;
867    int                 dir_hash, file_hash;
868    FcGlobalCacheDir    *dir;
869    FcGlobalCacheFile   *file;
870    FcAtomic            *atomic;
871
872    if (!cache->updated && cache->referenced == cache->entries)
873        return FcTrue;
874   
875    if (cache->broken)
876        return FcFalse;
877
878#if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
879    /* Set-UID programs can't safely update the cache */
880    if (getuid () != geteuid ())
881        return FcFalse;
882#endif
883   
884    atomic = FcAtomicCreate (cache_file);
885    if (!atomic)
886        goto bail0;
887    if (!FcAtomicLock (atomic))
888        goto bail1;
889    f = fopen ((char *) FcAtomicNewFile(atomic), "w");
890    if (!f)
891        goto bail2;
892
893    for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
894    {
895        for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
896        {
897            if (!dir->info.referenced)
898                continue;
899            if (!FcCacheWriteString (f, dir->info.file))
900                goto bail4;
901            if (PUTC (' ', f) == EOF)
902                goto bail4;
903            if (!FcCacheWriteInt (f, 0))
904                goto bail4;
905            if (PUTC (' ', f) == EOF)
906                goto bail4;
907            if (!FcCacheWriteTime (f, dir->info.time))
908                goto bail4;
909            if (PUTC (' ', f) == EOF)
910                goto bail4;
911            if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
912                goto bail4;
913            if (PUTC ('\n', f) == EOF)
914                goto bail4;
915           
916            for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
917            {
918                for (file = dir->ents[file_hash]; file; file = file->next)
919                {
920                    if (!file->info.referenced)
921                        continue;
922                    if (!FcCacheWritePath (f, dir->info.file, file->info.file))
923                        goto bail4;
924                    if (PUTC (' ', f) == EOF)
925                        goto bail4;
926                    if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
927                        goto bail4;
928                    if (PUTC (' ', f) == EOF)
929                        goto bail4;
930                    if (!FcCacheWriteTime (f, file->info.time))
931                        goto bail4;
932                    if (PUTC (' ', f) == EOF)
933                        goto bail4;
934                    if (!FcCacheWriteString (f, file->name))
935                        goto bail4;
936                    if (PUTC ('\n', f) == EOF)
937                        goto bail4;
938                }
939            }
940        }
941    }
942
943    if (fclose (f) == EOF)
944        goto bail3;
945   
946    if (!FcAtomicReplaceOrig (atomic))
947        goto bail3;
948   
949    FcAtomicUnlock (atomic);
950    FcAtomicDestroy (atomic);
951
952    cache->updated = FcFalse;
953    return FcTrue;
954
955bail4:
956    fclose (f);
957bail3:
958    FcAtomicDeleteNew (atomic);
959bail2:
960    FcAtomicUnlock (atomic);
961bail1:
962    FcAtomicDestroy (atomic);
963bail0:
964    return FcFalse;
965}
966
967FcBool
968FcDirCacheValid (const FcChar8 *dir)
969{
970    FcChar8     *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
971    struct stat file_stat, dir_stat;
972
973    if (stat ((char *) dir, &dir_stat) < 0)
974    {
975        FcStrFree (cache_file);
976        return FcFalse;
977    }
978    if (stat ((char *) cache_file, &file_stat) < 0)
979    {
980        FcStrFree (cache_file);
981        return FcFalse;
982    }
983    FcStrFree (cache_file);
984    /*
985     * If the directory has been modified more recently than
986     * the cache file, the cache is not valid
987     */
988    if (dir_stat.st_mtime - file_stat.st_mtime > 0)
989        return FcFalse;
990    return FcTrue;
991}
992
993FcBool
994FcDirCacheReadDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config)
995{
996    FcChar8         *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
997    FILE            *f;
998    FcChar8         *base;
999    int             id;
1000    int             dir_len;
1001    FcChar8         file_buf[8192], *file;
1002    FcChar8         name_buf[8192], *name;
1003    FcBool          ret = FcFalse;
1004
1005    if (!cache_file)
1006        goto bail0;
1007   
1008    if (FcDebug () & FC_DBG_CACHE)
1009        printf ( "FcDirCacheReadDir cache_file \"%s\"\n", cache_file);
1010   
1011    f = fopen ((char *) cache_file, "r");
1012    if (!f)
1013    {
1014        if (FcDebug () & FC_DBG_CACHE)
1015            printf ( " no cache file\n");
1016        goto bail1;
1017    }
1018
1019    if (!FcDirCacheValid (dir))
1020    {
1021        if (FcDebug () & FC_DBG_CACHE)
1022            printf ( " cache file older than directory\n");
1023        goto bail2;
1024    }
1025   
1026    base = (FcChar8 *) strrchr ((char *) cache_file, '/');
1027    if (!base)
1028        goto bail2;
1029    base++;
1030    dir_len = base - cache_file;
1031   
1032    file = 0;
1033    name = 0;
1034    while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
1035           FcCacheReadInt (f, &id) &&
1036           (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
1037    {
1038        if (!FcCacheFontSetAdd (set, dirs, cache_file, dir_len,
1039                                file, name, config))
1040            goto bail3;
1041        if (file != file_buf)
1042            free (file);
1043        if (name != name_buf)
1044            free (name);
1045        file = name = 0;
1046    }
1047    if (FcDebug () & FC_DBG_CACHE)
1048        printf ( " cache loaded\n");
1049   
1050    ret = FcTrue;
1051bail3:
1052    if (file && file != file_buf)
1053        free (file);
1054    if (name && name != name_buf)
1055        free (name);
1056bail2:
1057    fclose (f);
1058bail1:
1059    FcStrFree (cache_file);
1060bail0:
1061    return ret;
1062}
1063
1064/*
1065 * return the path from the directory containing 'cache' to 'file'
1066 */
1067
1068static const FcChar8 *
1069FcFileBaseName (const FcChar8 *cache, const FcChar8 *file)
1070{
1071    const FcChar8   *cache_slash;
1072
1073    cache_slash = FcStrLastSlash (cache);
1074    if (cache_slash && !strncmp ((const char *) cache, (const char *) file,
1075                                 (cache_slash + 1) - cache))
1076        return file + ((cache_slash + 1) - cache);
1077    return file;
1078}
1079
1080FcBool
1081FcDirCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
1082{
1083    FcChar8         *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1084    FcPattern       *font;
1085    FILE            *f;
1086    FcChar8         *name;
1087    const FcChar8   *file, *base;
1088    int             n;
1089    int             id;
1090    FcBool          ret;
1091    FcStrList       *list;
1092
1093    if (!cache_file)
1094        goto bail0;
1095    if (FcDebug () & FC_DBG_CACHE)
1096        printf ( "FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
1097   
1098    f = fopen ((char *) cache_file, "w");
1099    if (!f)
1100    {
1101        if (FcDebug () & FC_DBG_CACHE)
1102            printf ( " can't create \"%s\"\n", cache_file);
1103        goto bail1;
1104    }
1105   
1106    list = FcStrListCreate (dirs);
1107    if (!list)
1108        goto bail2;
1109   
1110    while ((dir = FcStrListNext (list)))
1111    {
1112        base = FcFileBaseName (cache_file, dir);
1113        if (!FcCacheWriteString (f, base))
1114            goto bail3;
1115        if (PUTC (' ', f) == EOF)
1116            goto bail3;
1117        if (!FcCacheWriteInt (f, 0))
1118            goto bail3;
1119        if (PUTC (' ', f) == EOF)
1120            goto bail3;
1121        if (!FcCacheWriteString (f, FC_FONT_FILE_DIR))
1122            goto bail3;
1123        if (PUTC ('\n', f) == EOF)
1124            goto bail3;
1125    }
1126   
1127    for (n = 0; n < set->nfont; n++)
1128    {
1129        font = set->fonts[n];
1130        if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
1131            goto bail3;
1132        base = FcFileBaseName (cache_file, file);
1133        if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
1134            goto bail3;
1135        if (FcDebug () & FC_DBG_CACHEV)
1136            printf ( " write file \"%s\"\n", base);
1137        if (!FcCacheWriteString (f, base))
1138            goto bail3;
1139        if (PUTC (' ', f) == EOF)
1140            goto bail3;
1141        if (!FcCacheWriteInt (f, id))
1142            goto bail3;
1143        if (PUTC (' ', f) == EOF)
1144            goto bail3;
1145        name = FcNameUnparse (font);
1146        if (!name)
1147            goto bail3;
1148        ret = FcCacheWriteString (f, name);
1149        FcStrFree (name);
1150        if (!ret)
1151            goto bail3;
1152        if (PUTC ('\n', f) == EOF)
1153            goto bail3;
1154    }
1155   
1156    FcStrListDone (list);
1157
1158    if (fclose (f) == EOF)
1159        goto bail1;
1160   
1161    FcStrFree (cache_file);
1162
1163    if (FcDebug () & FC_DBG_CACHE)
1164        printf ( " cache written\n");
1165    return FcTrue;
1166   
1167bail3:
1168    FcStrListDone (list);
1169bail2:
1170    fclose (f);
1171bail1:
1172    unlink ((char *) cache_file);
1173    FcStrFree (cache_file);
1174bail0:
1175    return FcFalse;
1176}
Note: See TracBrowser for help on using the repository browser.