[Fontconfig] fontconfig: Branch 'master' - 19 commits

Akira TAGOH tagoh at kemper.freedesktop.org
Tue Dec 5 12:59:31 UTC 2017


 configure.ac     |    8 ++
 git.mk           |   12 +++
 src/Makefile.am  |    4 -
 src/fccache.c    |  141 ++++++++++++++++++++++++++++++++++++++++--
 src/fccfg.c      |   32 +++++++++
 src/fcdir.c      |    1 
 src/fchash.c     |  183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/fcint.h      |   48 ++++++++++++++
 src/fclist.c     |   36 ++++++++++
 src/fcmatch.c    |   32 +++++++++
 test/Makefile.am |    4 -
 test/run-test.sh |   32 +++++++++
 12 files changed, 522 insertions(+), 11 deletions(-)

New commits:
commit a6797cd5c2d430d22f689240eb4318f2d91fd677
Author: Akira TAGOH <akira at tagoh.org>
Date:   Tue Dec 5 21:57:19 2017 +0900

    Fix distcheck error

diff --git a/test/Makefile.am b/test/Makefile.am
index 2e7f243..245853f 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -17,10 +17,10 @@ TESTDATA=4x6.pcf 8x16.pcf out.expected fonts.conf.in
 
 if FREETYPE_PCF_LONG_FAMILY_NAMES
 out.expected: $(srcdir)/out.expected-long-family-names
-	cp $(srcdir)/out.expected-long-family-names out.expected
+	cp $(srcdir)/out.expected-long-family-names $(builddir)/out.expected
 else
 out.expected: $(srcdir)/out.expected-no-long-family-names
-	cp $(srcdir)/out.expected-no-long-family-names out.expected
+	cp $(srcdir)/out.expected-no-long-family-names $(builddir)/out.expected
 endif
 
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir)
diff --git a/test/run-test.sh b/test/run-test.sh
index 5dc4c83..be2cc92 100644
--- a/test/run-test.sh
+++ b/test/run-test.sh
@@ -51,7 +51,7 @@ check () {
   echo "=" >> out
   $FCLIST - family pixelsize | sort >> out
   tr -d '\015' <out >out.tmp; mv out.tmp out
-  if cmp out $EXPECTED > /dev/null ; then : ; else
+  if cmp out $TESTDIR/$EXPECTED > /dev/null ; then : ; else
     echo "*** Test failed: $TEST"
     echo "*** output is in 'out', expected output in '$EXPECTED'"
     exit 1
commit 1b2279d6b5118fc00bc028340d14fe1e345a4ab4
Author: Akira TAGOH <akira at tagoh.org>
Date:   Fri Nov 24 10:53:39 2017 +0530

    thread-safe functions in fchash.c

diff --git a/src/fchash.c b/src/fchash.c
index 7a320d5..9857abc 100644
--- a/src/fchash.c
+++ b/src/fchash.c
@@ -169,13 +169,15 @@ FcHashTableAdd (FcHashTable *table,
 
 	return !ret;
     }
+  retry:
     for (prev = &table->buckets[hash % FC_HASH_SIZE];
-	 (b = *prev); prev = &(b->next))
+	 (b = fc_atomic_ptr_get (prev)); prev = &(b->next))
     {
 	if (!table->compare_func (bucket->key, key))
 	    goto destroy;
     }
-    *prev = bucket;
+    if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
+	goto retry;
 
     return FcTrue;
 }
commit 4758144492cf694b9d762733bc0907c0dad5b34d
Author: Akira TAGOH <akira at tagoh.org>
Date:   Mon Nov 20 17:46:47 2017 +0530

    Fix the testcase for env not enabled PCF_CONFIG_OPTION_LONG_FAMILY_NAMES in freetype

diff --git a/test/run-test.sh b/test/run-test.sh
index ece6fea..5dc4c83 100644
--- a/test/run-test.sh
+++ b/test/run-test.sh
@@ -131,7 +131,7 @@ ls -l $CACHEDIR > out1
 TESTTMPDIR=`mktemp -d /tmp/fontconfig.XXXXXXXX`
 sed "s!@FONTDIR@!$TESTTMPDIR/fonts!
 s!@CACHEDIR@!$TESTTMPDIR/cache.dir!" < $TESTDIR/fonts.conf.in > bind-fonts.conf
-$BWRAP --bind / / --bind $CACHEDIR $TESTTMPDIR/cache.dir --bind $FONTDIR $TESTTMPDIR/fonts --bind .. $TESTTMPDIR/build --dev-bind /dev /dev --setenv FONTCONFIG_FILE $TESTTMPDIR/build/test/bind-fonts.conf $TESTTMPDIR/build/fc-match/fc-match$EXEEXT -f "%{file}\n" "Misc Fixed" > xxx
+$BWRAP --bind / / --bind $CACHEDIR $TESTTMPDIR/cache.dir --bind $FONTDIR $TESTTMPDIR/fonts --bind .. $TESTTMPDIR/build --dev-bind /dev /dev --setenv FONTCONFIG_FILE $TESTTMPDIR/build/test/bind-fonts.conf $TESTTMPDIR/build/fc-match/fc-match$EXEEXT -f "%{file}\n" ":foundry=Misc" > xxx
 ls -l $CACHEDIR > out2
 if cmp out1 out2 > /dev/null ; then : ; else
   echo "*** Test failed: $TEST"
@@ -140,7 +140,7 @@ if cmp out1 out2 > /dev/null ; then : ; else
 fi
 if [ x`cat xxx` != "x$TESTTMPDIR/fonts/4x6.pcf" ]; then
   echo "*** Test failed: $TEST"
-  echo "file property doesn't points to the new place"
+  echo "file property doesn't points to the new place: $TESTTMPDIR/fonts/4x6.pcf"
   exit 1
 fi
 rm -rf $TESTTMPDIR out1 out2 xxx bind-fonts.conf
commit abe91a1694bb6b89c51c7d61af23bf2607c4c4be
Author: Akira TAGOH <akira at tagoh.org>
Date:   Mon Nov 20 14:33:18 2017 +0530

    Use smaller prime for hash size

diff --git a/src/fchash.c b/src/fchash.c
index 7e8a1df..7a320d5 100644
--- a/src/fchash.c
+++ b/src/fchash.c
@@ -22,7 +22,7 @@
 #include "fcint.h"
 #include <uuid/uuid.h>
 
-#define FC_HASH_SIZE 4099
+#define FC_HASH_SIZE 227
 
 typedef struct _FcHashBucket {
     struct _FcHashBucket  *next;
commit c4b2787ba41006d60c964438fec17f15d75f03c0
Author: Akira TAGOH <akira at tagoh.org>
Date:   Mon Nov 20 13:46:55 2017 +0530

    cleanup

diff --git a/doc/fcpattern.fncs b/doc/fcpattern.fncs
index 912d43d..928f0bc 100644
--- a/doc/fcpattern.fncs
+++ b/doc/fcpattern.fncs
@@ -313,16 +313,6 @@ in preference to FcPatternGet to provide compile-time typechecking.
 <function>FcPatternGetRange</function> are available since 2.11.91.
 @@
 
- at RET@		FcResult
- at FUNC@		FcPatternFindFont
- at TYPE1@		const FcPattern *		@ARG1@		p
- at TYPE3@		FcChar8 **			@ARG3@		ret
- at PURPOSE@	Find a font corresponding to a filename in a pattern
- at DESC@
-Returns a filename in <parameter>p</parameter> to the font.
- at SINCE@		2.13.0
-@@
-
 @RET@		FcPattern *
 @FUNC@		FcPatternBuild
 @TYPE1@		FcPattern *			@ARG1@		pattern
commit 5af21201e1bf2daf2bae4b684243bc62dd2c7ee7
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 23:30:26 2017 +0900

    Add a testcase for bind-mounted cachedir

diff --git a/test/run-test.sh b/test/run-test.sh
index e5bc32c..ece6fea 100644
--- a/test/run-test.sh
+++ b/test/run-test.sh
@@ -36,6 +36,11 @@ ECHO=true
 FCLIST=../fc-list/fc-list$EXEEXT
 FCCACHE=../fc-cache/fc-cache$EXEEXT
 
+which bwrap > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+    BWRAP=`which bwrap`
+fi
+
 FONT1=$TESTDIR/4x6.pcf
 FONT2=$TESTDIR/8x16.pcf
 
@@ -116,4 +121,29 @@ mkdir $FONTDIR/a
 cp $FONT2 $FONTDIR/a
 check
 
+if [ x"$BWRAP" != "x" ]; then
+dotest "Basic functionality with the bind-mounted cache dir"
+prep
+cp $FONT1 $FONT2 $FONTDIR
+$FCCACHE $FONTDIR
+sleep 1
+ls -l $CACHEDIR > out1
+TESTTMPDIR=`mktemp -d /tmp/fontconfig.XXXXXXXX`
+sed "s!@FONTDIR@!$TESTTMPDIR/fonts!
+s!@CACHEDIR@!$TESTTMPDIR/cache.dir!" < $TESTDIR/fonts.conf.in > bind-fonts.conf
+$BWRAP --bind / / --bind $CACHEDIR $TESTTMPDIR/cache.dir --bind $FONTDIR $TESTTMPDIR/fonts --bind .. $TESTTMPDIR/build --dev-bind /dev /dev --setenv FONTCONFIG_FILE $TESTTMPDIR/build/test/bind-fonts.conf $TESTTMPDIR/build/fc-match/fc-match$EXEEXT -f "%{file}\n" "Misc Fixed" > xxx
+ls -l $CACHEDIR > out2
+if cmp out1 out2 > /dev/null ; then : ; else
+  echo "*** Test failed: $TEST"
+  echo "cache was updated."
+  exit 1
+fi
+if [ x`cat xxx` != "x$TESTTMPDIR/fonts/4x6.pcf" ]; then
+  echo "*** Test failed: $TEST"
+  echo "file property doesn't points to the new place"
+  exit 1
+fi
+rm -rf $TESTTMPDIR out1 out2 xxx bind-fonts.conf
+fi
+
 rm -rf $FONTDIR $CACHEFILE $CACHEDIR $FONTCONFIG_FILE out
commit 2f486f6584f3c0d6d1c7eadfbc56cd13a8f3122f
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 23:24:24 2017 +0900

    Don't call FcStat when the alias has already been added
    
    Similar changes to 3a3d6ea applies to fclist and fcmatch.

diff --git a/src/fclist.c b/src/fclist.c
index c78f55b..5f92a72 100644
--- a/src/fclist.c
+++ b/src/fclist.c
@@ -450,43 +450,38 @@ FcListAppend (FcListHashTable	*table,
 	{
 	    if (FcRefIsConst (&font->ref) && !strcmp (os->objects[o], FC_FILE))
 	    {
-		struct stat statb;
+		FcChar8 *dir, *alias;
+		FcConfig *config = FcConfigGetCurrent (); /* FIXME: this may need to be exported as API? */
 
 		for (v = FcPatternEltValues (e); v->value.type != FcTypeString; v = FcValueListNext (v));
 		if (!v)
 		    goto bail2;
-		if (FcStat (FcValueString (&v->value), &statb) < 0)
+		dir = FcStrDirname (FcValueString (&v->value));
+		if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
 		{
-		    FcChar8 *dir = FcStrDirname (FcValueString (&v->value));
-		    FcChar8 *alias;
-		    FcConfig *config = FcConfigGetCurrent (); /* FIXME: this may need to be exported as API? */
-
-		    if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
+		    FcChar8 *base = FcStrBasename (FcValueString (&v->value));
+		    FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
+		    FcValue vv;
+
+		    FcStrFree (alias);
+		    FcStrFree (base);
+		    vv.type = FcTypeString;
+		    vv.u.s = s;
+		    if (!FcPatternAdd (bucket->pattern,
+				       os->objects[o],
+				       FcValueCanonicalize (&vv),
+				       FcTrue))
 		    {
-			FcChar8 *base = FcStrBasename (FcValueString (&v->value));
-			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
-			FcValue vv;
-
-			FcStrFree (alias);
-			FcStrFree (base);
-			vv.type = FcTypeString;
-			vv.u.s = s;
-			if (!FcPatternAdd (bucket->pattern,
-					   os->objects[o],
-					   FcValueCanonicalize (&vv),
-					   FcTrue))
-			{
-			    FcStrFree (s);
-			    FcStrFree (dir);
-			    goto bail2;
-			}
 			FcStrFree (s);
 			FcStrFree (dir);
-			goto bail3;
+			goto bail2;
 		    }
-		    else
-			FcStrFree (dir);
+		    FcStrFree (s);
+		    FcStrFree (dir);
+		    goto bail3;
 		}
+		else
+		    FcStrFree (dir);
 	    }
 	    for (v = FcPatternEltValues(e), idx = 0; v;
 		 v = FcValueListNext(v), ++idx)
diff --git a/src/fcmatch.c b/src/fcmatch.c
index 01e5fae..5d30cc7 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -646,36 +646,33 @@ FcFontRenderPrepare (FcConfig	    *config,
 	    if (FcRefIsConst (&font->ref) && fe->object == FC_FILE_OBJECT)
 	    {
 		FcValueListPtr l = FcPatternEltValues (fe);
-		struct stat statb;
+		FcChar8 *dir, *alias;
 
 		while (l->value.type != FcTypeString)
 		    l = FcValueListNext (l);
-		if (FcStat (FcValueString (&l->value), &statb) < 0)
+		if (!l)
+		    goto bail0;
+		dir = FcStrDirname (FcValueString (&l->value));
+		if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
 		{
-		    FcChar8 *dir = FcStrDirname (FcValueString (&l->value));
-		    FcChar8 *alias;
-
-		    if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
-		    {
-			FcChar8 *base = FcStrBasename (FcValueString (&l->value));
-			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
-			FcValue v;
-
-			FcStrFree (alias);
-			FcStrFree (base);
-			v.type = FcTypeString;
-			v.u.s = s;
-			FcPatternObjectAddWithBinding (new, fe->object,
-						       FcValueCanonicalize (&v),
-						       l->binding,
-						       FcTrue);
-			FcStrFree (s);
-			FcStrFree (dir);
-			goto bail0;
-		    }
-		    else
-			FcStrFree (dir);
+		    FcChar8 *base = FcStrBasename (FcValueString (&l->value));
+		    FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
+		    FcValue v;
+
+		    FcStrFree (alias);
+		    FcStrFree (base);
+		    v.type = FcTypeString;
+		    v.u.s = s;
+		    FcPatternObjectAddWithBinding (new, fe->object,
+						   FcValueCanonicalize (&v),
+						   l->binding,
+						   FcTrue);
+		    FcStrFree (s);
+		    FcStrFree (dir);
+		    goto bail0;
 		}
+		else
+		    FcStrFree (dir);
 	    }
 	    FcPatternObjectListAdd (new, fe->object,
 				    FcValueListDuplicate (FcPatternEltValues (fe)),
commit 665a5d30443cee9ef0eb977857ed2d19ed9f3cb6
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 23:00:31 2017 +0900

    Fix a typo

diff --git a/src/fchash.c b/src/fchash.c
index f4bc0c9..7e8a1df 100644
--- a/src/fchash.c
+++ b/src/fchash.c
@@ -169,7 +169,7 @@ FcHashTableAdd (FcHashTable *table,
 
 	return !ret;
     }
-    for (prev = &table->buckets[hash & FC_HASH_SIZE];
+    for (prev = &table->buckets[hash % FC_HASH_SIZE];
 	 (b = *prev); prev = &(b->next))
     {
 	if (!table->compare_func (bucket->key, key))
commit 6b82c7083565d646b8a08d17dbcb41bd998a5a3c
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 23:00:23 2017 +0900

    Fix memory leak

diff --git a/src/fccache.c b/src/fccache.c
index fcc0ab0..c9e7256 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -221,15 +221,16 @@ FcDirCacheBasenameMD5 (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
 static FcChar8 *
 FcDirCacheBasenameUUID (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN], FcConfig *config)
 {
-    uuid_t uuid;
+    void *u;
     FcChar8 *alias;
 
     if (!FcHashTableFind (config->alias_table, dir, (void **)&alias))
 	alias = FcStrdup (dir);
-    if (FcHashTableFind (config->uuid_table, alias, (void **)&uuid))
+    if (FcHashTableFind (config->uuid_table, alias, &u))
     {
-	uuid_unparse (uuid, (char *) cache_base);
+	uuid_unparse (u, (char *) cache_base);
 	strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
+	FcHashUuidFree (u);
 	FcStrFree (alias);
 	return cache_base;
     }
commit da071b32d41f91856a5e211c1fea7192d33ef09f
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 16:34:02 2017 +0900

    update

diff --git a/git.mk b/git.mk
index 0c9a16a..2bedbf6 100644
--- a/git.mk
+++ b/git.mk
@@ -143,13 +143,18 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
 				po/Makefile.in.in \
 				po/Makefile.in \
 				po/Makefile \
+				po/Makevars.template \
 				po/POTFILES \
+				po/Rules-quot \
 				po/stamp-it \
 				po/stamp-po \
 				po/.intltool-merge-cache \
+				"po/*~" \
+				"po/*.header" \
 				"po/*.gmo" \
 				"po/*.mo" \
 				"po/*.sed" \
+				"po/*.sin" \
 				po/$(GETTEXT_PACKAGE).pot \
 				intltool-extract.in \
 				intltool-merge.in \
@@ -158,14 +163,20 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
 		fi; \
 		if test  -f $(srcdir)/po-conf/Makefile.in.in; then \
 			for x in \
+				po-conf/Makefile.in.in \
 				po-conf/Makefile.in \
 				po-conf/Makefile \
+				po-conf/Makevars.template \
 				po-conf/POTFILES \
+				po-conf/Rules-quot \
 				po-conf/stamp-it \
 				po-conf/stamp-po \
+				"po-conf/*~" \
+				"po-conf/*.header" \
 				"po-conf/*.gmo" \
 				"po-conf/*.mo" \
 				"po-conf/*.sed" \
+				"po-conf/*.sin" \
 				po-conf/$(GETTEXT_PACKAGE)-conf.pot \
 			; do echo /$$x; done; \
 		fi; \
@@ -177,6 +188,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
 				stamp-h1 \
 				libtool \
 				config.lt \
+				config.rpath \
 			; do echo /$$x; done; \
 		fi; \
 		if test "x$(DEJATOOL)" = x; then :; else \
commit 8f88b1c47cb7918aa65ed415f64e04464b1653c9
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 16:10:49 2017 +0900

    abstract hash table functions

diff --git a/src/Makefile.am b/src/Makefile.am
index 8d71e88..80780b6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -140,6 +140,7 @@ libfontconfig_la_SOURCES = \
 	fcfreetype.c \
 	fcfs.c \
 	fcptrlist.c \
+	fchash.c \
 	fcinit.c \
 	fclang.c \
 	fclist.c \
diff --git a/src/fccache.c b/src/fccache.c
index 067077e..fcc0ab0 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -44,92 +44,10 @@
 #define O_BINARY 0
 #endif
 
-#define FC_UUID_HASH_SIZE 4099
-
-typedef struct _FcUuidBucket {
-    struct _FcUuidBucket *next;
-    FcChar8              *file;
-    uuid_t                uuid;
-} FcUuidBucket;
-
-typedef struct _FcUuidHashTable {
-    FcUuidBucket *buckets[FC_UUID_HASH_SIZE];
-} FcUuidHashTable;
-
-static FcUuidHashTable uuid_table;
-
-static FcBool
-FcCacheUuidAdd (FcUuidHashTable *table,
-		const FcChar8   *file,
-		uuid_t           uuid)
-{
-    FcUuidBucket **prev, *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (file);
-
-    for (prev = &table->buckets[hash % FC_UUID_HASH_SIZE];
-	 (bucket = *prev); prev = &(bucket->next))
-    {
-	if (FcStrCmp (bucket->file, file) == 0)
-	    return FcTrue;
-    }
-    bucket = (FcUuidBucket *) malloc (sizeof (FcUuidBucket));
-    if (!bucket)
-	return FcFalse;
-    bucket->next = NULL;
-    bucket->file = FcStrdup (file);
-    uuid_copy (bucket->uuid, uuid);
-    if (!bucket->file)
-    {
-	free (bucket);
-	return FcFalse;
-    }
-    *prev = bucket;
-
-    return FcTrue;
-}
-
-static void
-FcCacheUuidDestroy (FcUuidHashTable *table)
-{
-    int i;
-
-    for (i = 0; i < FC_UUID_HASH_SIZE; i++)
-    {
-	FcUuidBucket *bucket = table->buckets[i], *prev;
-
-	while (bucket)
-	{
-	    FcStrFree (bucket->file);
-	    prev = bucket;
-	    bucket = bucket->next;
-	    free (prev);
-	}
-	table->buckets[i] = NULL;
-    }
-}
-
-static FcBool
-FcCacheUuidFind (FcUuidHashTable *table,
-		 const FcChar8   *file,
-		 uuid_t           ret)
-{
-    FcUuidBucket *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (file);
-
-    for (bucket = table->buckets[hash % FC_UUID_HASH_SIZE]; bucket; bucket = bucket->next)
-    {
-	if (FcStrCmp (bucket->file, file) == 0)
-	{
-	    uuid_copy (ret, bucket->uuid);
-	    return FcTrue;
-	}
-    }
-    return FcFalse;
-}
-
 FcBool
-FcDirCacheCreateUUID (const FcChar8 *dir,
-		      FcBool         force)
+FcDirCacheCreateUUID (FcChar8  *dir,
+		      FcBool    force,
+		      FcConfig *config)
 {
     FcBool ret = FcTrue;
     FcChar8 *uuidname;
@@ -163,7 +81,11 @@ FcDirCacheCreateUUID (const FcChar8 *dir,
 	    goto bail3;
 	}
 	uuid_generate_random (uuid);
-	FcCacheUuidAdd (&uuid_table, dir, uuid);
+	if (!FcHashTableAdd (config->uuid_table, dir, uuid))
+	{
+	    ret = FcFalse;
+	    goto bail3;
+	}
 	uuid_unparse (uuid, out);
 	if (FcDebug () & FC_DBG_CACHE)
 	    printf ("FcDirCacheCreateUUID %s: %s\n", uuidname, out);
@@ -182,11 +104,13 @@ FcDirCacheCreateUUID (const FcChar8 *dir,
 }
 
 static void
-FcDirCacheReadUUID (const FcChar8 *dir)
+FcDirCacheReadUUID (FcChar8  *dir,
+		    FcConfig *config)
 {
+    void *u;
     uuid_t uuid;
 
-    if (!FcCacheUuidFind (&uuid_table, dir, uuid))
+    if (!FcHashTableFind (config->uuid_table, dir, &u))
     {
 	FcChar8 *uuidname = FcStrBuildFilename (dir, ".uuid", NULL);
 	int fd;
@@ -203,7 +127,7 @@ FcDirCacheReadUUID (const FcChar8 *dir)
 		{
 		    if (FcDebug () & FC_DBG_CACHE)
 			printf ("FcDirCacheReadUUID %s -> %s\n", uuidname, suuid);
-		    FcCacheUuidAdd (&uuid_table, dir, uuid);
+		    FcHashTableAdd (config->uuid_table, dir, uuid);
 		}
 	    }
 	    close (fd);
@@ -215,111 +139,8 @@ FcDirCacheReadUUID (const FcChar8 *dir)
 	}
 	FcStrFree (uuidname);
     }
-}
-
-#define FC_ALIAS_HASH_SIZE 4099
-
-typedef struct _FcAliasBucket {
-    struct _FcAliasBucket *next;
-    FcChar8               *orig;
-    FcChar8               *alias;
-} FcAliasBucket;
-
-typedef struct _FcAliasHashTable {
-    FcAliasBucket *buckets[FC_ALIAS_HASH_SIZE];
-} FcAliasHashTable;
-
-static FcAliasHashTable alias_table;
-
-static FcBool
-FcCacheAliasAdd (FcAliasHashTable *table,
-		 const FcChar8    *orig,
-		 const FcChar8    *alias)
-{
-    FcAliasBucket **prev, *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (orig);
-
-    for (prev = &table->buckets[hash % FC_ALIAS_HASH_SIZE];
-	 (bucket = *prev); prev = &(bucket->next))
-    {
-	if (FcStrCmp (bucket->orig, orig) == 0)
-	    return FcTrue;
-    }
-    bucket = (FcAliasBucket *) malloc (sizeof (FcAliasBucket));
-    if (!bucket)
-	return FcFalse;
-    bucket->next = NULL;
-    bucket->orig = FcStrdup (orig);
-    bucket->alias = FcStrdup (alias);
-    if (!bucket->orig || !bucket->alias)
-    {
-	if (bucket->orig)
-	    FcStrFree (bucket->orig);
-	if (bucket->alias)
-	    FcStrFree (bucket->alias);
-	free (bucket);
-	return FcFalse;
-    }
-    *prev = bucket;
-
-    return FcTrue;
-}
-
-static void
-FcCacheAliasDestroy (FcAliasHashTable *table)
-{
-    int i;
-
-    for (i = 0; i < FC_ALIAS_HASH_SIZE; i++)
-    {
-	FcAliasBucket *bucket = table->buckets[i], *prev;
-
-	while (bucket)
-	{
-	    prev = bucket;
-	    FcStrFree (bucket->orig);
-	    FcStrFree (bucket->alias);
-	    bucket = bucket->next;
-	    free (prev);
-	}
-	table->buckets[i] = NULL;
-    }
-}
-
-static FcBool
-FcCacheAliasFind (FcAliasHashTable *table,
-		  const FcChar8    *orig,
-		  const FcChar8    **ret)
-{
-    FcAliasBucket *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (orig);
-
-    for (bucket = table->buckets[hash % FC_ALIAS_HASH_SIZE]; bucket; bucket = bucket->next)
-    {
-	if (FcStrCmp (bucket->orig, orig) == 0)
-	{
-	    *ret = bucket->alias;
-	    return FcTrue;
-	}
-    }
-    return FcFalse;
-}
-
-static void
-FcDirCacheAddAliasPath (const FcChar8 *orig,
-			const FcChar8 *alias)
-{
-    FcCacheAliasAdd (&alias_table, orig, alias);
-}
-
-const FcChar8 *
-FcDirCacheFindAliasPath (const FcChar8 *dir)
-{
-    const FcChar8 *ret = NULL;
-
-    if (FcCacheAliasFind (&alias_table, dir, &ret))
-	return ret;
-    return NULL;
+    else
+	FcHashUuidFree (u);
 }
 
 struct MD5Context {
@@ -398,20 +219,21 @@ FcDirCacheBasenameMD5 (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
 }
 
 static FcChar8 *
-FcDirCacheBasenameUUID (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
+FcDirCacheBasenameUUID (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN], FcConfig *config)
 {
     uuid_t uuid;
-    const FcChar8 *alias;
+    FcChar8 *alias;
 
-    alias = FcDirCacheFindAliasPath (dir);
-    if (!alias)
-	alias = dir;
-    if (FcCacheUuidFind (&uuid_table, alias, uuid))
+    if (!FcHashTableFind (config->alias_table, dir, (void **)&alias))
+	alias = FcStrdup (dir);
+    if (FcHashTableFind (config->uuid_table, alias, (void **)&uuid))
     {
 	uuid_unparse (uuid, (char *) cache_base);
 	strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
+	FcStrFree (alias);
 	return cache_base;
     }
+    FcStrFree (alias);
     return NULL;
 }
 
@@ -424,7 +246,7 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
     FcChar8	*cache_dir;
     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
 
-    if (!FcDirCacheBasenameUUID (dir, cache_base))
+    if (!FcDirCacheBasenameUUID (dir, cache_base, config))
 	FcDirCacheBasenameMD5 (dir, cache_base);
 
     list = FcStrListCreate (config->cacheDirs);
@@ -501,7 +323,7 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
     }
     FcStrFree (d);
 
-    if (!FcDirCacheBasenameUUID (dir, cache_base))
+    if (!FcDirCacheBasenameUUID (dir, cache_base, config))
 	FcDirCacheBasenameMD5 (dir, cache_base);
 
     list = FcStrListCreate (config->cacheDirs);
@@ -832,14 +654,7 @@ FcCacheObjectDereference (void *object)
     if (skip)
     {
 	if (FcRefDec (&skip->ref) == 1)
-	{
 	    FcDirCacheDisposeUnlocked (skip->cache);
-	    if (fcCacheMaxLevel == 0)
-	    {
-		FcCacheUuidDestroy (&uuid_table);
-		FcCacheAliasDestroy (&alias_table);
-	    }
-	}
     }
     unlock_cache ();
 }
@@ -1099,7 +914,7 @@ FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
     FcCache *cache = NULL;
     const FcChar8 *d;
 
-    FcDirCacheReadUUID (dir);
+    FcDirCacheReadUUID ((FcChar8 *) dir, config);
     if (!FcDirCacheProcess (config, dir,
 			    FcDirCacheMapHelper,
 			    &cache, cache_file))
@@ -1107,7 +922,7 @@ FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
 
     d = FcCacheDir (cache);
     if (FcStrCmp (dir, d))
-	FcDirCacheAddAliasPath (d, dir);
+	FcHashTableAdd (config->alias_table, (FcChar8 *) d, (FcChar8 *) dir);
 
     return cache;
 }
@@ -1360,7 +1175,7 @@ FcDirCacheWrite (FcCache *cache, FcConfig *config)
     if (!cache_dir)
 	return FcFalse;
 
-    if (!FcDirCacheBasenameUUID (dir, cache_base))
+    if (!FcDirCacheBasenameUUID (dir, cache_base, config))
 	FcDirCacheBasenameMD5 (dir, cache_base);
     cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
     if (!cache_hashed)
@@ -1558,7 +1373,7 @@ FcDirCacheLock (const FcChar8 *dir,
     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
     int fd = -1;
 
-    if (!FcDirCacheBasenameUUID (dir, cache_base))
+    if (!FcDirCacheBasenameUUID (dir, cache_base, config))
 	FcDirCacheBasenameMD5 (dir, cache_base);
     list = FcStrListCreate (config->cacheDirs);
     if (!list)
diff --git a/src/fccfg.c b/src/fccfg.c
index 5b876ee..be2e993 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -151,6 +151,19 @@ FcConfigCreate (void)
     if (!config->availConfigFiles)
 	goto bail10;
 
+    config->uuid_table = FcHashTableCreate ((FcHashFunc) FcStrHashIgnoreCase,
+					    (FcCompareFunc) FcStrCmp,
+					    FcHashStrCopy,
+					    FcHashUuidCopy,
+					    (FcDestroyFunc) FcStrFree,
+					    FcHashUuidFree);
+    config->alias_table = FcHashTableCreate ((FcHashFunc) FcStrHashIgnoreCase,
+					     (FcCompareFunc) FcStrCmp,
+					     FcHashStrCopy,
+					     FcHashStrCopy,
+					     (FcDestroyFunc) FcStrFree,
+					     (FcDestroyFunc) FcStrFree);
+
     FcRefInit (&config->ref, 1);
 
     return config;
@@ -312,6 +325,9 @@ FcConfigDestroy (FcConfig *config)
     if (config->sysRoot)
 	FcStrFree (config->sysRoot);
 
+    FcHashTableDestroy (config->uuid_table);
+    FcHashTableDestroy (config->alias_table);
+
     free (config);
 }
 
@@ -371,14 +387,15 @@ FcConfigAddCache (FcConfig *config, FcCache *cache,
 	for (i = 0; i < cache->dirs_count; i++)
 	{
 	    const FcChar8 *dir = FcCacheSubdir (cache, i);
-	    const FcChar8 *alias;
+	    FcChar8 *alias;
 	    FcChar8 *d = FcStrDirname (dir);
 	    FcChar8 *s = NULL;
 
-	    if ((alias = FcDirCacheFindAliasPath (d)))
+	    if (FcHashTableFind (config->alias_table, d, (void **)&alias))
 	    {
 		FcChar8 *base = FcStrBasename (dir);
 		dir = s = FcStrBuildFilename (alias, base, NULL);
+		FcStrFree (alias);
 		FcStrFree (base);
 	    }
 	    FcStrFree (d);
diff --git a/src/fcdir.c b/src/fcdir.c
index afe448e..c6514a4 100644
--- a/src/fcdir.c
+++ b/src/fcdir.c
@@ -409,7 +409,7 @@ FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
 {
     FcCache		*cache = NULL;
 
-    FcDirCacheCreateUUID (dir, force);
+    FcDirCacheCreateUUID ((FcChar8 *) dir, force, config);
     /* Try to use existing cache file */
     if (!force)
 	cache = FcDirCacheLoad (dir, config, NULL);
diff --git a/src/fchash.c b/src/fchash.c
new file mode 100644
index 0000000..f4bc0c9
--- /dev/null
+++ b/src/fchash.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright © 2000 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the author(s) not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  The authors make no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "fcint.h"
+#include <uuid/uuid.h>
+
+#define FC_HASH_SIZE 4099
+
+typedef struct _FcHashBucket {
+    struct _FcHashBucket  *next;
+    void                  *key;
+    void                  *value;
+} FcHashBucket;
+
+struct _FcHashTable {
+    FcHashBucket  *buckets[FC_HASH_SIZE];
+    FcHashFunc     hash_func;
+    FcCompareFunc  compare_func;
+    FcCopyFunc     key_copy_func;
+    FcCopyFunc     value_copy_func;
+    FcDestroyFunc  key_destroy_func;
+    FcDestroyFunc  value_destroy_func;
+};
+
+
+FcBool
+FcHashStrCopy (const void  *src,
+	       void       **dest)
+{
+    *dest = FcStrdup (src);
+
+    return *dest != NULL;
+}
+
+FcBool
+FcHashUuidCopy (const void  *src,
+		void       **dest)
+{
+    *dest = malloc (sizeof (uuid_t));
+    uuid_copy (*dest, src);
+    return FcTrue;
+}
+
+void
+FcHashUuidFree (void *data)
+{
+    free (data);
+}
+
+FcHashTable *
+FcHashTableCreate (FcHashFunc    hash_func,
+		   FcCompareFunc compare_func,
+		   FcCopyFunc    key_copy_func,
+		   FcCopyFunc    value_copy_func,
+		   FcDestroyFunc key_destroy_func,
+		   FcDestroyFunc value_destroy_func)
+{
+    FcHashTable *ret = malloc (sizeof (FcHashTable));
+
+    if (ret)
+    {
+	memset (ret->buckets, 0, sizeof (FcHashBucket *) * FC_HASH_SIZE);
+	ret->hash_func = hash_func;
+	ret->compare_func = compare_func;
+	ret->key_copy_func = key_copy_func;
+	ret->value_copy_func = value_copy_func;
+	ret->key_destroy_func = key_destroy_func;
+	ret->value_destroy_func = value_destroy_func;
+    }
+    return ret;
+}
+
+void
+FcHashTableDestroy (FcHashTable *table)
+{
+    int i;
+
+    for (i = 0; i < FC_HASH_SIZE; i++)
+    {
+	FcHashBucket *bucket = table->buckets[i], *prev;
+
+	while (bucket)
+	{
+	    if (table->key_destroy_func)
+		table->key_destroy_func (bucket->key);
+	    if (table->value_destroy_func)
+		table->value_destroy_func (bucket->value);
+	    prev = bucket;
+	    bucket = bucket->next;
+	    free (prev);
+	}
+	table->buckets[i] = NULL;
+    }
+    free (table);
+}
+
+FcBool
+FcHashTableFind (FcHashTable  *table,
+		 const void   *key,
+		 void        **value)
+{
+    FcHashBucket *bucket;
+    FcChar32 hash = table->hash_func (key);
+
+    for (bucket = table->buckets[hash % FC_HASH_SIZE]; bucket; bucket = bucket->next)
+    {
+	if (!table->compare_func(bucket->key, key))
+	{
+	    if (table->value_copy_func)
+	    {
+		if (!table->value_copy_func (bucket->value, value))
+		    return FcFalse;
+	    }
+	    else
+		*value = bucket->value;
+	    return FcTrue;
+	}
+    }
+    return FcFalse;
+}
+
+FcBool
+FcHashTableAdd (FcHashTable *table,
+		void        *key,
+		void        *value)
+{
+    FcHashBucket **prev, *bucket, *b;
+    FcChar32 hash = table->hash_func (key);
+    FcBool ret = FcFalse;
+
+    bucket = (FcHashBucket *) malloc (sizeof (FcHashBucket));
+    if (!bucket)
+	return FcFalse;
+    memset (bucket, 0, sizeof (FcHashBucket));
+    if (table->key_copy_func)
+	ret |= !table->key_copy_func (key, &bucket->key);
+    else
+	bucket->key = key;
+    if (table->value_copy_func)
+	ret |= !table->value_copy_func (value, &bucket->value);
+    else
+	bucket->value = value;
+    if (ret)
+    {
+    destroy:
+	if (bucket->key && table->key_destroy_func)
+	    table->key_destroy_func (bucket->key);
+	if (bucket->value && table->value_destroy_func)
+	    table->value_destroy_func (bucket->value);
+	free (bucket);
+
+	return !ret;
+    }
+    for (prev = &table->buckets[hash & FC_HASH_SIZE];
+	 (b = *prev); prev = &(b->next))
+    {
+	if (!table->compare_func (bucket->key, key))
+	    goto destroy;
+    }
+    *prev = bucket;
+
+    return FcTrue;
+}
diff --git a/src/fcint.h b/src/fcint.h
index 4a0888b..e1c9a9f 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -392,6 +392,13 @@ typedef struct _FcStrBuf {
     FcChar8 buf_static[16 * sizeof (void *)];
 } FcStrBuf;
 
+typedef struct _FcHashTable	FcHashTable;
+
+typedef FcChar32 (* FcHashFunc)	   (const void *data);
+typedef int	 (* FcCompareFunc) (const void *v1, const void *v2);
+typedef FcBool	 (* FcCopyFunc)	   (const void *src, void **dest);
+
+
 struct _FcCache {
     unsigned int magic;              /* FC_CACHE_MAGIC_MMAP or FC_CACHE_ALLOC */
     int		version;	    /* FC_CACHE_VERSION_NUMBER */
@@ -558,6 +565,8 @@ struct _FcConfig {
     FcChar8     *sysRoot;	    /* override the system root directory */
     FcStrSet	*availConfigFiles;  /* config files available */
     FcPtrList	*rulesetList;	    /* List of rulesets being installed */
+    FcHashTable *uuid_table;	    /* UUID table for cachedirs */
+    FcHashTable *alias_table;	    /* alias table for cachedirs */
 };
 
 typedef struct _FcFileTime {
@@ -588,11 +597,9 @@ struct _FcValuePromotionBuffer {
 /* fccache.c */
 
 FcPrivate FcBool
-FcDirCacheCreateUUID (const FcChar8 *dir,
-		      FcBool         force);
-
-FcPrivate const FcChar8 *
-FcDirCacheFindAliasPath (const FcChar8 *dir);
+FcDirCacheCreateUUID (FcChar8  *dir,
+		      FcBool    force,
+		      FcConfig *config);
 
 FcPrivate FcCache *
 FcDirCacheScan (const FcChar8 *dir, FcConfig *config);
@@ -1300,4 +1307,38 @@ FcObjectLookupOtherTypeById (FcObject id);
 FcPrivate const FcObjectType *
 FcObjectLookupOtherTypeByName (const char *str);
 
+/* fchash.c */
+FcPrivate FcBool
+FcHashStrCopy (const void  *src,
+	       void       **dest);
+
+FcPrivate FcBool
+FcHashUuidCopy (const void  *src,
+		void       **dest);
+
+FcPrivate void
+FcHashUuidFree (void *data);
+
+FcPrivate FcHashTable *
+FcHashTableCreate (FcHashFunc    hash_func,
+		   FcCompareFunc compare_func,
+		   FcCopyFunc    key_copy_func,
+		   FcCopyFunc    value_copy_func,
+		   FcDestroyFunc key_destroy_func,
+		   FcDestroyFunc value_destroy_func);
+
+FcPrivate void
+FcHashTableDestroy (FcHashTable *table);
+
+FcPrivate FcBool
+FcHashTableFind (FcHashTable  *table,
+		 const void   *key,
+		 void        **value);
+
+FcPrivate FcBool
+FcHashTableAdd (FcHashTable *table,
+		void        *key,
+		void        *value);
+
+
 #endif /* _FC_INT_H_ */
diff --git a/src/fclist.c b/src/fclist.c
index 835cc32..c78f55b 100644
--- a/src/fclist.c
+++ b/src/fclist.c
@@ -458,14 +458,16 @@ FcListAppend (FcListHashTable	*table,
 		if (FcStat (FcValueString (&v->value), &statb) < 0)
 		{
 		    FcChar8 *dir = FcStrDirname (FcValueString (&v->value));
-		    const FcChar8 *alias;
+		    FcChar8 *alias;
+		    FcConfig *config = FcConfigGetCurrent (); /* FIXME: this may need to be exported as API? */
 
-		    if ((alias = FcDirCacheFindAliasPath (dir)))
+		    if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
 		    {
 			FcChar8 *base = FcStrBasename (FcValueString (&v->value));
 			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
 			FcValue vv;
 
+			FcStrFree (alias);
 			FcStrFree (base);
 			vv.type = FcTypeString;
 			vv.u.s = s;
diff --git a/src/fcmatch.c b/src/fcmatch.c
index 261254f..01e5fae 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -653,14 +653,15 @@ FcFontRenderPrepare (FcConfig	    *config,
 		if (FcStat (FcValueString (&l->value), &statb) < 0)
 		{
 		    FcChar8 *dir = FcStrDirname (FcValueString (&l->value));
-		    const FcChar8 *alias;
+		    FcChar8 *alias;
 
-		    if ((alias = FcDirCacheFindAliasPath (dir)))
+		    if (FcHashTableFind (config->alias_table, dir, (void **) &alias))
 		    {
 			FcChar8 *base = FcStrBasename (FcValueString (&l->value));
 			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
 			FcValue v;
 
+			FcStrFree (alias);
 			FcStrFree (base);
 			v.type = FcTypeString;
 			v.u.s = s;
commit 68ff99c4142e25989409f465e392b1bb3042494d
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Nov 15 16:08:30 2017 +0900

    cleanup

diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index e7a0a59..6ceabc3 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -931,9 +931,6 @@ FcPatternGetLangSet (const FcPattern *p, const char *object, int n, FcLangSet **
 FcPublic FcResult
 FcPatternGetRange (const FcPattern *p, const char *object, int id, FcRange **r);
 
-FcPublic FcResult
-FcPatternFindFont (const FcPattern *p, FcChar8 **s);
-
 FcPublic FcPattern *
 FcPatternVaBuild (FcPattern *p, va_list va);
     
diff --git a/src/fcpat.c b/src/fcpat.c
index 707afc0..dd1307d 100644
--- a/src/fcpat.c
+++ b/src/fcpat.c
@@ -1105,36 +1105,6 @@ FcPatternGetRange (const FcPattern *p, const char *object, int id, FcRange **r)
     return FcPatternObjectGetRange (p, FcObjectFromName (object), id, r);
 }
 
-FcResult
-FcPatternFindFont (const FcPattern *p, FcChar8 **s)
-{
-    FcChar8 *file;
-    FcResult ret = FcResultNoMatch;
-
-    if (FcPatternObjectGetString (p, FC_FILE_OBJECT, 0, &file) == FcResultMatch)
-    {
-	FcChar8 *dir = FcStrDirname (file);
-	const FcChar8 *alias;
-
-	if ((alias = FcDirCacheFindAliasPath (dir)))
-	{
-	    FcChar8 *font = FcStrBasename (file);
-
-	    if (s)
-		*s = FcStrBuildFilename (alias, font, NULL);
-	    FcStrFree (font);
-	}
-	else
-	{
-	    if (s)
-		*s = FcStrdup (file);
-	}
-	ret = FcResultMatch;
-	FcStrFree (dir);
-    }
-    return ret;
-}
-
 FcPattern *
 FcPatternDuplicate (const FcPattern *orig)
 {
commit b01bf646f110cacfaeb5fe097475d3582fa6cd33
Author: Akira TAGOH <akira at tagoh.org>
Date:   Tue Oct 3 13:08:54 2017 +0900

    Destroy the alias and UUID tables when all of caches is unloaded
    
    When a cache contains no fonts, it will be unloaded immediately.
    Previously the certain alias and UUID entries will be purged at that time though,
    this doesn't work when the targeted directory has sub-directories.
    To avoid the unnecessary cache creation with the md5-based naming, try to keep them
    as far as possible.
    Although this way seems not perfectly working if the first directory to look up is like that

diff --git a/src/fccache.c b/src/fccache.c
index 6f7722d..067077e 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -88,28 +88,24 @@ FcCacheUuidAdd (FcUuidHashTable *table,
     return FcTrue;
 }
 
-static FcBool
-FcCacheUuidRemove (FcUuidHashTable *table,
-		   const FcChar8   *file)
+static void
+FcCacheUuidDestroy (FcUuidHashTable *table)
 {
-    FcUuidBucket **prev, *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (file);
+    int i;
 
-    for (prev = &table->buckets[hash % FC_UUID_HASH_SIZE];
-	 (bucket = *prev); )
+    for (i = 0; i < FC_UUID_HASH_SIZE; i++)
     {
-	if (FcStrCmp (bucket->file, file) == 0)
+	FcUuidBucket *bucket = table->buckets[i], *prev;
+
+	while (bucket)
 	{
-	    *prev = bucket->next;
 	    FcStrFree (bucket->file);
-	    free (bucket);
-
-	    return FcTrue;
+	    prev = bucket;
+	    bucket = bucket->next;
+	    free (prev);
 	}
-	else
-	    prev = &(bucket->next);
+	table->buckets[i] = NULL;
     }
-    return FcFalse;
 }
 
 static FcBool
@@ -269,29 +265,25 @@ FcCacheAliasAdd (FcAliasHashTable *table,
     return FcTrue;
 }
 
-static FcBool
-FcCacheAliasRemove (FcAliasHashTable *table,
-		    const FcChar8    *orig)
+static void
+FcCacheAliasDestroy (FcAliasHashTable *table)
 {
-    FcAliasBucket **prev, *bucket;
-    FcChar32 hash = FcStrHashIgnoreCase (orig);
+    int i;
 
-    for (prev = &table->buckets[hash % FC_ALIAS_HASH_SIZE];
-	 (bucket = *prev); )
+    for (i = 0; i < FC_ALIAS_HASH_SIZE; i++)
     {
-	if (FcStrCmp (bucket->orig, orig) == 0)
+	FcAliasBucket *bucket = table->buckets[i], *prev;
+
+	while (bucket)
 	{
-	    *prev = bucket->next;
+	    prev = bucket;
 	    FcStrFree (bucket->orig);
 	    FcStrFree (bucket->alias);
-	    free (bucket);
-
-	    return FcTrue;
+	    bucket = bucket->next;
+	    free (prev);
 	}
-	else
-	    prev = &(bucket->next);
+	table->buckets[i] = NULL;
     }
-    return FcFalse;
 }
 
 static FcBool
@@ -841,13 +833,12 @@ FcCacheObjectDereference (void *object)
     {
 	if (FcRefDec (&skip->ref) == 1)
 	{
-	    const FcChar8 *d = FcDirCacheFindAliasPath (FcCacheDir (skip->cache));
-
-	    FcCacheUuidRemove (&uuid_table, FcCacheDir (skip->cache));
-	    if (d)
-		FcCacheUuidRemove (&uuid_table, d);
-	    FcCacheAliasRemove (&alias_table, FcCacheDir (skip->cache));
 	    FcDirCacheDisposeUnlocked (skip->cache);
+	    if (fcCacheMaxLevel == 0)
+	    {
+		FcCacheUuidDestroy (&uuid_table);
+		FcCacheAliasDestroy (&alias_table);
+	    }
 	}
     }
     unlock_cache ();
commit d7133f4ed7071c6ac257e8d4de0e438e22ca0254
Author: Akira TAGOH <akira at tagoh.org>
Date:   Mon Oct 2 21:17:06 2017 +0900

    Don't call FcStat when the alias has already been added
    
    We could assume that the targeted location is mapped at the different place
    when there are in the alias table.

diff --git a/src/fccfg.c b/src/fccfg.c
index e1aef86..5b876ee 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -371,24 +371,17 @@ FcConfigAddCache (FcConfig *config, FcCache *cache,
 	for (i = 0; i < cache->dirs_count; i++)
 	{
 	    const FcChar8 *dir = FcCacheSubdir (cache, i);
+	    const FcChar8 *alias;
+	    FcChar8 *d = FcStrDirname (dir);
 	    FcChar8 *s = NULL;
-	    struct stat statb;
 
-	    if (FcStat (dir, &statb) < 0)
+	    if ((alias = FcDirCacheFindAliasPath (d)))
 	    {
-		const FcChar8 *alias;
-		FcChar8 *d = FcStrDirname (dir);
-
-		if ((alias = FcDirCacheFindAliasPath (dir)))
-		    dir = alias;
-		else if ((alias = FcDirCacheFindAliasPath (d)))
-		{
-		    FcChar8 *base = FcStrBasename (dir);
-		    dir = s = FcStrBuildFilename (alias, base, NULL);
-		    FcStrFree (base);
-		}
-		FcStrFree (d);
+		FcChar8 *base = FcStrBasename (dir);
+		dir = s = FcStrBuildFilename (alias, base, NULL);
+		FcStrFree (base);
 	    }
+	    FcStrFree (d);
 	    if (FcConfigAcceptFilename (config, dir))
 		FcStrSetAddFilename (dirSet, dir);
 	    if (s)
commit cf5acaed9621990d890a0dfd655494d7242aba26
Author: Akira TAGOH <akira at tagoh.org>
Date:   Sat Sep 23 18:49:55 2017 +0900

    Replace the path of subdirs in caches as well

diff --git a/src/fccfg.c b/src/fccfg.c
index e297a23..e1aef86 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -370,9 +370,29 @@ FcConfigAddCache (FcConfig *config, FcCache *cache,
     {
 	for (i = 0; i < cache->dirs_count; i++)
 	{
-	    FcChar8	*dir = FcOffsetToPtr (dirs, dirs[i], FcChar8);
+	    const FcChar8 *dir = FcCacheSubdir (cache, i);
+	    FcChar8 *s = NULL;
+	    struct stat statb;
+
+	    if (FcStat (dir, &statb) < 0)
+	    {
+		const FcChar8 *alias;
+		FcChar8 *d = FcStrDirname (dir);
+
+		if ((alias = FcDirCacheFindAliasPath (dir)))
+		    dir = alias;
+		else if ((alias = FcDirCacheFindAliasPath (d)))
+		{
+		    FcChar8 *base = FcStrBasename (dir);
+		    dir = s = FcStrBuildFilename (alias, base, NULL);
+		    FcStrFree (base);
+		}
+		FcStrFree (d);
+	    }
 	    if (FcConfigAcceptFilename (config, dir))
 		FcStrSetAddFilename (dirSet, dir);
+	    if (s)
+		FcStrFree (s);
 	}
     }
     return FcTrue;
commit 6d3b306cbecac22f4e0974c1a6e836289bd522f4
Author: Akira TAGOH <akira at tagoh.org>
Date:   Tue Sep 19 20:21:22 2017 +0900

    Replace the original path to the new one

diff --git a/src/fclist.c b/src/fclist.c
index d7cd5c8..835cc32 100644
--- a/src/fclist.c
+++ b/src/fclist.c
@@ -448,7 +448,6 @@ FcListAppend (FcListHashTable	*table,
 	e = FcPatternObjectFindElt (font, FcObjectFromName (os->objects[o]));
 	if (e)
 	{
-	    idx = 0;
 	    if (FcRefIsConst (&font->ref) && !strcmp (os->objects[o], FC_FILE))
 	    {
 		struct stat statb;
@@ -481,13 +480,13 @@ FcListAppend (FcListHashTable	*table,
 			}
 			FcStrFree (s);
 			FcStrFree (dir);
-			idx++;
+			goto bail3;
 		    }
 		    else
 			FcStrFree (dir);
 		}
 	    }
-	    for (v = FcPatternEltValues(e); v;
+	    for (v = FcPatternEltValues(e), idx = 0; v;
 		 v = FcValueListNext(v), ++idx)
 	    {
 		if (!FcPatternAdd (bucket->pattern,
@@ -495,6 +494,7 @@ FcListAppend (FcListHashTable	*table,
 				   FcValueCanonicalize(&v->value), defidx != idx))
 		    goto bail2;
 	    }
+	  bail3:;
 	}
     }
     *prev = bucket;
diff --git a/src/fcmatch.c b/src/fcmatch.c
index 30070f4..261254f 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -670,6 +670,7 @@ FcFontRenderPrepare (FcConfig	    *config,
 						       FcTrue);
 			FcStrFree (s);
 			FcStrFree (dir);
+			goto bail0;
 		    }
 		    else
 			FcStrFree (dir);
@@ -678,6 +679,7 @@ FcFontRenderPrepare (FcConfig	    *config,
 	    FcPatternObjectListAdd (new, fe->object,
 				    FcValueListDuplicate (FcPatternEltValues (fe)),
 				    FcTrue);
+	  bail0:;
 	}
     }
     for (i = 0; i < pat->num; i++)
commit 6f226ad67e4373fa62359d1a7b94400d200e66ed
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu Sep 7 19:43:59 2017 +0900

    Replace the font path in FcPattern to what it is actually located.

diff --git a/src/fclist.c b/src/fclist.c
index d7e8fc0..d7cd5c8 100644
--- a/src/fclist.c
+++ b/src/fclist.c
@@ -448,7 +448,46 @@ FcListAppend (FcListHashTable	*table,
 	e = FcPatternObjectFindElt (font, FcObjectFromName (os->objects[o]));
 	if (e)
 	{
-	    for (v = FcPatternEltValues(e), idx = 0; v;
+	    idx = 0;
+	    if (FcRefIsConst (&font->ref) && !strcmp (os->objects[o], FC_FILE))
+	    {
+		struct stat statb;
+
+		for (v = FcPatternEltValues (e); v->value.type != FcTypeString; v = FcValueListNext (v));
+		if (!v)
+		    goto bail2;
+		if (FcStat (FcValueString (&v->value), &statb) < 0)
+		{
+		    FcChar8 *dir = FcStrDirname (FcValueString (&v->value));
+		    const FcChar8 *alias;
+
+		    if ((alias = FcDirCacheFindAliasPath (dir)))
+		    {
+			FcChar8 *base = FcStrBasename (FcValueString (&v->value));
+			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
+			FcValue vv;
+
+			FcStrFree (base);
+			vv.type = FcTypeString;
+			vv.u.s = s;
+			if (!FcPatternAdd (bucket->pattern,
+					   os->objects[o],
+					   FcValueCanonicalize (&vv),
+					   FcTrue))
+			{
+			    FcStrFree (s);
+			    FcStrFree (dir);
+			    goto bail2;
+			}
+			FcStrFree (s);
+			FcStrFree (dir);
+			idx++;
+		    }
+		    else
+			FcStrFree (dir);
+		}
+	    }
+	    for (v = FcPatternEltValues(e); v;
 		 v = FcValueListNext(v), ++idx)
 	    {
 		if (!FcPatternAdd (bucket->pattern,
diff --git a/src/fcmatch.c b/src/fcmatch.c
index d5eaea7..30070f4 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -643,6 +643,38 @@ FcFontRenderPrepare (FcConfig	    *config,
 	}
 	else
 	{
+	    if (FcRefIsConst (&font->ref) && fe->object == FC_FILE_OBJECT)
+	    {
+		FcValueListPtr l = FcPatternEltValues (fe);
+		struct stat statb;
+
+		while (l->value.type != FcTypeString)
+		    l = FcValueListNext (l);
+		if (FcStat (FcValueString (&l->value), &statb) < 0)
+		{
+		    FcChar8 *dir = FcStrDirname (FcValueString (&l->value));
+		    const FcChar8 *alias;
+
+		    if ((alias = FcDirCacheFindAliasPath (dir)))
+		    {
+			FcChar8 *base = FcStrBasename (FcValueString (&l->value));
+			FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
+			FcValue v;
+
+			FcStrFree (base);
+			v.type = FcTypeString;
+			v.u.s = s;
+			FcPatternObjectAddWithBinding (new, fe->object,
+						       FcValueCanonicalize (&v),
+						       l->binding,
+						       FcTrue);
+			FcStrFree (s);
+			FcStrFree (dir);
+		    }
+		    else
+			FcStrFree (dir);
+		}
+	    }
 	    FcPatternObjectListAdd (new, fe->object,
 				    FcValueListDuplicate (FcPatternEltValues (fe)),
 				    FcTrue);
commit 85d9de58ed093ade638b51697fc3a23309e5d5a6
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Aug 2 11:02:19 2017 +0100

    Add new API to find out a font from current search path

diff --git a/doc/fcpattern.fncs b/doc/fcpattern.fncs
index 928f0bc..912d43d 100644
--- a/doc/fcpattern.fncs
+++ b/doc/fcpattern.fncs
@@ -313,6 +313,16 @@ in preference to FcPatternGet to provide compile-time typechecking.
 <function>FcPatternGetRange</function> are available since 2.11.91.
 @@
 
+ at RET@		FcResult
+ at FUNC@		FcPatternFindFont
+ at TYPE1@		const FcPattern *		@ARG1@		p
+ at TYPE3@		FcChar8 **			@ARG3@		ret
+ at PURPOSE@	Find a font corresponding to a filename in a pattern
+ at DESC@
+Returns a filename in <parameter>p</parameter> to the font.
+ at SINCE@		2.13.0
+@@
+
 @RET@		FcPattern *
 @FUNC@		FcPatternBuild
 @TYPE1@		FcPattern *			@ARG1@		pattern
diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 6ceabc3..e7a0a59 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -931,6 +931,9 @@ FcPatternGetLangSet (const FcPattern *p, const char *object, int n, FcLangSet **
 FcPublic FcResult
 FcPatternGetRange (const FcPattern *p, const char *object, int id, FcRange **r);
 
+FcPublic FcResult
+FcPatternFindFont (const FcPattern *p, FcChar8 **s);
+
 FcPublic FcPattern *
 FcPatternVaBuild (FcPattern *p, va_list va);
     
diff --git a/src/fccache.c b/src/fccache.c
index 9176019..6f7722d 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -221,6 +221,115 @@ FcDirCacheReadUUID (const FcChar8 *dir)
     }
 }
 
+#define FC_ALIAS_HASH_SIZE 4099
+
+typedef struct _FcAliasBucket {
+    struct _FcAliasBucket *next;
+    FcChar8               *orig;
+    FcChar8               *alias;
+} FcAliasBucket;
+
+typedef struct _FcAliasHashTable {
+    FcAliasBucket *buckets[FC_ALIAS_HASH_SIZE];
+} FcAliasHashTable;
+
+static FcAliasHashTable alias_table;
+
+static FcBool
+FcCacheAliasAdd (FcAliasHashTable *table,
+		 const FcChar8    *orig,
+		 const FcChar8    *alias)
+{
+    FcAliasBucket **prev, *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (orig);
+
+    for (prev = &table->buckets[hash % FC_ALIAS_HASH_SIZE];
+	 (bucket = *prev); prev = &(bucket->next))
+    {
+	if (FcStrCmp (bucket->orig, orig) == 0)
+	    return FcTrue;
+    }
+    bucket = (FcAliasBucket *) malloc (sizeof (FcAliasBucket));
+    if (!bucket)
+	return FcFalse;
+    bucket->next = NULL;
+    bucket->orig = FcStrdup (orig);
+    bucket->alias = FcStrdup (alias);
+    if (!bucket->orig || !bucket->alias)
+    {
+	if (bucket->orig)
+	    FcStrFree (bucket->orig);
+	if (bucket->alias)
+	    FcStrFree (bucket->alias);
+	free (bucket);
+	return FcFalse;
+    }
+    *prev = bucket;
+
+    return FcTrue;
+}
+
+static FcBool
+FcCacheAliasRemove (FcAliasHashTable *table,
+		    const FcChar8    *orig)
+{
+    FcAliasBucket **prev, *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (orig);
+
+    for (prev = &table->buckets[hash % FC_ALIAS_HASH_SIZE];
+	 (bucket = *prev); )
+    {
+	if (FcStrCmp (bucket->orig, orig) == 0)
+	{
+	    *prev = bucket->next;
+	    FcStrFree (bucket->orig);
+	    FcStrFree (bucket->alias);
+	    free (bucket);
+
+	    return FcTrue;
+	}
+	else
+	    prev = &(bucket->next);
+    }
+    return FcFalse;
+}
+
+static FcBool
+FcCacheAliasFind (FcAliasHashTable *table,
+		  const FcChar8    *orig,
+		  const FcChar8    **ret)
+{
+    FcAliasBucket *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (orig);
+
+    for (bucket = table->buckets[hash % FC_ALIAS_HASH_SIZE]; bucket; bucket = bucket->next)
+    {
+	if (FcStrCmp (bucket->orig, orig) == 0)
+	{
+	    *ret = bucket->alias;
+	    return FcTrue;
+	}
+    }
+    return FcFalse;
+}
+
+static void
+FcDirCacheAddAliasPath (const FcChar8 *orig,
+			const FcChar8 *alias)
+{
+    FcCacheAliasAdd (&alias_table, orig, alias);
+}
+
+const FcChar8 *
+FcDirCacheFindAliasPath (const FcChar8 *dir)
+{
+    const FcChar8 *ret = NULL;
+
+    if (FcCacheAliasFind (&alias_table, dir, &ret))
+	return ret;
+    return NULL;
+}
+
 struct MD5Context {
         FcChar32 buf[4];
         FcChar32 bits[2];
@@ -300,8 +409,12 @@ static FcChar8 *
 FcDirCacheBasenameUUID (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
 {
     uuid_t uuid;
+    const FcChar8 *alias;
 
-    if (FcCacheUuidFind (&uuid_table, dir, uuid))
+    alias = FcDirCacheFindAliasPath (dir);
+    if (!alias)
+	alias = dir;
+    if (FcCacheUuidFind (&uuid_table, alias, uuid))
     {
 	uuid_unparse (uuid, (char *) cache_base);
 	strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
@@ -728,7 +841,12 @@ FcCacheObjectDereference (void *object)
     {
 	if (FcRefDec (&skip->ref) == 1)
 	{
+	    const FcChar8 *d = FcDirCacheFindAliasPath (FcCacheDir (skip->cache));
+
 	    FcCacheUuidRemove (&uuid_table, FcCacheDir (skip->cache));
+	    if (d)
+		FcCacheUuidRemove (&uuid_table, d);
+	    FcCacheAliasRemove (&alias_table, FcCacheDir (skip->cache));
 	    FcDirCacheDisposeUnlocked (skip->cache);
 	}
     }
@@ -988,12 +1106,17 @@ FcCache *
 FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
 {
     FcCache *cache = NULL;
+    const FcChar8 *d;
 
+    FcDirCacheReadUUID (dir);
     if (!FcDirCacheProcess (config, dir,
 			    FcDirCacheMapHelper,
 			    &cache, cache_file))
 	return NULL;
-    FcDirCacheReadUUID (FcCacheDir (cache));
+
+    d = FcCacheDir (cache);
+    if (FcStrCmp (dir, d))
+	FcDirCacheAddAliasPath (d, dir);
 
     return cache;
 }
diff --git a/src/fcint.h b/src/fcint.h
index d1a2383..4a0888b 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -591,6 +591,9 @@ FcPrivate FcBool
 FcDirCacheCreateUUID (const FcChar8 *dir,
 		      FcBool         force);
 
+FcPrivate const FcChar8 *
+FcDirCacheFindAliasPath (const FcChar8 *dir);
+
 FcPrivate FcCache *
 FcDirCacheScan (const FcChar8 *dir, FcConfig *config);
 
diff --git a/src/fcpat.c b/src/fcpat.c
index dd1307d..707afc0 100644
--- a/src/fcpat.c
+++ b/src/fcpat.c
@@ -1105,6 +1105,36 @@ FcPatternGetRange (const FcPattern *p, const char *object, int id, FcRange **r)
     return FcPatternObjectGetRange (p, FcObjectFromName (object), id, r);
 }
 
+FcResult
+FcPatternFindFont (const FcPattern *p, FcChar8 **s)
+{
+    FcChar8 *file;
+    FcResult ret = FcResultNoMatch;
+
+    if (FcPatternObjectGetString (p, FC_FILE_OBJECT, 0, &file) == FcResultMatch)
+    {
+	FcChar8 *dir = FcStrDirname (file);
+	const FcChar8 *alias;
+
+	if ((alias = FcDirCacheFindAliasPath (dir)))
+	{
+	    FcChar8 *font = FcStrBasename (file);
+
+	    if (s)
+		*s = FcStrBuildFilename (alias, font, NULL);
+	    FcStrFree (font);
+	}
+	else
+	{
+	    if (s)
+		*s = FcStrdup (file);
+	}
+	ret = FcResultMatch;
+	FcStrFree (dir);
+    }
+    return ret;
+}
+
 FcPattern *
 FcPatternDuplicate (const FcPattern *orig)
 {
commit 7b48fd3dd406b926f0e5240b211f72197ed538a9
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed Sep 6 17:01:19 2017 +0900

    Use uuid-based cache filename if uuid is assigned to dirs

diff --git a/configure.ac b/configure.ac
index 71f32c1..81b433a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -339,6 +339,14 @@ LIBS="$fontconfig_save_libs"
 CFLAGS="$fontconfig_save_cflags"
 
 #
+# Check for uuid
+#
+PKG_CHECK_MODULES([UUID], [uuid])
+PKGCONFIG_REQUIRES_PRIVATELY="$PKGCONFIG_REQUIRES_PRIVATELY uuid"
+AC_SUBST(UUID_CFLAGS)
+AC_SUBST(UUID_LIBS)
+
+#
 # Check expat configuration
 #
 AC_ARG_WITH(expat,
diff --git a/src/Makefile.am b/src/Makefile.am
index b273ff3..8d71e88 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -77,6 +77,7 @@ AM_CPPFLAGS = 						\
 	$(ICONV_CFLAGS)					\
 	$(LIBXML2_CFLAGS)				\
 	$(EXPAT_CFLAGS)					\
+	$(UUID_CFLAGS)					\
 	$(WARN_CFLAGS)					\
 	-DFC_CACHEDIR='"$(FC_CACHEDIR)"'                \
 	-DFONTCONFIG_PATH='"$(BASECONFIGDIR)"'		\
@@ -165,7 +166,7 @@ lib_LTLIBRARIES = libfontconfig.la
 libfontconfig_la_LDFLAGS =			\
 	-version-info @LIBT_VERSION_INFO@ -no-undefined $(export_symbols)
 
-libfontconfig_la_LIBADD = $(ICONV_LIBS) $(FREETYPE_LIBS) $(LIBXML2_LIBS) $(EXPAT_LIBS)
+libfontconfig_la_LIBADD = $(ICONV_LIBS) $(FREETYPE_LIBS) $(LIBXML2_LIBS) $(EXPAT_LIBS) $(UUID_LIBS)
 
 libfontconfig_la_DEPENDENCIES = $(fontconfig_def_dependency)
 
diff --git a/src/fccache.c b/src/fccache.c
index c59ea19..9176019 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -38,11 +38,188 @@
 #if defined(_WIN32)
 #include <sys/locking.h>
 #endif
+#include <uuid/uuid.h>
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
+#define FC_UUID_HASH_SIZE 4099
+
+typedef struct _FcUuidBucket {
+    struct _FcUuidBucket *next;
+    FcChar8              *file;
+    uuid_t                uuid;
+} FcUuidBucket;
+
+typedef struct _FcUuidHashTable {
+    FcUuidBucket *buckets[FC_UUID_HASH_SIZE];
+} FcUuidHashTable;
+
+static FcUuidHashTable uuid_table;
+
+static FcBool
+FcCacheUuidAdd (FcUuidHashTable *table,
+		const FcChar8   *file,
+		uuid_t           uuid)
+{
+    FcUuidBucket **prev, *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (file);
+
+    for (prev = &table->buckets[hash % FC_UUID_HASH_SIZE];
+	 (bucket = *prev); prev = &(bucket->next))
+    {
+	if (FcStrCmp (bucket->file, file) == 0)
+	    return FcTrue;
+    }
+    bucket = (FcUuidBucket *) malloc (sizeof (FcUuidBucket));
+    if (!bucket)
+	return FcFalse;
+    bucket->next = NULL;
+    bucket->file = FcStrdup (file);
+    uuid_copy (bucket->uuid, uuid);
+    if (!bucket->file)
+    {
+	free (bucket);
+	return FcFalse;
+    }
+    *prev = bucket;
+
+    return FcTrue;
+}
+
+static FcBool
+FcCacheUuidRemove (FcUuidHashTable *table,
+		   const FcChar8   *file)
+{
+    FcUuidBucket **prev, *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (file);
+
+    for (prev = &table->buckets[hash % FC_UUID_HASH_SIZE];
+	 (bucket = *prev); )
+    {
+	if (FcStrCmp (bucket->file, file) == 0)
+	{
+	    *prev = bucket->next;
+	    FcStrFree (bucket->file);
+	    free (bucket);
+
+	    return FcTrue;
+	}
+	else
+	    prev = &(bucket->next);
+    }
+    return FcFalse;
+}
+
+static FcBool
+FcCacheUuidFind (FcUuidHashTable *table,
+		 const FcChar8   *file,
+		 uuid_t           ret)
+{
+    FcUuidBucket *bucket;
+    FcChar32 hash = FcStrHashIgnoreCase (file);
+
+    for (bucket = table->buckets[hash % FC_UUID_HASH_SIZE]; bucket; bucket = bucket->next)
+    {
+	if (FcStrCmp (bucket->file, file) == 0)
+	{
+	    uuid_copy (ret, bucket->uuid);
+	    return FcTrue;
+	}
+    }
+    return FcFalse;
+}
+
+FcBool
+FcDirCacheCreateUUID (const FcChar8 *dir,
+		      FcBool         force)
+{
+    FcBool ret = FcTrue;
+    FcChar8 *uuidname;
+
+    uuidname = FcStrBuildFilename (dir, ".uuid", NULL);
+    if (!uuidname)
+	return FcFalse;
+
+    if (force || access ((const char *) uuidname, F_OK) < 0)
+    {
+	FcAtomic *atomic;
+	int fd;
+	uuid_t uuid;
+	char out[37];
+
+	atomic = FcAtomicCreate (uuidname);
+	if (!atomic)
+	{
+	    ret = FcFalse;
+	    goto bail1;
+	}
+	if (!FcAtomicLock (atomic))
+	{
+	    ret = FcFalse;
+	    goto bail2;
+	}
+	fd = FcOpen ((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0644);
+	if (fd == -1)
+	{
+	    ret = FcFalse;
+	    goto bail3;
+	}
+	uuid_generate_random (uuid);
+	FcCacheUuidAdd (&uuid_table, dir, uuid);
+	uuid_unparse (uuid, out);
+	if (FcDebug () & FC_DBG_CACHE)
+	    printf ("FcDirCacheCreateUUID %s: %s\n", uuidname, out);
+	write (fd, out, strlen (out));
+	close (fd);
+	FcAtomicReplaceOrig (atomic);
+    bail3:
+	FcAtomicUnlock (atomic);
+    bail2:
+	FcAtomicDestroy (atomic);
+    }
+    bail1:
+    FcStrFree (uuidname);
+
+    return ret;
+}
+
+static void
+FcDirCacheReadUUID (const FcChar8 *dir)
+{
+    uuid_t uuid;
+
+    if (!FcCacheUuidFind (&uuid_table, dir, uuid))
+    {
+	FcChar8 *uuidname = FcStrBuildFilename (dir, ".uuid", NULL);
+	int fd;
+
+	if ((fd = FcOpen ((char *) uuidname, O_RDONLY)) >= 0)
+	{
+	    char suuid[37];
+
+	    memset (suuid, 0, sizeof (suuid));
+	    if (read (fd, suuid, 36) > 0)
+	    {
+		memset (uuid, 0, sizeof (uuid));
+		if (uuid_parse (suuid, uuid) == 0)
+		{
+		    if (FcDebug () & FC_DBG_CACHE)
+			printf ("FcDirCacheReadUUID %s -> %s\n", uuidname, suuid);
+		    FcCacheUuidAdd (&uuid_table, dir, uuid);
+		}
+	    }
+	    close (fd);
+	}
+	else
+	{
+	    if (FcDebug () & FC_DBG_CACHE)
+		printf ("FcDirCacheReadUUID Unable to read %s\n", uuidname);
+	}
+	FcStrFree (uuidname);
+    }
+}
 
 struct MD5Context {
         FcChar32 buf[4];
@@ -55,7 +232,7 @@ static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned
 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
 
-#define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
+#define CACHEBASE_LEN (1 + 36 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
 
 static FcBool
 FcCacheIsMmapSafe (int fd)
@@ -94,7 +271,7 @@ static const char bin2hex[] = { '0', '1', '2', '3',
 				'c', 'd', 'e', 'f' };
 
 static FcChar8 *
-FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
+FcDirCacheBasenameMD5 (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
 {
     unsigned char 	hash[16];
     FcChar8		*hex_hash;
@@ -119,6 +296,20 @@ FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
     return cache_base;
 }
 
+static FcChar8 *
+FcDirCacheBasenameUUID (const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
+{
+    uuid_t uuid;
+
+    if (FcCacheUuidFind (&uuid_table, dir, uuid))
+    {
+	uuid_unparse (uuid, (char *) cache_base);
+	strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
+	return cache_base;
+    }
+    return NULL;
+}
+
 FcBool
 FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
 {
@@ -128,7 +319,8 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
     FcChar8	*cache_dir;
     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
 
-    FcDirCacheBasename (dir, cache_base);
+    if (!FcDirCacheBasenameUUID (dir, cache_base))
+	FcDirCacheBasenameMD5 (dir, cache_base);
 
     list = FcStrListCreate (config->cacheDirs);
     if (!list)
@@ -204,7 +396,8 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
     }
     FcStrFree (d);
 
-    FcDirCacheBasename (dir, cache_base);
+    if (!FcDirCacheBasenameUUID (dir, cache_base))
+	FcDirCacheBasenameMD5 (dir, cache_base);
 
     list = FcStrListCreate (config->cacheDirs);
     if (!list)
@@ -534,7 +727,10 @@ FcCacheObjectDereference (void *object)
     if (skip)
     {
 	if (FcRefDec (&skip->ref) == 1)
+	{
+	    FcCacheUuidRemove (&uuid_table, FcCacheDir (skip->cache));
 	    FcDirCacheDisposeUnlocked (skip->cache);
+	}
     }
     unlock_cache ();
 }
@@ -797,6 +993,7 @@ FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
 			    FcDirCacheMapHelper,
 			    &cache, cache_file))
 	return NULL;
+    FcDirCacheReadUUID (FcCacheDir (cache));
 
     return cache;
 }
@@ -1049,7 +1246,8 @@ FcDirCacheWrite (FcCache *cache, FcConfig *config)
     if (!cache_dir)
 	return FcFalse;
 
-    FcDirCacheBasename (dir, cache_base);
+    if (!FcDirCacheBasenameUUID (dir, cache_base))
+	FcDirCacheBasenameMD5 (dir, cache_base);
     cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
     if (!cache_hashed)
         return FcFalse;
@@ -1246,7 +1444,8 @@ FcDirCacheLock (const FcChar8 *dir,
     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
     int fd = -1;
 
-    FcDirCacheBasename (dir, cache_base);
+    if (!FcDirCacheBasenameUUID (dir, cache_base))
+	FcDirCacheBasenameMD5 (dir, cache_base);
     list = FcStrListCreate (config->cacheDirs);
     if (!list)
 	return -1;
diff --git a/src/fcdir.c b/src/fcdir.c
index 4f38f4b..afe448e 100644
--- a/src/fcdir.c
+++ b/src/fcdir.c
@@ -409,6 +409,7 @@ FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
 {
     FcCache		*cache = NULL;
 
+    FcDirCacheCreateUUID (dir, force);
     /* Try to use existing cache file */
     if (!force)
 	cache = FcDirCacheLoad (dir, config, NULL);
diff --git a/src/fcint.h b/src/fcint.h
index 2a6ec27..d1a2383 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -587,6 +587,10 @@ struct _FcValuePromotionBuffer {
 
 /* fccache.c */
 
+FcPrivate FcBool
+FcDirCacheCreateUUID (const FcChar8 *dir,
+		      FcBool         force);
+
 FcPrivate FcCache *
 FcDirCacheScan (const FcChar8 *dir, FcConfig *config);
 


More information about the Fontconfig mailing list