#endif
static int
-FcDirCacheOpen (const FcChar8 * dir);
-
-static char *
-FcDirCacheHashName (const FcChar8 * dir, int collisions);
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path);
static off_t
FcCacheSkipToArch (int fd, const char * arch);
* cache, and the hashed location has an up-to-date cache. Oh well,
* sucks to be you in that case! */
FcBool
-FcDirCacheValid (const FcChar8 *dir)
+FcDirCacheValid (const FcChar8 *dir, FcConfig *config)
{
struct stat file_stat, dir_stat;
int fd;
if (stat ((char *) dir, &dir_stat) < 0)
return FcFalse;
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
return FcFalse;
/* Assumes that the cache file in 'dir' exists.
* Checks that the cache has the appropriate arch section. */
FcBool
-FcDirCacheHasCurrentArch (const FcChar8 *dir)
+FcDirCacheHasCurrentArch (const FcChar8 *dir, FcConfig *config)
{
int fd;
off_t current_arch_start;
FcCache metadata;
char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
goto bail;
return FcFalse;
}
-FcBool
-FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+#define CACHEBASE_LEN (1 + 32 + sizeof (FC_CACHE_SUFFIX))
+
+static const char bin2hex[] = { '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f' };
+
+static FcChar8 *
+FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
{
- char *cache_hashed = 0;
- int fd, collisions;
- struct stat cache_stat;
- char dir_buf[FC_MAX_FILE_LEN];
+ unsigned char hash[16];
+ FcChar8 *hex_hash;
+ int cnt;
+ struct MD5Context ctx;
- dir = FcConfigNormalizeFontDir (config, dir);
+ MD5Init (&ctx);
+ MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir));
- /* Remove any applicable hashed files. */
- fd = -1; collisions = 0;
- do
+ MD5Final (hash, &ctx);
+
+ cache_base[0] = '/';
+ hex_hash = cache_base + 1;
+ for (cnt = 0; cnt < 16; ++cnt)
{
- if (cache_hashed)
- FcStrFree ((FcChar8 *)cache_hashed);
+ hex_hash[2*cnt ] = bin2hex[hash[cnt] >> 4];
+ hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
+ }
+ hex_hash[2*cnt] = 0;
+ strcat ((char *) cache_base, FC_CACHE_SUFFIX);
- cache_hashed = FcDirCacheHashName (dir, collisions++);
- if (!cache_hashed)
- goto bail;
+ return cache_base;
+}
- if (fd > 0)
- close (fd);
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- if (fd == -1)
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- return FcTrue;
- }
+FcBool
+FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+{
+ int fd = -1;
+ FcChar8 *cache_hashed = NULL;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcStrList *list;
+ FcChar8 *cache_dir;
+ char dir_buf[FC_MAX_FILE_LEN];
- if (!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) || !strlen(dir_buf))
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- goto bail;
- }
- } while (strcmp ((char *) dir_buf, (char *) dir) != 0);
+ dir = FcConfigNormalizeFontDir (config, dir);
- close (fd);
+ FcDirCacheBasename (dir, cache_base);
- if (stat ((char *) cache_hashed, &cache_stat) == 0 &&
- unlink ((char *)cache_hashed) != 0)
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return FcFalse;
+
+ while ((cache_dir = FcStrListNext (list)))
{
- FcStrFree ((FcChar8 *)cache_hashed);
- goto bail;
+ 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 (FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) &&
+ strcmp (dir_buf, (char *) dir) == 0)
+ {
+ close (fd);
+ if (unlink ((char *) cache_hashed) < 0)
+ break;
+ } else
+ close (fd);
+ }
}
-
- FcStrFree ((FcChar8 *)cache_hashed);
+ FcStrListDone (list);
+ /* return FcFalse if something went wrong */
+ if (cache_dir)
+ return FcFalse;
return FcTrue;
-
- bail:
- return FcFalse;
}
static int
FcStrSetDestroy (subdirs);
continue;
}
- if (FcDirCacheValid (dir) &&
- FcDirCacheHasCurrentArch (dir) &&
+ if (FcDirCacheValid (dir, config) &&
+ FcDirCacheHasCurrentArch (dir, config) &&
FcDirCacheRead (set, subdirs, dir, config))
{
/* if an old entry is found in the global cache, disable it */
return 0;
}
-static const char bin2hex[] = { '0', '1', '2', '3',
- '4', '5', '6', '7',
- '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f' };
-
-static char *
-FcDirCacheHashName (const FcChar8 * dir, int collisions)
-{
- unsigned char hash[16], hex_hash[33];
- char *cache_hashed;
- unsigned char uscore = '_';
- int cnt, i;
- FcChar8 *tmp;
- struct MD5Context ctx;
-
- MD5Init (&ctx);
- MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir));
-
- for (i = 0; i < collisions; i++)
- MD5Update (&ctx, &uscore, 1);
-
- MD5Final (hash, &ctx);
-
- for (cnt = 0; cnt < 16; ++cnt)
- {
- hex_hash[2*cnt] = bin2hex[hash[cnt] >> 4];
- hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
- }
- hex_hash[32] = 0;
-
- tmp = FcStrPlus ((FcChar8 *)hex_hash, (FcChar8 *)FC_CACHE_SUFFIX);
- if (!tmp)
- return 0;
-
- cache_hashed = (char *)FcStrPlus ((FcChar8 *)PKGCACHEDIR"/", tmp);
- free (tmp);
-
- return cache_hashed;
-}
-
-/* Opens the hashed name for cache_file.
- * This would fail in the unlikely event of a collision and subsequent
- * removal of the file which originally caused the collision. */
+/* Opens the cache file for 'dir' for reading.
+ * This searches the list of cache dirs for the relevant cache file,
+ * returning the first one found.
+ */
static int
-FcDirCacheOpen (const FcChar8 *dir)
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path)
{
- FcBool found;
- int fd = -1, collisions = 0;
- char *cache_hashed;
+ int fd = -1;
+ FcChar8 *cache_hashed = NULL;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcStrList *list;
+ FcChar8 *cache_dir;
char dir_buf[FC_MAX_FILE_LEN];
- struct stat dir_stat;
- if (stat ((char *)dir, &dir_stat) == -1)
- return -1;
+ FcDirCacheBasename (dir, cache_base);
- found = FcFalse;
- do
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return -1;
+
+ while ((cache_dir = FcStrListNext (list)))
{
- struct stat c;
-
- if (fd >= 0)
- close (fd);
-
- cache_hashed = FcDirCacheHashName (dir, collisions++);
- if (!cache_hashed)
- return -1;
-
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- FcStrFree ((FcChar8 *)cache_hashed);
-
- if (fd == -1)
+ cache_hashed = FcStrPlus (cache_dir, cache_base);
+ if (!cache_hashed)
break;
- if (!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) ||
- !strlen(dir_buf))
+ fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
+ if (fd >= 0)
break;
+ FcStrFree (cache_hashed);
+ }
+ FcStrListDone (list);
- if (stat ((char *)dir_buf, &c) == -1)
- continue;
-
- found = (c.st_ino == dir_stat.st_ino) && (c.st_dev == dir_stat.st_dev);
- } while (!found);
+ if (fd < 0)
+ return -1;
+
+ if (!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) ||
+ strcmp (dir_buf, (char *) dir) != 0)
+ {
+ close (fd);
+ FcStrFree (cache_hashed);
+ return -1;
+ }
+
+ if (cache_path)
+ *cache_path = cache_hashed;
+ else
+ FcStrFree (cache_hashed);
return fd;
}
off_t current_arch_start = 0;
char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
goto bail;
return 0;
}
+static FcBool
+FcMakeDirectory (const FcChar8 *dir)
+{
+ FcChar8 *parent;
+ FcBool ret;
+
+ if (strlen ((char *) dir) == 0)
+ return FcFalse;
+
+ parent = FcStrDirname (dir);
+ if (!parent)
+ return FcFalse;
+ 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;
+}
+
/* write serialized state to the cache file */
FcBool
-FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
+FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config)
{
- char *cache_hashed;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcChar8 *cache_hashed;
int fd, fd_orig, i, dirs_count;
FcAtomic *atomic;
FcCache metadata;
off_t current_arch_start = 0, truncate_to;
- char dir_buf[FC_MAX_FILE_LEN];
- int collisions;
-
+ FcStrList *list;
char *current_arch_machine_name, * header;
void *current_dir_block = 0;
+ FcChar8 *cache_dir;
dir = FcConfigNormalizeFontDir (FcConfigGetCurrent(), dir);
if (!dir)
return FcFalse;
- /* Ensure that we're not trampling a cache for some other dir. */
- /* This is slightly different from FcDirCacheOpen, since it
- * needs the filename, not the file descriptor. */
- fd = -1; collisions = 0;
- do
- {
- cache_hashed = FcDirCacheHashName (dir, collisions++);
- if (!cache_hashed)
- goto bail0;
-
- if (fd > 0)
- close (fd);
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- if (fd == -1)
- break;
- if(!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) || !strlen(dir_buf))
- {
- close (fd);
- FcStrFree ((FcChar8 *)cache_hashed);
- continue;
- }
- close (fd);
-
- if (strcmp (dir_buf, (char *) dir) != 0)
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- continue;
- }
+ /*
+ * Check for an existing cache file and dump stuff in the same place
+ */
+ fd = FcDirCacheOpen (config, dir, &cache_hashed);
- break;
- } while (1);
+ if (fd >= 0)
+ close (fd);
+ else {
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return FcFalse;
+ while ((cache_dir = FcStrListNext (list))) {
+ if (access ((char *) cache_dir, W_OK|X_OK) == 0)
+ break;
+ /*
+ * If the directory doesn't exist, try to create it
+ */
+ if (access ((char *) cache_dir, F_OK) == -1) {
+ if (FcMakeDirectory (cache_dir))
+ break;
+ }
+ }
+ FcStrListDone (list);
+ if (!cache_dir)
+ return FcFalse;
+ FcDirCacheBasename (dir, cache_base);
+ cache_hashed = FcStrPlus (cache_dir, cache_base);
+ if (!cache_hashed)
+ return FcFalse;
+ }
current_dir_block = FcDirCacheProduce (set, &metadata);
FcAtomicDestroy (atomic);
bail1:
FcStrFree ((FcChar8 *)cache_hashed);
- bail0:
if (current_dir_block)
free (current_dir_block);
return FcFalse;