X-Git-Url: https://git.wh0rd.org/?a=blobdiff_plain;f=src%2Ffccache.c;h=1411766fadcfec7e60c51ce1e875eb98fdb615af;hb=2d3387fd720f33f80847ae6cbb83d94c9a52fde3;hp=9b92173b2c129f0fb638061f3a47f19a9b81742d;hpb=212c9f437e959fbdc5fe344c67b8c1cf8ca63edb;p=fontconfig.git diff --git a/src/fccache.c b/src/fccache.c index 9b92173..1411766 100644 --- a/src/fccache.c +++ b/src/fccache.c @@ -1,7 +1,6 @@ /* - * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $ - * * Copyright © 2000 Keith Packard + * Copyright © 2005 Patrick Lam * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -22,686 +21,839 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include #include "fcint.h" +#include "../fc-arch/fcarch.h" +#include +#include +#include +#include +#include +#if defined(HAVE_MMAP) || defined(__CYGWIN__) +# include +# include +#elif defined(_WIN32) +# include +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +struct MD5Context { + FcChar32 buf[4]; + FcChar32 bits[2]; + unsigned char in[64]; +}; + +static void MD5Init(struct MD5Context *ctx); +static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *ctx); +static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]); -#define PAGESIZE 8192 +#define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX)) -static FcBool force; +static const char bin2hex[] = { '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' }; static FcChar8 * -FcCacheReadString (int fd, FcChar8 *dest, int len) +FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN]) { - FcChar8 c; - FcBool escape; - int size; - int i; + unsigned char hash[16]; + FcChar8 *hex_hash; + int cnt; + struct MD5Context ctx; - if (len == 0) - return FcFalse; - - size = len; - i = 0; - escape = FcFalse; - while (read (fd, &c, 1) == 1) + MD5Init (&ctx); + MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir)); + + MD5Final (hash, &ctx); + + cache_base[0] = '/'; + hex_hash = cache_base + 1; + for (cnt = 0; cnt < 16; ++cnt) { - if (!escape) - { - switch (c) { - case '"': - c = '\0'; - break; - case '\\': - escape = FcTrue; - continue; - } - } - if (i == size) - { - dest[i++] = 0; - return dest; - } - dest[i++] = c; - if (c == '\0') - return dest; - escape = FcFalse; + hex_hash[2*cnt ] = bin2hex[hash[cnt] >> 4]; + hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf]; } - return 0; -} + hex_hash[2*cnt] = 0; + strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX); -static FcBool -FcCacheWriteString (int fd, const FcChar8 *chars) -{ - if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1) - return FcFalse; - return FcTrue; + return cache_base; } -#if 0 -/* - * Verify the saved timestamp for a file - */ FcBool -FcGlobalCacheCheckTime (const FcChar8 *file, FcGlobalCacheInfo *info) +FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config) { - struct stat statb; + FcChar8 *cache_hashed = NULL; + FcChar8 cache_base[CACHEBASE_LEN]; + FcStrList *list; + FcChar8 *cache_dir; + + FcDirCacheBasename (dir, cache_base); - if (stat ((char *) file, &statb) < 0) + list = FcStrListCreate (config->cacheDirs); + if (!list) + return FcFalse; + + while ((cache_dir = FcStrListNext (list))) { - if (FcDebug () & FC_DBG_CACHE) - printf (" file %s missing\n", file); - return FcFalse; + cache_hashed = FcStrPlus (cache_dir, cache_base); + if (!cache_hashed) + break; + (void) unlink ((char *) cache_hashed); } - if (statb.st_mtime != info->time) - { - if (FcDebug () & FC_DBG_CACHE) - printf (" timestamp mismatch (was %d is %d)\n", - (int) info->time, (int) statb.st_mtime); + FcStrListDone (list); + /* return FcFalse if something went wrong */ + if (cache_dir) return FcFalse; - } return FcTrue; } -void -FcGlobalCacheReferenced (FcGlobalCache *cache, - FcGlobalCacheInfo *info) +static int +FcCacheReadDirs (FcConfig * config, + FcStrList *list, FcFontSet * set, FcStrSet *processed_dirs) { - if (!info->referenced) + int ret = 0; + FcChar8 *dir; + FcStrSet *subdirs; + FcStrList *sublist; + + /* + * Read in the results from 'list'. + */ + while ((dir = FcStrListNext (list))) { - info->referenced = FcTrue; - cache->referenced++; - if (FcDebug () & FC_DBG_CACHE_REF) - printf ("Reference %d %s\n", cache->referenced, info->file); - } -} + if (!FcConfigAcceptFilename (config, dir)) + continue; -/* - * Break a path into dir/base elements and compute the base hash - * and the dir length. This is shared between the functions - * which walk the file caches - */ + /* Skip this directory if already updated + * to avoid the looped directories via symlinks + * Clearly a dir not in fonts.conf shouldn't be globally cached. + */ -typedef struct _FcFilePathInfo { - const FcChar8 *dir; - int dir_len; - const FcChar8 *base; - unsigned int base_hash; -} FcFilePathInfo; + if (FcStrSetMember (processed_dirs, dir)) + continue; + if (!FcStrSetAdd (processed_dirs, dir)) + continue; -static FcFilePathInfo -FcFilePathInfoGet (const FcChar8 *path) -{ - FcFilePathInfo i; - FcChar8 *slash; - - slash = FcStrLastSlash (path); - if (slash) - { - i.dir = path; - i.dir_len = slash - path; - if (!i.dir_len) - i.dir_len = 1; - i.base = slash + 1; - } - else - { - i.dir = (const FcChar8 *) "."; - i.dir_len = 1; - i.base = path; + subdirs = FcStrSetCreate (); + if (!subdirs) + { + fprintf (stderr, "Can't create directory set\n"); + ret++; + continue; + } + + FcDirScanConfig (set, subdirs, + config->blanks, dir, FcFalse, config); + + sublist = FcStrListCreate (subdirs); + FcStrSetDestroy (subdirs); + if (!sublist) + { + fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir); + ret++; + continue; + } + ret += FcCacheReadDirs (config, sublist, set, processed_dirs); } - i.base_hash = FcCacheHash (i.base, -1); - return i; + FcStrListDone (list); + return ret; } -FcGlobalCache * -FcGlobalCacheCreate (void) +FcFontSet * +FcCacheRead (FcConfig *config) { - FcGlobalCache *cache; - int h; + FcFontSet *s = FcFontSetCreate(); + FcStrSet *processed_dirs; - cache = malloc (sizeof (FcGlobalCache)); - if (!cache) + if (!s) return 0; - FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache)); - for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) - cache->ents[h] = 0; - cache->entries = 0; - cache->referenced = 0; - cache->updated = FcFalse; - cache->broken = FcFalse; - return cache; + + processed_dirs = FcStrSetCreate(); + if (!processed_dirs) + goto bail; + + if (FcCacheReadDirs (config, FcConfigGetConfigDirs (config), s, processed_dirs)) + goto bail1; + + FcStrSetDestroy (processed_dirs); + return s; + + bail1: + FcStrSetDestroy (processed_dirs); + bail: + FcFontSetDestroy (s); + return 0; } -void -FcGlobalCacheDestroy (FcGlobalCache *cache) +/* + * Look for a cache file for the specified dir. Attempt + * to use each one we find, stopping when the callback + * indicates success + */ +static FcBool +FcDirCacheProcess (FcConfig *config, const FcChar8 *dir, + FcBool (*callback) (int fd, off_t size, void *closure), + void *closure) { - FcGlobalCacheDir *d, *next; - int h; + int fd = -1; + FcChar8 cache_base[CACHEBASE_LEN]; + FcStrList *list; + FcChar8 *cache_dir; + struct stat file_stat, dir_stat; + FcBool ret = FcFalse; + + if (stat ((char *) dir, &dir_stat) < 0) + return FcFalse; - for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++) + FcDirCacheBasename (dir, cache_base); + + list = FcStrListCreate (config->cacheDirs); + if (!list) + return FcFalse; + + while ((cache_dir = FcStrListNext (list))) { - for (d = cache->ents[h]; d; d = next) - { - next = d->next; - FcGlobalCacheDirDestroy (d); + FcChar8 *cache_hashed = FcStrPlus (cache_dir, cache_base); + if (!cache_hashed) + break; + fd = open((char *) cache_hashed, O_RDONLY | O_BINARY); + FcStrFree (cache_hashed); + if (fd >= 0) { + if (fstat (fd, &file_stat) >= 0 && + dir_stat.st_mtime <= file_stat.st_mtime) + { + ret = (*callback) (fd, file_stat.st_size, closure); + if (ret) + { + close (fd); + break; + } + } + close (fd); } } - FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache)); - free (cache); + FcStrListDone (list); + + return ret; } -/* - * Cache file syntax is quite simple: - * - * "file_name" id time "font_name" \n - */ - -void -FcGlobalCacheLoad (FcGlobalCache *cache, - const FcChar8 *cache_file) +static FcBool +FcDirCacheLoad (int fd, off_t size, void *closure) { - FILE *f; - FcChar8 file_buf[8192], *file; - int id; - time_t time; - FcChar8 name_buf[8192], *name; - FcGlobalCacheInfo *info; - - f = fopen ((char *) cache_file, "r"); - if (!f) - return; + FcCache *cache; + FcBool allocated = FcFalse; - cache->updated = FcFalse; - file = 0; - name = 0; - while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) && - FcCacheReadInt (f, &id) && - FcCacheReadTime (f, &time) && - (name = FcCacheReadString (f, name_buf, sizeof (name_buf)))) + if (size < sizeof (FcCache)) + return FcFalse; +#if defined(HAVE_MMAP) || defined(__CYGWIN__) + cache = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0); +#elif defined(_WIN32) { - if (FcDebug () & FC_DBG_CACHEV) - printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name); - if (!FcStrCmp (name, FC_FONT_FILE_DIR)) - info = FcGlobalCacheDirAdd (cache, file, time, FcFalse, FcTrue); - else - info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse); - if (!info) - cache->broken = FcTrue; + HANDLE hFileMap; + + cache = NULL; + hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL, + PAGE_READONLY, 0, 0, NULL); + if (hFileMap != NULL) + { + cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, size); + CloseHandle (hFileMap); + } + } +#endif + if (!cache) + { + cache = malloc (size); + if (!cache) + return FcFalse; + + if (read (fd, cache, size) != size) + { + free (cache); + return FcFalse; + } + allocated = FcTrue; + } + if (cache->magic != FC_CACHE_MAGIC || + cache->size != size) + { + if (allocated) + free (cache); else - cache->entries++; - if (FcDebug () & FC_DBG_CACHE_REF) - printf ("FcGlobalCacheLoad entry %d %s\n", - cache->entries, file); - if (file != file_buf) - free (file); - if (name != name_buf) - free (name); - file = 0; - name = 0; + { +#if defined(HAVE_MMAP) || defined(__CYGWIN__) + munmap (cache, size); +#elif defined(_WIN32) + UnmapViewOfFile (cache); +#endif + } + return FcFalse; } - if (file && file != file_buf) - free (file); - if (name && name != name_buf) - free (name); - fclose (f); + + /* Mark allocated caches so they're freed rather than unmapped */ + if (allocated) + cache->magic = FC_CACHE_MAGIC_COPY; + + *((FcCache **) closure) = cache; + return FcTrue; } -FcBool -FcGlobalCacheUpdate (FcGlobalCache *cache, - const FcChar8 *file, - int id, - const FcChar8 *name) +FcCache * +FcDirCacheMap (int fd, off_t size) { - const FcChar8 *match; - struct stat statb; - FcGlobalCacheInfo *info; - - match = file; + FcCache *cache; - if (stat ((char *) file, &statb) < 0) - return FcFalse; - if (S_ISDIR (statb.st_mode)) - info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime, - FcTrue, FcTrue); - else - info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime, - name, FcTrue); - if (info) - { - FcGlobalCacheReferenced (cache, info); - cache->updated = FcTrue; - } - else - cache->broken = FcTrue; - return info != 0; + if (FcDirCacheLoad (fd, size, &cache)) + return cache; + return NULL; } FcBool -FcGlobalCacheSave (FcGlobalCache *cache, - const FcChar8 *cache_file) +FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, + const FcChar8 *dir, FcConfig *config) { - FILE *f; - int dir_hash, file_hash; - FcGlobalCacheDir *dir; - FcGlobalCacheFile *file; - FcAtomic *atomic; - - if (!cache->updated && cache->referenced == cache->entries) - return FcTrue; - - if (cache->broken) - return FcFalse; + FcCache *cache; + int i; + FcFontSet *cache_set; + intptr_t *cache_dirs; + FcPattern **cache_fonts; -#if defined (HAVE_GETUID) && defined (HAVE_GETEUID) - /* Set-UID programs can't safely update the cache */ - if (getuid () != geteuid ()) + if (!FcDirCacheProcess (config, dir, + FcDirCacheLoad, + &cache)) return FcFalse; -#endif - atomic = FcAtomicCreate (cache_file); - if (!atomic) - goto bail0; - if (!FcAtomicLock (atomic)) - goto bail1; - f = fopen ((char *) FcAtomicNewFile(atomic), "w"); - if (!f) - goto bail2; - - for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++) + cache_set = FcCacheSet (cache); + cache_fonts = FcFontSetFonts(cache_set); + if (FcDebug() & FC_DBG_CACHE) + printf ("FcDirCacheRead mapped cache for %s (%d fonts %d subdirs)\n", + dir, cache_set->nfont, cache->dirs_count); + for (i = 0; i < cache_set->nfont; i++) { - for (dir = cache->ents[dir_hash]; dir; dir = dir->next) - { - if (!dir->info.referenced) - continue; - if (!FcCacheWriteString (f, dir->info.file)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteInt (f, 0)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteTime (f, dir->info.time)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR)) - goto bail4; - if (PUTC ('\n', f) == EOF) - goto bail4; - - for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++) - { - for (file = dir->ents[file_hash]; file; file = file->next) - { - if (!file->info.referenced) - continue; - if (!FcCacheWritePath (f, dir->info.file, file->info.file)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteTime (f, file->info.time)) - goto bail4; - if (PUTC (' ', f) == EOF) - goto bail4; - if (!FcCacheWriteString (f, file->name)) - goto bail4; - if (PUTC ('\n', f) == EOF) - goto bail4; - } - } + FcPattern *font = FcEncodedOffsetToPtr (cache_set, + cache_fonts[i], + FcPattern); + if (FcDebug() & FC_DBG_CACHEV) { + printf ("Mapped font %d\n", i); + FcPatternPrint (font); } + FcFontSetAdd (set, font); } - - if (fclose (f) == EOF) - goto bail3; - if (!FcAtomicReplaceOrig (atomic)) - goto bail3; + cache_dirs = FcCacheDirs (cache); + for (i = 0; i < cache->dirs_count; i++) + FcStrSetAdd (dirs, FcOffsetToPtr (cache_dirs, + cache_dirs[i], + FcChar8)); + + if (config) + FcConfigAddFontDir (config, (FcChar8 *)dir); - FcAtomicUnlock (atomic); - FcAtomicDestroy (atomic); - - cache->updated = FcFalse; return FcTrue; - -bail4: - fclose (f); -bail3: - FcAtomicDeleteNew (atomic); -bail2: - FcAtomicUnlock (atomic); -bail1: - FcAtomicDestroy (atomic); -bail0: - return FcFalse; } -#endif - -/* - * Find the next presumably-mmapable offset after the current file - * pointer. - */ -int -FcCacheNextOffset(int fd) + +static FcBool +FcDirCacheValidate (int fd, off_t size, void *closure) { - off_t w; - w = lseek(fd, 0, SEEK_END); - - if (w % PAGESIZE == 0) - return w; - else - return ((w / PAGESIZE)+1)*PAGESIZE; + FcBool ret = FcTrue; + FcCache c; + struct stat file_stat; + + if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache)) + ret = FcFalse; + else if (fstat (fd, &file_stat) < 0) + ret = FcFalse; + else if (c.magic != FC_CACHE_MAGIC) + ret = FcFalse; + else if (file_stat.st_size != c.size) + ret = FcFalse; + return ret; } -/* will go away once we use config->cache */ -#define CACHE_DEFAULT_TMPDIR "/tmp" -#define CACHE_DEFAULT_NAME "/fontconfig-mmap" -static char * -FcCacheFilename(void) +FcBool +FcDirCacheValid (const FcChar8 *dir, FcConfig *config) { - struct utsname b; - static char * name = 0; - - if (name) - return name; - - if (uname(&b) == -1) - name = CACHE_DEFAULT_NAME; - else - { - char * tmpname = getenv("TMPDIR"); - char * logname = getenv("LOGNAME"); - if (!tmpname) - tmpname = CACHE_DEFAULT_TMPDIR; - - name = malloc(strlen(CACHE_DEFAULT_NAME) + - strlen(tmpname) + - (logname ? strlen(logname) : 0) + 5); - strcpy(name, tmpname); - strcat(name, CACHE_DEFAULT_NAME); - strcat(name, "-"); - strcat(name, logname ? logname : ""); - } - return name; + return FcDirCacheProcess (config, dir, FcDirCacheValidate, NULL); } -/* - * Wipe out static state. - */ void -FcCacheClearStatic() +FcDirCacheUnmap (FcCache *cache) { - FcFontSetClearStatic(); - FcPatternClearStatic(); - FcValueListClearStatic(); - FcObjectClearStatic(); - FcMatrixClearStatic(); - FcCharSetClearStatic(); - FcLangSetClearStatic(); + if (cache->magic == FC_CACHE_MAGIC_COPY) + { + free (cache); + return; + } +#if defined(HAVE_MMAP) || defined(__CYGWIN__) + munmap (cache, cache->size); +#elif defined(_WIN32) + UnmapViewOfFile (cache); +#endif } /* - * Trigger the counting phase: this tells us how much to allocate. + * Cache file is: + * + * FcCache + * dir name + * subdirs + * FcFontSet */ -FcBool -FcCachePrepareSerialize (FcConfig * config) -{ - int i; - for (i = FcSetSystem; i <= FcSetApplication; i++) - if (config->fonts[i] && !FcFontSetPrepareSerialize(config->fonts[i])) - return FcFalse; - return FcTrue; -} -/* allocate and populate static structures */ -FcBool -FcCacheSerialize (FcConfig * config) +static FcCache * +FcDirCacheProduce (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs) { + FcSerialize *serialize = FcSerializeCreate (); + FcCache *cache; int i; - for (i = FcSetSystem; i <= FcSetApplication; i++) - if (config->fonts[i] && !FcFontSetSerialize(config->fonts[i])) - return FcFalse; - return FcTrue; + intptr_t cache_offset; + intptr_t dirs_offset; + FcChar8 *dir_serialize; + intptr_t *dirs_serialize; + FcFontSet *set_serialize; + + if (!serialize) + return NULL; + /* + * Space for cache structure + */ + cache_offset = FcSerializeReserve (serialize, sizeof (FcCache)); + /* + * Directory name + */ + if (!FcStrSerializeAlloc (serialize, dir)) + goto bail1; + /* + * Subdirs + */ + dirs_offset = FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *)); + for (i = 0; i < dirs->num; i++) + if (!FcStrSerializeAlloc (serialize, dirs->strs[i])) + goto bail1; + + /* + * Patterns + */ + if (!FcFontSetSerializeAlloc (serialize, set)) + goto bail1; + + /* Serialize layout complete. Now allocate space and fill it */ + cache = malloc (serialize->size); + if (!cache) + goto bail1; + /* shut up valgrind */ + memset (cache, 0, serialize->size); + + serialize->linear = cache; + + cache->magic = FC_CACHE_MAGIC; + cache->size = serialize->size; + + /* + * Serialize directory name + */ + dir_serialize = FcStrSerialize (serialize, dir); + if (!dir_serialize) + goto bail2; + cache->dir = FcPtrToOffset (cache, dir_serialize); + + /* + * Serialize sub dirs + */ + dirs_serialize = FcSerializePtr (serialize, dirs); + if (!dirs_serialize) + goto bail2; + cache->dirs = FcPtrToOffset (cache, dirs_serialize); + cache->dirs_count = dirs->num; + for (i = 0; i < dirs->num; i++) + { + FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]); + if (!d_serialize) + goto bail2; + dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize); + } + + /* + * Serialize font set + */ + set_serialize = FcFontSetSerialize (serialize, set); + if (!set_serialize) + goto bail2; + cache->set = FcPtrToOffset (cache, set_serialize); + + FcSerializeDestroy (serialize); + + return cache; + +bail2: + free (cache); +bail1: + FcSerializeDestroy (serialize); + return NULL; } -/* get the current arch name */ -/* caller is responsible for freeing returned pointer */ -static char * -FcCacheGetCurrentArch (void) +static FcBool +FcMakeDirectory (const FcChar8 *dir) { - struct utsname b; - char * current_arch_machine_name; - - if (uname(&b) == -1) + FcChar8 *parent; + FcBool ret; + + if (strlen ((char *) dir) == 0) + return FcFalse; + + parent = FcStrDirname (dir); + if (!parent) return FcFalse; - current_arch_machine_name = strdup(b.machine); - /* if (getenv ("FAKE_ARCH")) // testing purposes - current_arch_machine_name = strdup(getenv("FAKE_ARCH")); */ - return current_arch_machine_name; + if (access ((char *) parent, W_OK|X_OK) == 0) + ret = mkdir ((char *) dir, 0777) == 0; + else if (access ((char *) parent, F_OK) == -1) + ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0777) == 0); + else + ret = FcFalse; + FcStrFree (parent); + return ret; } -/* return the address of the segment for the provided arch, - * or -1 if arch not found */ -static off_t -FcCacheSkipToArch (int fd, const char * arch) +/* write serialized state to the cache file */ +FcBool +FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config) { - char candidate_arch_machine_name[64], bytes_to_skip[7]; - off_t current_arch_start = 0; - - /* skip arches that are not the current arch */ - while (1) - { - long bs; - - lseek (fd, current_arch_start, SEEK_SET); - if (FcCacheReadString (fd, candidate_arch_machine_name, - sizeof (candidate_arch_machine_name)) == 0) - break; - if (FcCacheReadString (fd, bytes_to_skip, 7) == 0) - break; - bs = a64l(bytes_to_skip); - if (bs == 0) - break; - - if (strcmp (candidate_arch_machine_name, arch)==0) + FcChar8 cache_base[CACHEBASE_LEN]; + FcChar8 *cache_hashed; + int fd; + FcAtomic *atomic; + FcCache *cache; + FcStrList *list; + FcChar8 *cache_dir = NULL; + FcChar8 *test_dir; + + /* + * Write it to the first directory in the list which is writable + */ + + list = FcStrListCreate (config->cacheDirs); + if (!list) + return FcFalse; + while ((test_dir = FcStrListNext (list))) { + if (access ((char *) test_dir, W_OK|X_OK) == 0) + { + cache_dir = test_dir; break; - current_arch_start += bs; + } + else + { + /* + * If the directory doesn't exist, try to create it + */ + if (access ((char *) test_dir, F_OK) == -1) { + if (FcMakeDirectory (test_dir)) + { + cache_dir = test_dir; + break; + } + } + } } + FcStrListDone (list); + if (!cache_dir) + return FcFalse; - if (strcmp (candidate_arch_machine_name, arch)!=0) - return -1; - - return current_arch_start; -} + FcDirCacheBasename (dir, cache_base); + cache_hashed = FcStrPlus (cache_dir, cache_base); + if (!cache_hashed) + return FcFalse; -/* Cuts out the segment at the file pointer (moves everything else - * down to cover it), and leaves the file pointer at the end of the - * file. */ -#define BUF_SIZE 8192 + cache = FcDirCacheProduce (set, dir, dirs); -static FcBool -FcCacheMoveDown (int fd, off_t start) -{ - char * buf = malloc (BUF_SIZE); - char candidate_arch_machine_name[64], bytes_to_skip[7]; - long bs; off_t pos; - int c, bytes_skipped; + if (!cache) + goto bail1; - if (!buf) - return FcFalse; + if (FcDebug () & FC_DBG_CACHE) + printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n", + dir, cache_hashed); - lseek (fd, start, SEEK_SET); - if (FcCacheReadString (fd, candidate_arch_machine_name, - sizeof (candidate_arch_machine_name)) == 0) - goto done; - if (FcCacheReadString (fd, bytes_to_skip, 7) == 0) - goto done; + atomic = FcAtomicCreate ((FcChar8 *)cache_hashed); + if (!atomic) + goto bail2; - bs = a64l(bytes_to_skip); - if (bs == 0) - goto done; + if (!FcAtomicLock (atomic)) + goto bail3; - bytes_skipped = 0; - do + fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666); + if (fd == -1) + goto bail4; + + if (write (fd, cache, cache->size) != cache->size) { - lseek (fd, start+bs+bytes_skipped, SEEK_SET); - if ((c = read (fd, buf, BUF_SIZE)) <= 0) - break; - lseek (fd, start+bytes_skipped, SEEK_SET); - if (write (fd, buf, c) < 0) - goto bail; - bytes_skipped += c; + perror ("write cache"); + goto bail5; } - while (c > 0); - lseek (fd, start+bytes_skipped, SEEK_SET); - done: - free (buf); + close(fd); + if (!FcAtomicReplaceOrig(atomic)) + goto bail4; + FcStrFree ((FcChar8 *)cache_hashed); + FcAtomicUnlock (atomic); + FcAtomicDestroy (atomic); return FcTrue; - bail: - free (buf); + bail5: + close (fd); + bail4: + FcAtomicUnlock (atomic); + bail3: + FcAtomicDestroy (atomic); + bail2: + free (cache); + bail1: + FcStrFree ((FcChar8 *)cache_hashed); return FcFalse; } -/* read serialized state from the cache file */ -FcBool -FcCacheRead (FcConfig *config) +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) { - int fd, i; - FcCache metadata; - char * current_arch_machine_name; - char candidate_arch_machine_name[64], bytes_in_block[7]; - off_t current_arch_start = 0; + FcChar32 t; + do { + t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(FcChar32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif - if (force) - return FcFalse; +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; - fd = open(FcCacheFilename(), O_RDONLY); - if (fd == -1) - return FcFalse; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} - current_arch_machine_name = FcCacheGetCurrentArch(); - current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); - if (current_arch_start < 0) - goto bail; +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len) +{ + FcChar32 t; - lseek (fd, current_arch_start, SEEK_SET); - if (FcCacheReadString (fd, candidate_arch_machine_name, - sizeof (candidate_arch_machine_name)) == 0) - goto bail; - if (FcCacheReadString (fd, bytes_in_block, 7) == 0) - goto bail; + /* Update bitcount */ - // sanity check for endianness issues - read(fd, &metadata, sizeof(FcCache)); - if (metadata.magic != FC_CACHE_MAGIC) - goto bail; - - if (!FcObjectRead(fd, metadata)) goto bail1; - if (!FcStrSetRead(fd, metadata)) goto bail1; - if (!FcCharSetRead(fd, metadata)) goto bail1; - if (!FcMatrixRead(fd, metadata)) goto bail1; - if (!FcLangSetRead(fd, metadata)) goto bail1; - if (!FcValueListRead(fd, metadata)) goto bail1; - if (!FcPatternEltRead(fd, metadata)) goto bail1; - if (!FcPatternRead(fd, metadata)) goto bail1; - if (!FcFontSetRead(fd, config, metadata)) goto bail1; - close(fd); - free (current_arch_machine_name); - return FcTrue; + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; - bail1: - for (i = FcSetSystem; i <= FcSetApplication; i++) - config->fonts[i] = 0; - close(fd); - bail: - free (current_arch_machine_name); - return FcFalse; -} + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ -/* write serialized state to the cache file */ -FcBool -FcCacheWrite (FcConfig * config) -{ - int fd; - FcCache metadata; - off_t current_arch_start = 0, truncate_to; - char * current_arch_machine_name, bytes_written[7] = "dedbef"; + /* Handle any leading odd-sized chunks */ - if (!FcCachePrepareSerialize (config)) - return FcFalse; + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; - if (!FcCacheSerialize (config)) - return FcFalse; + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (FcChar32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (FcChar32 *) ctx->in); + buf += 64; + len -= 64; + } - fd = open(FcCacheFilename(), O_RDWR | O_CREAT, 0666); - if (fd == -1) - return FcFalse; + /* Handle any remaining bytes of data. */ - current_arch_machine_name = FcCacheGetCurrentArch(); - current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name); - if (current_arch_start < 0) - current_arch_start = FcCacheNextOffset (fd); + memcpy(ctx->in, buf, len); +} - if (!FcCacheMoveDown(fd, current_arch_start)) - goto bail; +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (FcChar32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); - current_arch_start = lseek(fd, 0, SEEK_CUR); - if (ftruncate (fd, current_arch_start) == -1) - goto bail; + /* Append length in bits and transform */ + ((FcChar32 *) ctx->in)[14] = ctx->bits[0]; + ((FcChar32 *) ctx->in)[15] = ctx->bits[1]; - /* reserve space for arch, count & metadata */ - if (!FcCacheWriteString (fd, current_arch_machine_name)) - goto bail; - if (!FcCacheWriteString (fd, bytes_written)) - goto bail; - memset (&metadata, 0, sizeof(FcCache)); - metadata.magic = FC_CACHE_MAGIC; - write(fd, &metadata, sizeof(FcCache)); - - if (!FcFontSetWrite(fd, config, &metadata)) goto bail; - if (!FcPatternWrite(fd, &metadata)) goto bail; - if (!FcPatternEltWrite(fd, &metadata)) goto bail; - if (!FcValueListWrite(fd, &metadata)) goto bail; - if (!FcLangSetWrite(fd, &metadata)) goto bail; - if (!FcCharSetWrite(fd, &metadata)) goto bail; - if (!FcMatrixWrite(fd, &metadata)) goto bail; - if (!FcStrSetWrite(fd, &metadata)) goto bail; - if (!FcObjectWrite(fd, &metadata)) goto bail; - - /* now write the address of the next offset */ - truncate_to = FcCacheNextOffset(fd) - current_arch_start; - lseek(fd, current_arch_start + strlen(current_arch_machine_name)+1, - SEEK_SET); - strcpy (bytes_written, l64a(truncate_to)); - if (!FcCacheWriteString (fd, bytes_written)) - goto bail; + MD5Transform(ctx->buf, (FcChar32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} - /* now rewrite metadata & truncate file */ - if (write(fd, &metadata, sizeof(FcCache)) != sizeof (FcCache)) - goto bail; - if (ftruncate (fd, current_arch_start + truncate_to) == -1) - goto bail; - close(fd); - return FcTrue; +/* The four core functions - F1 is optimized somewhat */ - bail: - free (current_arch_machine_name); - return FcFalse; -} +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) -/* if true, ignore the cache file */ -void -FcCacheForce (FcBool f) +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]) { - force = f; + register FcChar32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; }