2006-08-04 Keith Packard (keithp@keithp.com) reviewed by: plam

Make cache directories configurable. Simplify and correct some code which
    deals with per-directory caches.
This commit is contained in:
Patrick Lam 2006-08-04 16:13:00 +00:00
parent 62a4a8459a
commit 7410e40bd9
15 changed files with 293 additions and 178 deletions

View File

@ -1,3 +1,27 @@
2006-08-04 Keith Packard (keithp@keithp.com)
reviewed by: plam
* configure.in:
* fonts.conf.in:
* fonts.dtd:
* fc-cache/fc-cache.c (scanDirs):
* fontconfig/fontconfig.h:
* src/Makefile.am:
* src/fcint.h:
* src/fccache.c (FcDirCacheValid, FcDirHasCurrentArch,
FcDirCacheUnlink, FcDirCacheBasename,
FcCacheReadDirs, FcDirCacheOpen, FcDirCacheRead,
FcMakeDirectory, FcDirCacheWrite):
* src/fccfg.c (FcConfigCreate, FcConfigDestroy, FcConfigAddCacheDir,
FcConfigGetCacheDirs):
* src/fcdir.c (FcDirScanConfig, FcDirSave):
* src/fcinit.c (FcInitFallbackConfig):
* src/fcxml.c (fcElementMap, FcEndElement):
* test/fonts.conf.in, test/run-test.sh:
Make cache directories configurable. Simplify and correct
some code which deals with per-directory caches.
2006-07-19 Jon Burgess (jburgess@uklinux.net)
reviewed by: plam

View File

@ -431,6 +431,22 @@ esac
AC_SUBST(FC_FONTPATH)
#
# Set default cache directory path
#
AC_ARG_WITH(cache-dir, [ --with-cache-dir=DIR Use DIR to store cache files (default /var/cache/fontconfig)], fc_cachedir="$withval", fc_cachedir=yes)
case $fc_cachedir in
no|yes)
fc_cachedir=`eval echo "${localstatedir}/cache/"${PACKAGE}`
;;
*)
;;
esac
AC_SUBST(fc_cachedir)
FC_CACHEDIR=${fc_cachedir}
AC_SUBST(FC_CACHEDIR)
FC_FONTDATE=`LC_ALL=C date`
AC_SUBST(FC_FONTDATE)
@ -516,12 +532,6 @@ fi
AC_SUBST(DOCDIR)
#
# Make /var/cache/fontconfig directory available to source code
#
pkgcachedir='${localstatedir}/cache/'${PACKAGE}
AC_SUBST(pkgcachedir)
AC_OUTPUT([
Makefile

View File

@ -230,7 +230,8 @@ scanDirs (FcStrList *list, FcConfig *config, char *program, FcBool force, FcBool
ret++;
continue;
}
if (!force && FcDirCacheValid (dir) && FcDirCacheHasCurrentArch (dir))
if (!force && FcDirCacheValid (dir, config) &&
FcDirCacheHasCurrentArch (dir, config))
{
if (verbose)
printf ("skipping, %d fonts, %d dirs\n",
@ -244,14 +245,12 @@ scanDirs (FcStrList *list, FcConfig *config, char *program, FcBool force, FcBool
/* This is the only reason we can't combine
* Valid w/HasCurrentArch... */
if (!FcDirCacheValid (dir))
if (!FcDirCacheValid (dir, config))
if (!FcDirCacheUnlink (dir, config))
ret++;
if (!FcDirSave (set, subdirs, dir))
{
if (!ret)
fprintf (stderr, "Caches are currently saved to \"%s\"\n", PKGCACHEDIR);
fprintf (stderr, "Can't save cache for \"%s\"\n", dir);
ret++;
}

View File

@ -276,10 +276,10 @@ typedef struct _FcStrSet FcStrSet;
_FCFUNCPROTOBEGIN
FcBool
FcDirCacheValid (const FcChar8 *cache_file);
FcDirCacheValid (const FcChar8 *cache_file, FcConfig *config);
FcBool
FcDirCacheHasCurrentArch (const FcChar8 *dir);
FcDirCacheHasCurrentArch (const FcChar8 *dir, FcConfig *config);
FcBool
FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config);

View File

@ -27,6 +27,11 @@
@FC_FONTPATH@
<dir>~/.fonts</dir>
<!-- Font cache directory list -->
<cachedir>@FC_CACHEDIR@</cachedir>
<cachedir>~/.fonts/fontconfig</cachedir>
<!--
Accept deprecated 'mono' alias, replacing it with 'monospace'
-->

View File

@ -22,6 +22,18 @@
<!ELEMENT cache (#PCDATA)>
<!ATTLIST cache xml:space (default|preserve) 'preserve'>
<!--
Add a directory that is searched for font cache files.
These hold per-directory cache data and are searched in
order for each directory. When writing cache files, the first
directory which allows the cache file to be created is used.
A leading '~' in a directory name is replaced with the users
home directory path.
-->
<!ELEMENT cachedir (#PCDATA)>
<!ATTLIST cachedir xml:space (default|preserve) 'preserve'>
<!--
Reference another configuration file; note that this
is another complete font configuration file and not

View File

@ -63,8 +63,6 @@ uninstall-ms-import-lib:
endif
AM_CPPFLAGS = -DPKGCACHEDIR='"${pkgcachedir}"'
INCLUDES = \
-I$(top_srcdir) \
-I$(top_srcdir)/src \
@ -72,6 +70,7 @@ INCLUDES = \
$(LIBXML2_CFLAGS) \
$(EXPAT_CFLAGS) \
$(WARN_CFLAGS) \
-DFC_CACHEDIR='"$(FC_CACHEDIR)"' \
-DFONTCONFIG_PATH='"$(CONFDIR)"'
EXTRA_DIST = fontconfig.def.in

View File

@ -45,10 +45,7 @@
#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);
@ -748,7 +745,7 @@ FcCacheCopyOld (int fd, int fd_orig, off_t start)
* 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;
@ -756,7 +753,7 @@ FcDirCacheValid (const FcChar8 *dir)
if (stat ((char *) dir, &dir_stat) < 0)
return FcFalse;
fd = FcDirCacheOpen (dir);
fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
return FcFalse;
@ -782,7 +779,7 @@ FcDirCacheValid (const FcChar8 *dir)
/* 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;
@ -790,7 +787,7 @@ FcDirCacheHasCurrentArch (const FcChar8 *dir)
FcCache metadata;
char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
fd = FcDirCacheOpen (dir);
fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
goto bail;
@ -827,57 +824,81 @@ FcDirCacheHasCurrentArch (const FcChar8 *dir)
return FcFalse;
}
#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])
{
unsigned char hash[16];
FcChar8 *hex_hash;
int cnt;
struct MD5Context ctx;
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)
{
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);
return cache_base;
}
FcBool
FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
{
char *cache_hashed = 0;
int fd, collisions;
struct stat cache_stat;
int fd = -1;
FcChar8 *cache_hashed = NULL;
FcChar8 cache_base[CACHEBASE_LEN];
FcStrList *list;
FcChar8 *cache_dir;
char dir_buf[FC_MAX_FILE_LEN];
dir = FcConfigNormalizeFontDir (config, dir);
/* Remove any applicable hashed files. */
fd = -1; collisions = 0;
do
FcDirCacheBasename (dir, cache_base);
list = FcStrListCreate (config->cacheDirs);
if (!list)
return FcFalse;
while ((cache_dir = FcStrListNext (list)))
{
if (cache_hashed)
FcStrFree ((FcChar8 *)cache_hashed);
cache_hashed = FcDirCacheHashName (dir, collisions++);
if (!cache_hashed)
goto bail;
if (fd > 0)
close (fd);
fd = open(cache_hashed, O_RDONLY | O_BINARY);
if (fd == -1)
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)
{
FcStrFree ((FcChar8 *)cache_hashed);
return FcTrue;
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);
}
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);
close (fd);
if (stat ((char *) cache_hashed, &cache_stat) == 0 &&
unlink ((char *)cache_hashed) != 0)
{
FcStrFree ((FcChar8 *)cache_hashed);
goto bail;
}
FcStrFree ((FcChar8 *)cache_hashed);
FcStrListDone (list);
/* return FcFalse if something went wrong */
if (cache_dir)
return FcFalse;
return FcTrue;
bail:
return FcFalse;
}
static int
@ -949,8 +970,8 @@ FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache,
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 */
@ -1009,87 +1030,53 @@ FcCacheRead (FcConfig *config, FcGlobalCache * cache)
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;
}
@ -1104,7 +1091,7 @@ FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir, FcConfig *
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;
@ -1278,56 +1265,77 @@ FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
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;
/*
* Check for an existing cache file and dump stuff in the same place
*/
fd = FcDirCacheOpen (config, dir, &cache_hashed);
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;
}
if (fd >= 0)
close (fd);
if (strcmp (dir_buf, (char *) dir) != 0)
{
FcStrFree ((FcChar8 *)cache_hashed);
continue;
}
break;
} while (1);
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);
@ -1455,7 +1463,6 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
FcAtomicDestroy (atomic);
bail1:
FcStrFree ((FcChar8 *)cache_hashed);
bail0:
if (current_dir_block)
free (current_dir_block);
return FcFalse;

View File

@ -100,13 +100,17 @@ FcConfigCreate (void)
if (!FcConfigSetCache (config, cache_dir))
{
FcStrFree (cache_dir);
goto bail6;
goto bail8;
}
FcStrFree (cache_dir);
}
}
#endif
config->cacheDirs = FcStrSetCreate ();
if (!config->cacheDirs)
goto bail9;
config->blanks = 0;
config->substPattern = 0;
@ -120,6 +124,8 @@ FcConfigCreate (void)
return config;
bail9:
FcStrFree (config->cache);
bail8:
FcFontSetDestroy (config->rejectPatterns);
bail7:
@ -226,6 +232,7 @@ FcConfigDestroy (FcConfig *config)
FcStrSetDestroy (config->configDirs);
FcStrSetDestroy (config->fontDirs);
FcStrSetDestroy (config->cacheDirs);
FcStrSetDestroy (config->configFiles);
FcStrSetDestroy (config->acceptGlobs);
FcStrSetDestroy (config->rejectGlobs);
@ -512,6 +519,25 @@ FcConfigGetFontDirs (FcConfig *config)
return FcStrListCreate (config->fontDirs);
}
FcBool
FcConfigAddCacheDir (FcConfig *config,
const FcChar8 *d)
{
return FcStrSetAddFilename (config->cacheDirs, d);
}
FcStrList *
FcConfigGetCacheDirs (FcConfig *config)
{
if (!config)
{
config = FcConfigGetCurrent ();
if (!config)
return 0;
}
return FcStrListCreate (config->cacheDirs);
}
FcBool
FcConfigAddConfigFile (FcConfig *config,
const FcChar8 *f)

View File

@ -151,8 +151,8 @@ FcDirScanConfig (FcFontSet *set,
if (cache && FcGlobalCacheReadDir (set, dirs, cache, (char *)dir, config))
return FcTrue;
if (FcDirCacheValid (dir) &&
FcDirCacheHasCurrentArch (dir) &&
if (FcDirCacheValid (dir, config) &&
FcDirCacheHasCurrentArch (dir, config) &&
FcDirCacheRead (set, dirs, dir, config))
return FcTrue;
}
@ -277,5 +277,5 @@ FcDirScan (FcFontSet *set,
FcBool
FcDirSave (FcFontSet *set, FcStrSet * dirs, const FcChar8 *dir)
{
return FcDirCacheWrite (set, dirs, dir);
return FcDirCacheWrite (set, dirs, dir, FcConfigGetCurrent ());
}

View File

@ -35,6 +35,8 @@ FcInitFallbackConfig (void)
goto bail0;
if (!FcConfigAddDir (config, (FcChar8 *) FC_DEFAULT_FONTS))
goto bail1;
if (!FcConfigAddCacheDir (config, (FcChar8 *) FC_CACHEDIR))
goto bail1;
return config;
bail1:

View File

@ -388,6 +388,10 @@ struct _FcConfig {
* of configured directories
*/
FcStrSet *fontDirs;
/*
* List of directories containing cache files.
*/
FcStrSet *cacheDirs;
/*
* Names of all of the configuration files used
* to create this configuration
@ -476,7 +480,7 @@ FcFontSet *
FcCacheRead (FcConfig *config, FcGlobalCache * cache);
FcBool
FcDirCacheWrite (FcFontSet *set, FcStrSet * dirs, const FcChar8 *dir);
FcDirCacheWrite (FcFontSet *set, FcStrSet * dirs, const FcChar8 *dir, FcConfig *config);
FcBool
FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir, FcConfig *config);
@ -508,6 +512,13 @@ FcBool
FcConfigAddDir (FcConfig *config,
const FcChar8 *d);
FcBool
FcConfigAddCacheDir (FcConfig *config,
const FcChar8 *d);
FcStrList *
FcConfigGetCacheDirs (FcConfig *config);
FcBool
FcConfigAddConfigFile (FcConfig *config,
const FcChar8 *f);

View File

@ -285,6 +285,7 @@ typedef enum _FcElement {
FcElementNone,
FcElementFontconfig,
FcElementDir,
FcElementCacheDir,
FcElementCache,
FcElementInclude,
FcElementConfig,
@ -345,6 +346,7 @@ static const struct {
} fcElementMap[] = {
{ "fontconfig", FcElementFontconfig },
{ "dir", FcElementDir },
{ "cachedir", FcElementCacheDir },
{ "cache", FcElementCache },
{ "include", FcElementInclude },
{ "config", FcElementConfig },
@ -2053,6 +2055,21 @@ FcEndElement(void *userData, const XML_Char *name)
}
FcStrFree (data);
break;
case FcElementCacheDir:
data = FcStrBufDone (&parse->pstack->str);
if (!data)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
if (!FcStrUsesHome (data) || FcConfigHome ())
{
if (!FcConfigAddCacheDir (parse->config, data))
FcConfigMessage (parse, FcSevereError, "out of memory; cannot add cache directory %s", data);
}
FcStrFree (data);
break;
case FcElementCache:
data = FcStrBufDone (&parse->pstack->str);
if (!data)

View File

@ -1,4 +1,5 @@
<fontconfig>
<dir>@FONTDIR@</dir>
<cache>@CACHEFILE@</cache>
<cachedir>@CACHEDIR@</cachedir>
</fontconfig>

View File

@ -3,6 +3,7 @@ TESTDIR=${srcdir-`pwd`}
FONTDIR=`pwd`/fonts
CACHEFILE=`pwd`/fonts.cache
CACHEDIR=`pwd`/cache.dir
ECHO=true
@ -39,7 +40,8 @@ dotest () {
}
sed "s!@FONTDIR@!$FONTDIR!
s!@CACHEFILE@!$CACHEFILE!" < $TESTDIR/fonts.conf.in > fonts.conf
s!@CACHEFILE@!$CACHEFILE!
s!@CACHEDIR@!$CACHEDIR!" < $TESTDIR/fonts.conf.in > fonts.conf
FONTCONFIG_FILE=`pwd`/fonts.conf
export FONTCONFIG_FILE
@ -89,4 +91,4 @@ mkdir $FONTDIR/a
cp $FONT2 $FONTDIR/a
check
rm -rf $FONTDIR $CACHEFILE $FONTCONFIG_FILE out
rm -rf $FONTDIR $CACHEFILE $CACHEDIR $FONTCONFIG_FILE out