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

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Jan 1 23:12:49 PST 2013


 .gitignore               |  100 -----------
 COPYING                  |    1 
 Makefile.am              |    2 
 conf.d/Makefile.am       |    2 
 configure.ac             |   38 ++++
 doc/Makefile.am          |    2 
 doc/fcconstant.fncs      |    8 
 doc/fcobjecttype.fncs    |    6 
 fc-cache/Makefile.am     |    2 
 fc-case/Makefile.am      |    2 
 fc-cat/Makefile.am       |    2 
 fc-glyphname/Makefile.am |    2 
 fc-lang/Makefile.am      |    2 
 fc-list/Makefile.am      |    2 
 fc-match/Makefile.am     |    2 
 fc-pattern/Makefile.am   |    2 
 fc-query/Makefile.am     |    2 
 fc-scan/Makefile.am      |    2 
 fontconfig/Makefile.am   |    2 
 fontconfig/fontconfig.h  |   10 -
 git.mk                   |  227 +++++++++++++++++++++++++
 m4/ax_pthread.m4         |  309 +++++++++++++++++++++++++++++++++++
 src/Makefile.am          |   34 +++
 src/fcatomic.h           |  124 ++++++++++++++
 src/fccache.c            |  146 ++++++++++++----
 src/fccfg.c              |   94 +++++++---
 src/fccharset.c          |   24 +-
 src/fcdbg.c              |   18 +-
 src/fcdefault.c          |   89 +++++++---
 src/fcinit.c             |   19 --
 src/fcint.h              |  120 ++++++-------
 src/fclang.c             |   43 ++--
 src/fcmatch.c            |    4 
 src/fcmutex.h            |  128 ++++++++++++++
 src/fcname.c             |  413 ++++++-----------------------------------------
 src/fcobjs.c             |  130 ++++++++++++++
 src/fcobjs.h             |   44 +++++
 src/fcobjshash.gperf.h   |   26 ++
 src/fcpat.c              |   81 ++-------
 src/fcstr.c              |   28 +--
 src/fcxml.c              |    4 
 test/Makefile.am         |    2 
 42 files changed, 1537 insertions(+), 761 deletions(-)

New commits:
commit b604f10c0c31a56ae16154dfe6a2f13b795aaabf
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Jan 2 01:09:20 2013 -0600

    Make fcobjs.c thread-safe
    
    With this, the library should be threadsafe as far as my analysis goes!

diff --git a/src/fcobjs.c b/src/fcobjs.c
index e3ba2f8..aaabb62 100644
--- a/src/fcobjs.c
+++ b/src/fcobjs.c
@@ -40,8 +40,8 @@ _FcObjectLookupOtherTypeByName (const char *str, FcObject *id)
 {
     struct FcObjectOtherTypeInfo *ots, *ot;
 
-    /* XXX MT-unsafe */
-    ots = other_types;
+retry:
+    ots = fc_atomic_ptr_get (&other_types);
 
     for (ot = ots; ot; ot = ot->next)
 	if (0 == strcmp (ot->object.object, str))
@@ -55,10 +55,13 @@ _FcObjectLookupOtherTypeByName (const char *str, FcObject *id)
 
 	ot->object.object = strdup (str);
 	ot->object.type = -1;
-	ot->id = next_id++; /* MT_unsafe */
+	ot->id = fc_atomic_int_add (next_id, +1);
 	ot->next = ot;
 
-	other_types = ot;
+	if (!fc_atomic_ptr_cmpexch (&other_types, ots, ot)) {
+	    free (ot);
+	    goto retry;
+	}
     }
 
     if (id)
@@ -95,10 +98,9 @@ FcObjectLookupIdByName (const char *str)
 const char *
 FcObjectLookupOtherNameById (FcObject id)
 {
-    /* XXX MT-unsafe */
     struct FcObjectOtherTypeInfo *ot;
 
-    for (ot = other_types; ot; ot = ot->next)
+    for (ot = fc_atomic_ptr_get (&other_types); ot; ot = ot->next)
 	if (ot->id == id)
 	    return ot->object.object;
 
@@ -114,10 +116,9 @@ FcObjectLookupOtherTypeByName (const char *str)
 FcPrivate const FcObjectType *
 FcObjectLookupOtherTypeById (FcObject id)
 {
-    /* XXX MT-unsafe */
     struct FcObjectOtherTypeInfo *ot;
 
-    for (ot = other_types; ot; ot = ot->next)
+    for (ot = fc_atomic_ptr_get (&other_types); ot; ot = ot->next)
 	if (ot->id == id)
 	    return &ot->object;
 
@@ -125,6 +126,5 @@ FcObjectLookupOtherTypeById (FcObject id)
 }
 
 
-#define __fcobjs__
 #include "fcaliastail.h"
 #undef __fcobjs__
commit 2ae07bbcd2a7650f2711b45e78e65e2ca1c4a17a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 15 19:35:03 2012 -0500

    Make FcDirCacheDispose() threadsafe

diff --git a/src/fccache.c b/src/fccache.c
index 77bf841..2f0f5d7 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -475,7 +475,7 @@ FcCacheFindByAddr (void *object)
 }
 
 static void
-FcCacheRemove (FcCache *cache)
+FcCacheRemoveUnlocked (FcCache *cache)
 {
     FcCacheSkip	    **update[FC_CACHE_MAX_LEVEL];
     FcCacheSkip	    *s, **next;
@@ -484,7 +484,6 @@ FcCacheRemove (FcCache *cache)
     /*
      * Find links along each chain
      */
-    lock_cache ();
     next = fcCacheChains;
     for (i = fcCacheMaxLevel; --i >= 0; )
     {
@@ -498,7 +497,6 @@ FcCacheRemove (FcCache *cache)
 	*update[i] = s->next[i];
     while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL)
 	fcCacheMaxLevel--;
-    unlock_cache ();
     free (s);
 }
 
@@ -522,8 +520,10 @@ FcCacheFindByStat (struct stat *cache_stat)
 }
 
 static void
-FcDirCacheDispose (FcCache *cache)
+FcDirCacheDisposeUnlocked (FcCache *cache)
 {
+    FcCacheRemoveUnlocked (cache);
+
     switch (cache->magic) {
     case FC_CACHE_MAGIC_ALLOC:
 	free (cache);
@@ -536,7 +536,6 @@ FcDirCacheDispose (FcCache *cache)
 #endif
 	break;
     }
-    FcCacheRemove (cache);
 }
 
 void
@@ -551,13 +550,16 @@ FcCacheObjectReference (void *object)
 void
 FcCacheObjectDereference (void *object)
 {
-    FcCacheSkip	*skip = FcCacheFindByAddr (object);
+    FcCacheSkip	*skip;
 
+    lock_cache ();
+    skip = FcCacheFindByAddrUnlocked (object);
     if (skip)
     {
 	if (FcRefDec (&skip->ref) <= 1)
-	    FcDirCacheDispose (skip->cache);
+	    FcDirCacheDisposeUnlocked (skip->cache);
     }
+    unlock_cache ();
 }
 
 void
commit 68b8ae9af8b0f86dade6135b01aaf0b2f2077fb5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Oct 10 15:24:31 2012 -0400

    Make cache hash threadsafe
    
    This concludes my first pass at making fontconfig threadsafe.  Now to
    testing and actually fixing it!

diff --git a/src/fccache.c b/src/fccache.c
index 751cea1..77bf841 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -248,6 +248,7 @@ struct _FcCacheSkip {
 
 #define FC_CACHE_MAX_LEVEL  16
 
+/* Protected by cache_lock below */
 static FcCacheSkip	*fcCacheChains[FC_CACHE_MAX_LEVEL];
 static int		fcCacheMaxLevel;
 
@@ -383,6 +384,8 @@ FcCacheInsert (FcCache *cache, struct stat *cache_stat)
     FcCacheSkip    *s, **next;
     int		    i, level;
 
+    lock_cache ();
+
     /*
      * Find links along each chain
      */
@@ -434,11 +437,13 @@ FcCacheInsert (FcCache *cache, struct stat *cache_stat)
 	s->next[i] = *update[i];
 	*update[i] = s;
     }
+
+    unlock_cache ();
     return FcTrue;
 }
 
 static FcCacheSkip *
-FcCacheFindByAddr (void *object)
+FcCacheFindByAddrUnlocked (void *object)
 {
     int	    i;
     FcCacheSkip    **next = fcCacheChains;
@@ -459,6 +464,16 @@ FcCacheFindByAddr (void *object)
     return NULL;
 }
 
+static FcCacheSkip *
+FcCacheFindByAddr (void *object)
+{
+    FcCacheSkip *ret;
+    lock_cache ();
+    ret = FcCacheFindByAddrUnlocked (object);
+    unlock_cache ();
+    return ret;
+}
+
 static void
 FcCacheRemove (FcCache *cache)
 {
@@ -469,6 +484,7 @@ FcCacheRemove (FcCache *cache)
     /*
      * Find links along each chain
      */
+    lock_cache ();
     next = fcCacheChains;
     for (i = fcCacheMaxLevel; --i >= 0; )
     {
@@ -482,6 +498,7 @@ FcCacheRemove (FcCache *cache)
 	*update[i] = s->next[i];
     while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL)
 	fcCacheMaxLevel--;
+    unlock_cache ();
     free (s);
 }
 
@@ -490,14 +507,17 @@ FcCacheFindByStat (struct stat *cache_stat)
 {
     FcCacheSkip	    *s;
 
+    lock_cache ();
     for (s = fcCacheChains[0]; s; s = s->next[0])
 	if (s->cache_dev == cache_stat->st_dev &&
 	    s->cache_ino == cache_stat->st_ino &&
 	    s->cache_mtime == cache_stat->st_mtime)
 	{
 	    FcRefInc (&s->ref);
+	    unlock_cache ();
 	    return s->cache;
 	}
+    unlock_cache ();
     return NULL;
 }
 
@@ -987,13 +1007,16 @@ FcDirCacheWrite (FcCache *cache, FcConfig *config)
      * new cache file is not read again.  If it's large, we don't do that
      * such that we reload it, using mmap, which is shared across processes.
      */
-    if (cache->size < FC_CACHE_MIN_MMAP &&
-	(skip = FcCacheFindByAddr (cache)) &&
-	FcStat (cache_hashed, &cache_stat))
+    if (cache->size < FC_CACHE_MIN_MMAP && FcStat (cache_hashed, &cache_stat))
     {
-	skip->cache_dev = cache_stat.st_dev;
-	skip->cache_ino = cache_stat.st_ino;
-	skip->cache_mtime = cache_stat.st_mtime;
+	lock_cache ();
+	if ((skip = FcCacheFindByAddrUnlocked (cache)))
+	{
+	    skip->cache_dev = cache_stat.st_dev;
+	    skip->cache_ino = cache_stat.st_ino;
+	    skip->cache_mtime = cache_stat.st_mtime;
+	}
+	unlock_cache ();
     }
 
     FcStrFree (cache_hashed);
commit adb03b730de5d090855f45bc23b934a65ef2399c
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 8 20:03:35 2012 -0400

    Make random-state initialization threadsafe

diff --git a/src/fccache.c b/src/fccache.c
index 14dcf32..751cea1 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -323,6 +323,11 @@ retry:
       FcMutexFinish (lock);
       goto retry;
     }
+
+    FcMutexLock (lock);
+    /* Initialize random state */
+    FcRandom ();
+    return;
   }
   FcMutexLock (lock);
 }
commit 91dd7d28ffc397fb1389f76ac55b397e55da809d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 8 20:02:05 2012 -0400

    Add a big cache lock
    
    Not used yet.

diff --git a/src/fccache.c b/src/fccache.c
index 1e9745a..14dcf32 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -307,6 +307,45 @@ FcRandom(void)
     return result;
 }
 
+
+static FcMutex *cache_lock;
+
+static void
+lock_cache (void)
+{
+  FcMutex *lock;
+retry:
+  lock = fc_atomic_ptr_get (&cache_lock);
+  if (!lock) {
+    lock = (FcMutex *) malloc (sizeof (FcMutex));
+    FcMutexInit (lock);
+    if (!fc_atomic_ptr_cmpexch (&cache_lock, NULL, lock)) {
+      FcMutexFinish (lock);
+      goto retry;
+    }
+  }
+  FcMutexLock (lock);
+}
+
+static void
+unlock_cache (void)
+{
+  FcMutexUnlock (cache_lock);
+}
+
+static void
+free_lock (void)
+{
+  FcMutex *lock;
+  lock = fc_atomic_ptr_get (&cache_lock);
+  if (lock && fc_atomic_ptr_cmpexch (&cache_lock, lock, NULL)) {
+    FcMutexFinish (lock);
+    free (lock);
+  }
+}
+
+
+
 /*
  * Generate a random level number, distributed
  * so that each level is 1/4 as likely as the one before
@@ -504,6 +543,8 @@ FcCacheFini (void)
     for (i = 0; i < FC_CACHE_MAX_LEVEL; i++)
 	assert (fcCacheChains[i] == NULL);
     assert (fcCacheMaxLevel == 0);
+
+    free_lock ();
 }
 
 static FcBool
commit 8d2bbb193ae49ea6abd5a9b4d28d3d88ac97d7a2
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 21:03:58 2012 -0400

    Make cache refcounting threadsafe

diff --git a/src/fcatomic.h b/src/fcatomic.h
index c066d12..71c0491 100644
--- a/src/fcatomic.h
+++ b/src/fcatomic.h
@@ -117,6 +117,7 @@ typedef struct _FcRef { fc_atomic_int_t count; } FcRef;
 static inline void   FcRefInit     (FcRef *r, int v) { r->count = v; }
 static inline int    FcRefInc      (FcRef *r) { return fc_atomic_int_add (r->count, +1); }
 static inline int    FcRefDec      (FcRef *r) { return fc_atomic_int_add (r->count, -1); }
+static inline int    FcRefAdd      (FcRef *r, int v) { return fc_atomic_int_add (r->count, v); }
 static inline void   FcRefSetConst (FcRef *r) { r->count = FC_REF_CONSTANT_VALUE; }
 static inline FcBool FcRefIsConst  (const FcRef *r) { return r->count == FC_REF_CONSTANT_VALUE; }
 
diff --git a/src/fccache.c b/src/fccache.c
index 19293e2..1e9745a 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -233,7 +233,7 @@ typedef struct _FcCacheSkip FcCacheSkip;
 
 struct _FcCacheSkip {
     FcCache	    *cache;
-    int		    ref;
+    FcRef	    ref;
     intptr_t	    size;
     dev_t	    cache_dev;
     ino_t	    cache_ino;
@@ -368,7 +368,7 @@ FcCacheInsert (FcCache *cache, struct stat *cache_stat)
 
     s->cache = cache;
     s->size = cache->size;
-    s->ref = 1;
+    FcRefInit (&s->ref, 1);
     if (cache_stat)
     {
 	s->cache_dev = cache_stat->st_dev;
@@ -451,7 +451,7 @@ FcCacheFindByStat (struct stat *cache_stat)
 	    s->cache_ino == cache_stat->st_ino &&
 	    s->cache_mtime == cache_stat->st_mtime)
 	{
-	    s->ref++;
+	    FcRefInc (&s->ref);
 	    return s->cache;
 	}
     return NULL;
@@ -481,7 +481,7 @@ FcCacheObjectReference (void *object)
     FcCacheSkip *skip = FcCacheFindByAddr (object);
 
     if (skip)
-	skip->ref++;
+	FcRefInc (&skip->ref);
 }
 
 void
@@ -491,8 +491,7 @@ FcCacheObjectDereference (void *object)
 
     if (skip)
     {
-	skip->ref--;
-	if (skip->ref <= 0)
+	if (FcRefDec (&skip->ref) <= 1)
 	    FcDirCacheDispose (skip->cache);
     }
 }
@@ -618,7 +617,7 @@ FcDirCacheReference (FcCache *cache, int nref)
     FcCacheSkip *skip = FcCacheFindByAddr (cache);
 
     if (skip)
-	skip->ref += nref;
+	FcRefAdd (&skip->ref, nref);
 }
 
 void
commit 31ee38e541180db6d7bc58d5abde83136352e7ce
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 17:46:12 2012 -0400

    Minor

diff --git a/src/fccfg.c b/src/fccfg.c
index 3245ede..1069cd2 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -1741,7 +1741,7 @@ FcConfigSubstitute (FcConfig	*config,
 #  define WIN32_EXTRA_LEAN
 #  include <windows.h>
 
-static FcChar8 fontconfig_path[1000] = "";
+static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
 
 #  if (defined (PIC) || defined (DLL_EXPORT))
 
@@ -1919,7 +1919,7 @@ FcConfigFreePath (FcChar8 **path)
     free (path);
 }
 
-static FcBool	_FcConfigHomeEnabled = FcTrue;
+static FcBool	_FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
 
 FcChar8 *
 FcConfigHome (void)
commit b8f238e49d7b7e1cff787c518cd4490b32039cca
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 17:42:18 2012 -0400

    Make FcCacheIsMmapSafe() threadsafe

diff --git a/src/fccache.c b/src/fccache.c
index aace6b3..19293e2 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -59,26 +59,32 @@ static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
 static FcBool
 FcCacheIsMmapSafe (int fd)
 {
-    static FcBool is_initialized = FcFalse;
-    static FcBool is_env_available = FcFalse;
-    static FcBool use_mmap = FcFalse;
+    enum {
+      MMAP_NOT_INITIALIZED = 0,
+      MMAP_USE,
+      MMAP_DONT_USE,
+      MMAP_CHECK_FS,
+    } status;
+    static void *static_status;
 
-    if (!is_initialized)
-    {
-	const char *env;
+    status = (intptr_t) fc_atomic_ptr_get (&static_status);
 
-	env = getenv ("FONTCONFIG_USE_MMAP");
-	if (env)
-	{
-	    if (FcNameBool ((const FcChar8 *)env, &use_mmap))
-		is_env_available = FcTrue;
-	}
-	is_initialized = FcTrue;
+    if (status == MMAP_NOT_INITIALIZED)
+    {
+	const char *env = getenv ("FONTCONFIG_USE_MMAP");
+	FcBool use;
+	if (env && FcNameBool ((const FcChar8 *) env, &use))
+	    status =  use ? MMAP_USE : MMAP_DONT_USE;
+	else
+	    status = MMAP_CHECK_FS;
+	fc_atomic_ptr_cmpexch (&static_status, NULL, (void *) status);
     }
-    if (is_env_available)
-	return use_mmap;
 
-    return FcIsFsMmapSafe (fd);
+    if (status == MMAP_CHECK_FS)
+	return FcIsFsMmapSafe (fd);
+    else
+	return status == MMAP_USE;
+
 }
 
 static const char bin2hex[] = { '0', '1', '2', '3',
commit b27a22aae9902d409c21e5bb19a97dcc5966ea24
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 17:29:45 2012 -0400

    Minor

diff --git a/src/fcinit.c b/src/fcinit.c
index a7d4bc5..146fbc1 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -160,8 +160,7 @@ FcInitReinitialize (void)
     config = FcInitLoadConfigAndFonts ();
     if (!config)
 	return FcFalse;
-    FcConfigSetCurrent (config);
-    return FcTrue;
+    return FcConfigSetCurrent (config);
 }
 
 FcBool
commit 0552f26016865b8a76819cf342fa0cf13afdc5e8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 17:02:50 2012 -0400

    Make default-FcConfig threadsafe

diff --git a/src/fccfg.c b/src/fccfg.c
index 1ac7c43..3245ede 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -22,6 +22,8 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
+/* Objects MT-safe for readonly access. */
+
 #include "fcint.h"
 #include <dirent.h>
 #include <sys/types.h>
@@ -36,27 +38,38 @@
 #define R_OK 4
 #endif
 
-static FcConfig    *_fcConfig;
+static FcConfig    *_fcConfig; /* MT-safe */
 
-FcBool
-FcConfigInit (void)
+static FcConfig *
+FcConfigEnsure (void)
 {
     FcConfig	*config;
-
-    if (_fcConfig)
-	return FcTrue;
-    config = FcInitLoadConfigAndFonts ();
+retry:
+    config = fc_atomic_ptr_get (&_fcConfig);
     if (!config)
-	return FcFalse;
-    FcConfigSetCurrent (config);
-    return FcTrue;
+    {
+	config = FcInitLoadConfigAndFonts ();
+
+	if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
+	    FcConfigDestroy (config);
+	    goto retry;
+	}
+    }
+    return config;
+}
+
+FcBool
+FcConfigInit (void)
+{
+  return FcConfigEnsure () ? FcTrue : FcFalse;
 }
 
 void
 FcConfigFini (void)
 {
-    if (_fcConfig)
-	FcConfigDestroy (_fcConfig);
+    FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
+    if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
+	FcConfigDestroy (cfg);
 }
 
 
@@ -257,8 +270,7 @@ FcConfigDestroy (FcConfig *config)
     if (FcRefDec (&config->ref) != 1)
 	return;
 
-    if (config == _fcConfig)
-	_fcConfig = 0;
+    fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
 
     FcStrSetDestroy (config->configDirs);
     FcStrSetDestroy (config->fontDirs);
@@ -411,25 +423,31 @@ FcConfigBuildFonts (FcConfig *config)
 FcBool
 FcConfigSetCurrent (FcConfig *config)
 {
-    if (config == _fcConfig)
+    FcConfig *cfg;
+
+retry:
+    cfg = fc_atomic_ptr_get (&_fcConfig);
+
+    if (config == cfg)
 	return FcTrue;
 
     if (!config->fonts[FcSetSystem])
 	if (!FcConfigBuildFonts (config))
 	    return FcFalse;
 
-    if (_fcConfig)
-	FcConfigDestroy (_fcConfig);
-    _fcConfig = config;
+    if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
+	goto retry;
+
+    if (cfg)
+	FcConfigDestroy (cfg);
+
     return FcTrue;
 }
 
 FcConfig *
 FcConfigGetCurrent (void)
 {
-    if (!_fcConfig)
-	FcConfigInit ();
-    return _fcConfig;
+    return FcConfigEnsure ();
 }
 
 FcBool
diff --git a/src/fcdefault.c b/src/fcdefault.c
index 2f13659..0069db2 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -119,7 +119,6 @@ FcDefaultFini (void)
     if (langs && fc_atomic_ptr_cmpexch (&default_langs, langs, NULL)) {
 	FcRefInit (&langs->ref, 1);
 	FcStrSetDestroy (langs);
-	langs = NULL;
     }
 }
 
@@ -212,7 +211,7 @@ FcDefaultSubstitute (FcPattern *pattern)
 	FcPatternObjectAdd (pattern, FC_FULLNAMELANG_OBJECT, namelang, FcTrue);
 	FcPatternObjectAddWithBinding (pattern, FC_FULLNAMELANG_OBJECT, v2, FcValueBindingWeak, FcTrue);
     }
-    FcSharedStrFree ((char *) v2.u.s);
+    FcSharedStrFree ((FcChar8 *) v2.u.s);
 }
 #define __fcdefault__
 #include "fcaliastail.h"
commit e53f5da54f066f73a53eba1f82f54521fa3f7ea2
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 16:42:36 2012 -0400

    Minor

diff --git a/src/fccfg.c b/src/fccfg.c
index d7e588c..1ac7c43 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -428,8 +428,7 @@ FcConfig *
 FcConfigGetCurrent (void)
 {
     if (!_fcConfig)
-	if (!FcConfigInit ())
-	    return 0;
+	FcConfigInit ();
     return _fcConfig;
 }
 
commit 7ae24b4554a8543d8cd41a83b6114e0143982758
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 16:37:03 2012 -0400

    Refactor; contain default config in fccfg.c

diff --git a/src/fccfg.c b/src/fccfg.c
index dd34f5a..d7e588c 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -36,7 +36,29 @@
 #define R_OK 4
 #endif
 
-FcConfig    *_fcConfig;
+static FcConfig    *_fcConfig;
+
+FcBool
+FcConfigInit (void)
+{
+    FcConfig	*config;
+
+    if (_fcConfig)
+	return FcTrue;
+    config = FcInitLoadConfigAndFonts ();
+    if (!config)
+	return FcFalse;
+    FcConfigSetCurrent (config);
+    return FcTrue;
+}
+
+void
+FcConfigFini (void)
+{
+    if (_fcConfig)
+	FcConfigDestroy (_fcConfig);
+}
+
 
 FcConfig *
 FcConfigCreate (void)
@@ -406,7 +428,7 @@ FcConfig *
 FcConfigGetCurrent (void)
 {
     if (!_fcConfig)
-	if (!FcInit ())
+	if (!FcConfigInit ())
 	    return 0;
     return _fcConfig;
 }
diff --git a/src/fcinit.c b/src/fcinit.c
index 75d96a9..a7d4bc5 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -135,15 +135,7 @@ FcInitLoadConfigAndFonts (void)
 FcBool
 FcInit (void)
 {
-    FcConfig	*config;
-
-    if (_fcConfig)
-	return FcTrue;
-    config = FcInitLoadConfigAndFonts ();
-    if (!config)
-	return FcFalse;
-    FcConfigSetCurrent (config);
-    return FcTrue;
+    return FcConfigInit ();
 }
 
 /*
@@ -152,9 +144,7 @@ FcInit (void)
 void
 FcFini (void)
 {
-    if (_fcConfig)
-	FcConfigDestroy (_fcConfig);
-
+    FcConfigFini ();
     FcCacheFini ();
     FcDefaultFini ();
 }
diff --git a/src/fcint.h b/src/fcint.h
index f678b0c..17298f7 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -505,8 +505,6 @@ struct _FcConfig {
     FcExprPage *expr_pool;	    /* pool of FcExpr's */
 };
 
-extern FcPrivate FcConfig	*_fcConfig;
-
 typedef struct _FcFileTime {
     time_t  time;
     FcBool  set;
@@ -569,6 +567,12 @@ FcDirCacheReference (FcCache *cache, int nref);
 
 /* fccfg.c */
 
+FcPrivate FcBool
+FcConfigInit (void);
+
+FcPrivate void
+FcConfigFini (void);
+
 FcPrivate FcChar8 *
 FcConfigXdgCacheHome (void);
 
diff --git a/src/fcxml.c b/src/fcxml.c
index cf9c8dd..8c934f5 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -1107,7 +1107,7 @@ FcPStackPop (FcConfigParse *parse)
 }
 
 static FcBool
-FcConfigInit (FcConfigParse *parse, const FcChar8 *name, FcConfig *config, XML_Parser parser)
+FcConfigParseInit (FcConfigParse *parse, const FcChar8 *name, FcConfig *config, XML_Parser parser)
 {
     parse->pstack = 0;
     parse->pstack_static_used = 0;
@@ -2984,7 +2984,7 @@ FcConfigParseAndLoad (FcConfig	    *config,
     if (!p)
 	goto bail1;
 
-    if (!FcConfigInit (&parse, name, config, p))
+    if (!FcConfigParseInit (&parse, name, config, p))
 	goto bail2;
 
 #ifndef ENABLE_LIBXML2
commit 32b0d88923524c24f4be36733ebef5872b57b95a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 16:26:53 2012 -0400

    Make FcDefaultFini() threadsafe

diff --git a/src/fcdefault.c b/src/fcdefault.c
index 62cf63b..2f13659 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -107,14 +107,19 @@ retry:
 void
 FcDefaultFini (void)
 {
-    if (default_lang) {
-	free (default_lang);
-	default_lang = NULL;
+    FcChar8  *lang;
+    FcStrSet *langs;
+
+    lang = fc_atomic_ptr_get (&default_lang);
+    if (lang && fc_atomic_ptr_cmpexch (&default_lang, lang, NULL)) {
+	free (lang);
     }
-    if (default_langs) {
-	FcRefInit (&default_langs->ref, 1);
-	FcStrSetDestroy (default_langs);
-	default_langs = NULL;
+
+    langs = fc_atomic_ptr_get (&default_langs);
+    if (langs && fc_atomic_ptr_cmpexch (&default_langs, langs, NULL)) {
+	FcRefInit (&langs->ref, 1);
+	FcStrSetDestroy (langs);
+	langs = NULL;
     }
 }
 
commit 7019896c99872b23d89b1404b02754cbc4ea1456
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 16:09:35 2012 -0400

    Make FcInitDebug() idempotent

diff --git a/src/fcdbg.c b/src/fcdbg.c
index e8cbe32..270d791 100644
--- a/src/fcdbg.c
+++ b/src/fcdbg.c
@@ -455,15 +455,17 @@ int FcDebugVal;
 void
 FcInitDebug (void)
 {
-    char    *e;
+    if (!FcDebugVal) {
+	char    *e;
 
-    e = getenv ("FC_DEBUG");
-    if (e)
-    {
-        printf ("FC_DEBUG=%s\n", e);
-        FcDebugVal = atoi (e);
-        if (FcDebugVal < 0)
-   	    FcDebugVal = 0;
+	e = getenv ("FC_DEBUG");
+	if (e)
+	{
+	    printf ("FC_DEBUG=%s\n", e);
+	    FcDebugVal = atoi (e);
+	    if (FcDebugVal < 0)
+		FcDebugVal = 0;
+	}
     }
 }
 #define __fcdbg__
commit b97ab0c94938448dc2b780b8f0f60fb68884899f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 15:52:25 2012 -0400

    Make FcGetDefaultLang and FcGetDefaultLangs thread-safe

diff --git a/src/fcdefault.c b/src/fcdefault.c
index d977857..62cf63b 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -25,6 +25,8 @@
 #include "fcint.h"
 #include <string.h>
 
+/* MT-safe */
+
 static const struct {
     FcObject	field;
     FcBool	value;
@@ -32,7 +34,6 @@ static const struct {
     { FC_HINTING_OBJECT,	   FcTrue	},  /* !FT_LOAD_NO_HINTING */
     { FC_VERTICAL_LAYOUT_OBJECT,   FcFalse	},  /* FC_LOAD_VERTICAL_LAYOUT */
     { FC_AUTOHINT_OBJECT,	   FcFalse	},  /* FC_LOAD_FORCE_AUTOHINT */
-    /* XXX: FC_GLOBAL_ADVANCE is deprecated */
     { FC_GLOBAL_ADVANCE_OBJECT,    FcTrue	},  /* !FC_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH */
     { FC_EMBEDDED_BITMAP_OBJECT,   FcTrue 	},  /* !FC_LOAD_NO_BITMAP */
     { FC_DECORATIVE_OBJECT,	   FcFalse	},
@@ -40,45 +41,81 @@ static const struct {
 
 #define NUM_FC_BOOL_DEFAULTS	(int) (sizeof FcBoolDefaults / sizeof FcBoolDefaults[0])
 
+FcStrSet *default_langs;
+
 FcStrSet *
 FcGetDefaultLangs (void)
 {
-    FcStrSet *result = FcStrSetCreate ();
-    char *langs;
-
-    langs = getenv ("FC_LANG");
-    if (!langs || !langs[0])
-	langs = getenv ("LC_ALL");
-    if (!langs || !langs[0])
-	langs = getenv ("LC_CTYPE");
-    if (!langs || !langs[0])
-	langs = getenv ("LANG");
-    if (langs && langs[0])
+    FcStrSet *result;
+retry:
+    result = (FcStrSet *) fc_atomic_ptr_get (&default_langs);
+    if (!result)
     {
-	if (!FcStrSetAddLangs (result, langs))
+	char *langs;
+
+	result = FcStrSetCreate ();
+
+	langs = getenv ("FC_LANG");
+	if (!langs || !langs[0])
+	    langs = getenv ("LC_ALL");
+	if (!langs || !langs[0])
+	    langs = getenv ("LC_CTYPE");
+	if (!langs || !langs[0])
+	    langs = getenv ("LANG");
+	if (langs && langs[0])
+	{
+	    if (!FcStrSetAddLangs (result, langs))
+		FcStrSetAdd (result, (const FcChar8 *) "en");
+	}
+	else
 	    FcStrSetAdd (result, (const FcChar8 *) "en");
+
+	FcRefSetConst (&result->ref);
+	if (!fc_atomic_ptr_cmpexch (&default_langs, NULL, result)) {
+	    FcRefInit (&result->ref, 1);
+	    FcStrSetDestroy (result);
+	    goto retry;
+	}
     }
-    else
-	FcStrSetAdd (result, (const FcChar8 *) "en");
 
     return result;
 }
 
+static FcChar8 *default_lang; /* MT-safe */
+
 FcChar8 *
 FcGetDefaultLang (void)
 {
-    static FcChar8 lang_local[128] = {0};
-    FcStrSet *langs;
-
-    if (!lang_local[0])
+    FcChar8 *lang;
+retry:
+    lang = fc_atomic_ptr_get (&default_lang);
+    if (!lang)
     {
-	langs = FcGetDefaultLangs ();
-	strncpy ((char *)lang_local, (const char *)langs->strs[0], 127);
-	lang_local[127] = 0;
+	FcStrSet *langs = FcGetDefaultLangs ();
+	lang = (FcChar8 *) strdup ((const char *) langs->strs[0]);
 	FcStrSetDestroy (langs);
+
+	if (!fc_atomic_ptr_cmpexch (&default_lang, NULL, lang)) {
+	    free (lang);
+	    goto retry;
+	}
     }
 
-    return lang_local;
+    return lang;
+}
+
+void
+FcDefaultFini (void)
+{
+    if (default_lang) {
+	free (default_lang);
+	default_lang = NULL;
+    }
+    if (default_langs) {
+	FcRefInit (&default_langs->ref, 1);
+	FcStrSetDestroy (default_langs);
+	default_langs = NULL;
+    }
 }
 
 void
diff --git a/src/fcinit.c b/src/fcinit.c
index 31578ad..75d96a9 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -156,6 +156,7 @@ FcFini (void)
 	FcConfigDestroy (_fcConfig);
 
     FcCacheFini ();
+    FcDefaultFini ();
 }
 
 /*
diff --git a/src/fcint.h b/src/fcint.h
index ab4a7e1..f678b0c 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -761,6 +761,9 @@ FcInitDebug (void);
 FcPrivate FcChar8 *
 FcGetDefaultLang (void);
 
+FcPrivate void
+FcDefaultFini (void);
+
 /* fcdir.c */
 
 FcPrivate FcBool
diff --git a/src/fcstr.c b/src/fcstr.c
index e72be5e..f278022 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -1234,6 +1234,10 @@ FcStrSetDestroy (FcStrSet *set)
 {
     int	i;
 
+    /* We rely on this in FcGetDefaultLangs for caching. */
+    if (FcRefIsConst (&set->ref))
+	return;
+
     if (FcRefDec (&set->ref) != 1)
 	return;
 
commit 64af9e1917114c789ad74dd28b3248f8c0525f45
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 14:41:38 2012 -0400

    Make refcounts, patterns, charsets, strings, and FcLang thread-safe

diff --git a/src/fcatomic.h b/src/fcatomic.h
index 7479a3d..c066d12 100644
--- a/src/fcatomic.h
+++ b/src/fcatomic.h
@@ -63,7 +63,7 @@ static inline void HBMemoryBarrier (void) {
 }
 #endif
 
-typedef long fc_atomic_int_t;
+typedef int fc_atomic_int_t;
 #define fc_atomic_int_add(AI, V)	InterlockedExchangeAdd (&(AI), (V))
 
 #define fc_atomic_ptr_get(P)		(HBMemoryBarrier (), (void *) *(P))
@@ -74,7 +74,7 @@ typedef long fc_atomic_int_t;
 
 #include <libkern/OSAtomic.h>
 
-typedef int32_t fc_atomic_int_t;
+typedef int fc_atomic_int_t;
 #define fc_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
 
 #define fc_atomic_ptr_get(P)		(OSMemoryBarrier (), (void *) *(P))
@@ -111,13 +111,13 @@ typedef int fc_atomic_int_t;
 #endif
 
 /* reference count */
-#define FC_REF_CONSTANT ((fc_atomic_int_t) -1)
-#define FC_REF_CONSTANT_INIT {FC_REF_CONSTANT}
+#define FC_REF_CONSTANT_VALUE ((fc_atomic_int_t) -1)
+#define FC_REF_CONSTANT {FC_REF_CONSTANT_VALUE}
 typedef struct _FcRef { fc_atomic_int_t count; } FcRef;
-static inline void   FcRefInit    (FcRef *r, int v) { r->count = v; }
-static inline int    FcRefInc     (FcRef *r) { return fc_atomic_int_add (r->count, +1); }
-static inline int    FcRefDec     (FcRef *r) { return fc_atomic_int_add (r->count, -1); }
-static inline void   FcRefFinish  (FcRef *r) { r->count = FC_REF_CONSTANT; }
-static inline FcBool FcRefIsConst (FcRef *r) { return r->count == FC_REF_CONSTANT; }
+static inline void   FcRefInit     (FcRef *r, int v) { r->count = v; }
+static inline int    FcRefInc      (FcRef *r) { return fc_atomic_int_add (r->count, +1); }
+static inline int    FcRefDec      (FcRef *r) { return fc_atomic_int_add (r->count, -1); }
+static inline void   FcRefSetConst (FcRef *r) { r->count = FC_REF_CONSTANT_VALUE; }
+static inline FcBool FcRefIsConst  (const FcRef *r) { return r->count == FC_REF_CONSTANT_VALUE; }
 
 #endif /* _FCATOMIC_H_ */
diff --git a/src/fccfg.c b/src/fccfg.c
index ef5f4ff..dd34f5a 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -94,7 +94,7 @@ FcConfigCreate (void)
 
     config->expr_pool = NULL;
 
-    config->ref = 1;
+    FcRefInit (&config->ref, 1);
 
     return config;
 
@@ -221,7 +221,7 @@ FcConfigReference (FcConfig *config)
 	    return 0;
     }
 
-    config->ref++;
+    FcRefInc (&config->ref);
 
     return config;
 }
@@ -232,7 +232,7 @@ FcConfigDestroy (FcConfig *config)
     FcSetName	set;
     FcExprPage	*page;
 
-    if (--config->ref > 0)
+    if (FcRefDec (&config->ref) != 1)
 	return;
 
     if (config == _fcConfig)
@@ -662,7 +662,7 @@ typedef struct _FcSubState {
 } FcSubState;
 
 static FcValue
-FcConfigPromote (FcValue v, FcValue u)
+FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
 {
     if (v.type == FcTypeInteger)
     {
@@ -674,9 +674,9 @@ FcConfigPromote (FcValue v, FcValue u)
 	v.u.m = &FcIdentityMatrix;
 	v.type = FcTypeMatrix;
     }
-    else if (v.type == FcTypeString && u.type == FcTypeLangSet)
+    else if (buf && v.type == FcTypeString && u.type == FcTypeLangSet)
     {
-	v.u.l = FcLangSetPromote (v.u.s);
+	v.u.l = FcLangSetPromote (v.u.s, buf);
 	v.type = FcTypeLangSet;
     }
     return v;
@@ -692,9 +692,10 @@ FcConfigCompareValue (const FcValue	*left_o,
     FcBool	ret = FcFalse;
     FcOp	op = FC_OP_GET_OP (op_);
     int		flags = FC_OP_GET_FLAGS (op_);
+    FcValuePromotionBuffer buf1, buf2;
 
-    left = FcConfigPromote (left, right);
-    right = FcConfigPromote (right, left);
+    left = FcConfigPromote (left, right, &buf1);
+    right = FcConfigPromote (right, left, &buf2);
     if (left.type == right.type)
     {
 	switch (left.type) {
@@ -893,10 +894,10 @@ FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
 	  FcMatrix m;
 	  FcValue xx, xy, yx, yy;
 	  v.type = FcTypeMatrix;
-	  xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v);
-	  xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v);
-	  yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v);
-	  yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v);
+	  xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
+	  xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
+	  yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
+	  yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
 	  if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
 	      yx.type == FcTypeDouble && yy.type == FcTypeDouble)
 	  {
@@ -987,8 +988,8 @@ FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
     case FcOpDivide:
 	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
 	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
-	vl = FcConfigPromote (vl, vr);
-	vr = FcConfigPromote (vr, vl);
+	vl = FcConfigPromote (vl, vr, NULL);
+	vr = FcConfigPromote (vr, vl, NULL);
 	if (vl.type == vr.type)
 	{
 	    switch ((int) vl.type) {
diff --git a/src/fccharset.c b/src/fccharset.c
index df98eb2..c9f928c 100644
--- a/src/fccharset.c
+++ b/src/fccharset.c
@@ -35,7 +35,7 @@ FcCharSetCreate (void)
     fcs = (FcCharSet *) malloc (sizeof (FcCharSet));
     if (!fcs)
 	return 0;
-    fcs->ref = 1;
+    FcRefInit (&fcs->ref, 1);
     fcs->num = 0;
     fcs->leaves_offset = 0;
     fcs->numbers_offset = 0;
@@ -55,12 +55,12 @@ FcCharSetDestroy (FcCharSet *fcs)
 
     if (fcs)
     {
-	if (fcs->ref == FC_REF_CONSTANT)
+	if (FcRefIsConst (&fcs->ref))
 	{
 	    FcCacheObjectDereference (fcs);
 	    return;
 	}
-	if (--fcs->ref > 0)
+	if (FcRefDec (&fcs->ref) != 1)
 	    return;
 	for (i = 0; i < fcs->num; i++)
 	    free (FcCharSetLeaf (fcs, i));
@@ -237,7 +237,7 @@ FcCharSetAddChar (FcCharSet *fcs, FcChar32 ucs4)
     FcCharLeaf	*leaf;
     FcChar32	*b;
 
-    if (fcs == NULL || fcs->ref == FC_REF_CONSTANT)
+    if (fcs == NULL || FcRefIsConst (&fcs->ref))
 	return FcFalse;
     leaf = FcCharSetFindLeafCreate (fcs, ucs4);
     if (!leaf)
@@ -253,7 +253,7 @@ FcCharSetDelChar (FcCharSet *fcs, FcChar32 ucs4)
     FcCharLeaf	*leaf;
     FcChar32	*b;
 
-    if (fcs == NULL || fcs->ref == FC_REF_CONSTANT)
+    if (fcs == NULL || FcRefIsConst (&fcs->ref))
 	return FcFalse;
     leaf = FcCharSetFindLeaf (fcs, ucs4);
     if (!leaf)
@@ -329,8 +329,8 @@ FcCharSetCopy (FcCharSet *src)
 {
     if (src)
     {
-	if (src->ref != FC_REF_CONSTANT)
-	    src->ref++;
+	if (!FcRefIsConst (&src->ref))
+	    FcRefInc (&src->ref);
 	else
 	    FcCacheObjectReference (src);
     }
@@ -488,7 +488,7 @@ FcCharSetMerge (FcCharSet *a, const FcCharSet *b, FcBool *changed)
     if (!a || !b)
 	return FcFalse;
 
-    if (a->ref == FC_REF_CONSTANT) {
+    if (FcRefIsConst (&a->ref)) {
 	if (changed)
 	    *changed = FcFalse;
 	return FcFalse;
@@ -1204,7 +1204,7 @@ FcCharSetFreezeBase (FcCharSetFreezer *freezer, FcCharSet *fcs)
 
     freezer->charsets_allocated++;
 
-    ent->set.ref = FC_REF_CONSTANT;
+    FcRefSetConst (&ent->set.ref);
     ent->set.num = fcs->num;
     if (fcs->num)
     {
@@ -1338,7 +1338,7 @@ FcCharSetSerializeAlloc (FcSerialize *serialize, const FcCharSet *cs)
     FcChar16	    *numbers;
     int		    i;
 
-    if (cs->ref != FC_REF_CONSTANT)
+    if (!FcRefIsConst (&cs->ref))
     {
 	if (!serialize->cs_freezer)
 	{
@@ -1377,7 +1377,7 @@ FcCharSetSerialize(FcSerialize *serialize, const FcCharSet *cs)
     FcCharLeaf	*leaf, *leaf_serialized;
     int		i;
 
-    if (cs->ref != FC_REF_CONSTANT && serialize->cs_freezer)
+    if (!FcRefIsConst (&cs->ref) && serialize->cs_freezer)
     {
 	cs = FcCharSetFindFrozen (serialize->cs_freezer, cs);
 	if (!cs)
@@ -1388,7 +1388,7 @@ FcCharSetSerialize(FcSerialize *serialize, const FcCharSet *cs)
     if (!cs_serialized)
 	return NULL;
 
-    cs_serialized->ref = FC_REF_CONSTANT;
+    FcRefSetConst (&cs_serialized->ref);
     cs_serialized->num = cs->num;
 
     if (cs->num)
diff --git a/src/fcdefault.c b/src/fcdefault.c
index 8ad1b1e..d977857 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -170,7 +170,7 @@ FcDefaultSubstitute (FcPattern *pattern)
 	FcPatternObjectAdd (pattern, FC_FULLNAMELANG_OBJECT, namelang, FcTrue);
 	FcPatternObjectAddWithBinding (pattern, FC_FULLNAMELANG_OBJECT, v2, FcValueBindingWeak, FcTrue);
     }
-    FcSharedStrFree (v2.u.s);
+    FcSharedStrFree ((char *) v2.u.s);
 }
 #define __fcdefault__
 #include "fcaliastail.h"
diff --git a/src/fcint.h b/src/fcint.h
index d881a77..ab4a7e1 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -35,6 +35,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#include <assert.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stddef.h>
@@ -108,6 +109,8 @@ extern pfnSHGetFolderPathA pSHGetFolderPathA;
 #define FcPrivate
 #endif
 
+FC_ASSERT_STATIC (sizeof (FcRef) == sizeof (int));
+
 typedef enum _FcValueBinding {
     FcValueBindingWeak, FcValueBindingStrong, FcValueBindingSame
 } FcValueBinding;
@@ -188,7 +191,7 @@ struct _FcPattern {
     int		    num;
     int		    size;
     intptr_t	    elts_offset;
-    int		    ref;
+    FcRef	    ref;
 };
 
 #define FcPatternElts(p)	FcOffsetMember(p,elts_offset,FcPatternElt)
@@ -296,7 +299,7 @@ typedef struct _FcCharLeaf {
 } FcCharLeaf;
 
 struct _FcCharSet {
-    int		    ref;	/* reference count */
+    FcRef	    ref;	/* reference count */
     int		    num;	/* size of leaves and numbers arrays */
     intptr_t	    leaves_offset;
     intptr_t	    numbers_offset;
@@ -309,7 +312,7 @@ struct _FcCharSet {
 #define FcCharSetNumbers(c)	FcOffsetMember(c,numbers_offset,FcChar16)
 
 struct _FcStrSet {
-    int		    ref;	/* reference count */
+    FcRef	    ref;	/* reference count */
     int		    num;
     int		    size;
     FcChar8	    **strs;
@@ -497,7 +500,7 @@ struct _FcConfig {
     time_t	rescanTime;	    /* last time information was scanned */
     int		rescanInterval;	    /* interval between scans */
 
-    int		ref;                /* reference count */
+    FcRef	ref;                /* reference count */
 
     FcExprPage *expr_pool;	    /* pool of FcExpr's */
 };
@@ -525,6 +528,17 @@ struct _FcStatFS {
     FcBool is_mtime_broken;
 };
 
+typedef struct _FcValuePromotionBuffer FcValuePromotionBuffer;
+
+struct _FcValuePromotionBuffer {
+  union {
+    double d;
+    int i;
+    long l;
+    char c[256]; /* Enlarge as needed */
+  } u;
+};
+
 /* fcblanks.c */
 
 /* fccache.c */
@@ -798,7 +812,7 @@ FcPrivate FcLangResult
 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2);
 
 FcPrivate FcLangSet *
-FcLangSetPromote (const FcChar8 *lang);
+FcLangSetPromote (const FcChar8 *lang, FcValuePromotionBuffer *buf);
 
 FcPrivate FcLangSet *
 FcNameParseLangSet (const FcChar8 *string);
@@ -952,7 +966,7 @@ FcPrivate const FcChar8 *
 FcSharedStr (const FcChar8 *name);
 
 FcPrivate FcBool
-FcSharedStrFree (const FcChar8 *name);
+FcSharedStrFree (FcChar8 *name);
 
 FcPrivate FcChar32
 FcStringHash (const FcChar8 *s);
diff --git a/src/fclang.c b/src/fclang.c
index d1fadf5..8e9b094 100644
--- a/src/fclang.c
+++ b/src/fclang.c
@@ -22,10 +22,11 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <string.h>
 #include "fcint.h"
 #include "fcftint.h"
 
+/* Objects MT-safe for readonly access. */
+
 typedef struct {
     const FcChar8    	lang[8];
     const FcCharSet	charset;
@@ -702,34 +703,38 @@ FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
 
 /*
  * Used in computing values -- mustn't allocate any storage
- * XXX Not thread-safe
  */
 FcLangSet *
-FcLangSetPromote (const FcChar8 *lang)
+FcLangSetPromote (const FcChar8 *lang, FcValuePromotionBuffer *vbuf)
 {
-    static FcLangSet	ls;
-    static FcStrSet	strs;
-    static FcChar8	*str;
-    int			id;
-
-    memset (ls.map, '\0', sizeof (ls.map));
-    ls.map_size = NUM_LANG_SET_MAP;
-    ls.extra = 0;
+    int		id;
+    typedef struct {
+	FcLangSet  ls;
+	FcStrSet   strs;
+	FcChar8   *str;
+    } FcLangSetPromotionBuffer;
+    FcLangSetPromotionBuffer *buf = (FcLangSetPromotionBuffer *) vbuf;
+
+    FC_ASSERT_STATIC (sizeof (FcLangSetPromotionBuffer) <= sizeof (FcValuePromotionBuffer));
+
+    memset (buf->ls.map, '\0', sizeof (buf->ls.map));
+    buf->ls.map_size = NUM_LANG_SET_MAP;
+    buf->ls.extra = 0;
     id = FcLangSetIndex (lang);
     if (id > 0)
     {
-	FcLangSetBitSet (&ls, id);
+	FcLangSetBitSet (&buf->ls, id);
     }
     else
     {
-	ls.extra = &strs;
-	strs.num = 1;
-	strs.size = 1;
-	strs.strs = &str;
-	strs.ref = 1;
-	str = (FcChar8 *) lang;
+	buf->ls.extra = &buf->strs;
+	buf->strs.num = 1;
+	buf->strs.size = 1;
+	buf->strs.strs = &buf->str;
+	FcRefInit (&buf->strs.ref, 1);
+	buf->str = (FcChar8 *) lang;
     }
-    return &ls;
+    return &buf->ls;
 }
 
 FcChar32
diff --git a/src/fcmatch.c b/src/fcmatch.c
index ed245c6..6778967 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -23,10 +23,6 @@
  */
 
 #include "fcint.h"
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdio.h>
 
 static double
 FcCompareNumber (FcValue *value1, FcValue *value2)
diff --git a/src/fcmutex.h b/src/fcmutex.h
index dd4487d..b10572c 100644
--- a/src/fcmutex.h
+++ b/src/fcmutex.h
@@ -36,6 +36,8 @@
 #include <config.h>
 #endif
 
+#define FC_STMT_START do
+#define FC_STMT_END while (0)
 
 /* mutex */
 
diff --git a/src/fcpat.c b/src/fcpat.c
index 9d95266..c3b90d6 100644
--- a/src/fcpat.c
+++ b/src/fcpat.c
@@ -22,9 +22,8 @@
 
 #include "fcint.h"
 #include "fcftint.h"
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
+
+/* Objects MT-safe for readonly access. */
 
 FcPattern *
 FcPatternCreate (void)
@@ -37,7 +36,7 @@ FcPatternCreate (void)
     p->num = 0;
     p->size = 0;
     p->elts_offset = FcPtrToOffset (p, NULL);
-    p->ref = 1;
+    FcRefInit (&p->ref, 1);
     return p;
 }
 
@@ -362,13 +361,13 @@ FcPatternDestroy (FcPattern *p)
     if (!p)
 	return;
 
-    if (p->ref == FC_REF_CONSTANT)
+    if (FcRefIsConst (&p->ref))
     {
 	FcCacheObjectDereference (p);
 	return;
     }
 	
-    if (--p->ref > 0)
+    if (FcRefDec (&p->ref) != 1)
 	return;
 
     elts = FcPatternElts (p);
@@ -546,7 +545,7 @@ FcPatternObjectListAdd (FcPattern	*p,
     FcPatternElt   *e;
     FcValueListPtr l, *prev;
 
-    if (p->ref == FC_REF_CONSTANT)
+    if (FcRefIsConst (&p->ref))
 	goto bail0;
 
     /*
@@ -598,7 +597,7 @@ FcPatternObjectAddWithBinding  (FcPattern	*p,
     FcPatternElt   *e;
     FcValueListPtr new, *prev;
 
-    if (p->ref == FC_REF_CONSTANT)
+    if (FcRefIsConst (&p->ref))
 	goto bail0;
 
     new = FcValueListCreate ();
@@ -1060,8 +1059,8 @@ bail0:
 void
 FcPatternReference (FcPattern *p)
 {
-    if (p->ref != FC_REF_CONSTANT)
-	p->ref++;
+    if (!FcRefIsConst (&p->ref))
+	FcRefInc (&p->ref);
     else
 	FcCacheObjectReference (p);
 }
@@ -1153,7 +1152,7 @@ bail0:
  * significant by any means. */
 
 FcBool
-FcSharedStrFree (const FcChar8 *name)
+FcSharedStrFree (FcChar8 *name)
 {
   free (name);
   return FcTrue;
@@ -1162,7 +1161,7 @@ FcSharedStrFree (const FcChar8 *name)
 const FcChar8 *
 FcSharedStr (const FcChar8 *name)
 {
-  return strdup (name);
+  return strdup ((const char *) name);
 }
 
 FcBool
@@ -1195,7 +1194,7 @@ FcPatternSerialize (FcSerialize *serialize, const FcPattern *pat)
 	return NULL;
     *pat_serialized = *pat;
     pat_serialized->size = pat->num;
-    pat_serialized->ref = FC_REF_CONSTANT;
+    FcRefSetConst (&pat_serialized->ref);
 
     elts_serialized = FcSerializePtr (serialize, elts);
     if (!elts_serialized)
diff --git a/src/fcstr.c b/src/fcstr.c
index 553588f..e72be5e 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -33,6 +33,8 @@
 #include <windows.h>
 #endif
 
+/* Objects MT-safe for readonly access. */
+
 FcChar8 *
 FcStrCopy (const FcChar8 *s)
 {
@@ -1078,7 +1080,7 @@ FcStrSetCreate (void)
     FcStrSet	*set = malloc (sizeof (FcStrSet));
     if (!set)
 	return 0;
-    set->ref = 1;
+    FcRefInit (&set->ref, 1);
     set->num = 0;
     set->size = 0;
     set->strs = 0;
@@ -1230,16 +1232,16 @@ FcStrSetDel (FcStrSet *set, const FcChar8 *s)
 void
 FcStrSetDestroy (FcStrSet *set)
 {
-    if (--set->ref == 0)
-    {
-	int	i;
+    int	i;
 
-	for (i = 0; i < set->num; i++)
-	    FcStrFree (set->strs[i]);
-	if (set->strs)
-	    free (set->strs);
-	free (set);
-    }
+    if (FcRefDec (&set->ref) != 1)
+	return;
+
+    for (i = 0; i < set->num; i++)
+	FcStrFree (set->strs[i]);
+    if (set->strs)
+	free (set->strs);
+    free (set);
 }
 
 FcStrList *
@@ -1251,7 +1253,7 @@ FcStrListCreate (FcStrSet *set)
     if (!list)
 	return 0;
     list->set = set;
-    set->ref++;
+    FcRefInc (&set->ref);
     list->n = 0;
     return list;
 }
commit 814871b2aaa3a22ef711ca4656507fb69c952156
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 14:24:28 2012 -0400

    Add thread-safety primitives

diff --git a/COPYING b/COPYING
index 2a5d777..66392b1 100644
--- a/COPYING
+++ b/COPYING
@@ -5,6 +5,7 @@ Copyright © 2005 Patrick Lam
 Copyright © 2009 Roozbeh Pournader
 Copyright © 2008,2009 Red Hat, Inc.
 Copyright © 2008 Danilo Šegan
+Copyright © 2012 Google, Inc.
 
 
 Permission to use, copy, modify, distribute, and sell this software and its
diff --git a/src/Makefile.am b/src/Makefile.am
index b700f5a..da1358c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -123,6 +123,7 @@ EXTRA_DIST += \
 libfontconfig_la_SOURCES = \
 	fcarch.h \
 	fcatomic.c \
+	fcatomic.h \
 	fcblanks.c \
 	fccache.c \
 	fccfg.c \
@@ -138,6 +139,7 @@ libfontconfig_la_SOURCES = \
 	fclist.c \
 	fcmatch.c \
 	fcmatrix.c \
+	fcmutex.h \
 	fcname.c \
 	fcobjs.c \
 	fcobjs.h \
diff --git a/src/fcatomic.h b/src/fcatomic.h
new file mode 100644
index 0000000..7479a3d
--- /dev/null
+++ b/src/fcatomic.h
@@ -0,0 +1,123 @@
+/*
+ * Mutex operations.  Originally copied from HarfBuzz.
+ *
+ * Copyright © 2007  Chris Wilson
+ * Copyright © 2009,2010  Red Hat, Inc.
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef _FCATOMIC_H_
+#define _FCATOMIC_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+/* atomic_int */
+
+/* We need external help for these */
+
+#if 0
+
+
+#elif !defined(FC_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* mingw32 does not have MemoryBarrier.
+ * MemoryBarrier may be defined as a macro or a function.
+ * Just make a failsafe version for ourselves. */
+#ifdef MemoryBarrier
+#define HBMemoryBarrier MemoryBarrier
+#else
+static inline void HBMemoryBarrier (void) {
+  long dummy = 0;
+  InterlockedExchange (&dummy, 1);
+}
+#endif
+
+typedef long fc_atomic_int_t;
+#define fc_atomic_int_add(AI, V)	InterlockedExchangeAdd (&(AI), (V))
+
+#define fc_atomic_ptr_get(P)		(HBMemoryBarrier (), (void *) *(P))
+#define fc_atomic_ptr_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
+
+
+#elif !defined(FC_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+typedef int32_t fc_atomic_int_t;
+#define fc_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+
+#define fc_atomic_ptr_get(P)		(OSMemoryBarrier (), (void *) *(P))
+#define fc_atomic_ptr_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+
+
+#elif !defined(FC_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+typedef int fc_atomic_int_t;
+#define fc_atomic_int_add(AI, V)	__sync_fetch_and_add (&(AI), (V))
+
+#define fc_atomic_ptr_get(P)		(void *) (__sync_synchronize (), *(P))
+#define fc_atomic_ptr_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
+
+
+#elif !defined(FC_NO_MT)
+
+#define FC_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
+typedef volatile int fc_atomic_int_t;
+#define fc_atomic_int_add(AI, V)	(((AI) += (V)) - (V))
+
+#define fc_atomic_ptr_get(P)		((void *) *(P))
+#define fc_atomic_ptr_cmpexch(P,O,N)	(* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
+
+
+#else /* FC_NO_MT */
+
+typedef int fc_atomic_int_t;
+#define fc_atomic_int_add(AI, V)	(((AI) += (V)) - (V))
+
+#define fc_atomic_ptr_get(P)		((void *) *(P))
+#define fc_atomic_ptr_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
+
+#endif
+
+/* reference count */
+#define FC_REF_CONSTANT ((fc_atomic_int_t) -1)
+#define FC_REF_CONSTANT_INIT {FC_REF_CONSTANT}
+typedef struct _FcRef { fc_atomic_int_t count; } FcRef;
+static inline void   FcRefInit    (FcRef *r, int v) { r->count = v; }
+static inline int    FcRefInc     (FcRef *r) { return fc_atomic_int_add (r->count, +1); }
+static inline int    FcRefDec     (FcRef *r) { return fc_atomic_int_add (r->count, -1); }
+static inline void   FcRefFinish  (FcRef *r) { r->count = FC_REF_CONSTANT; }
+static inline FcBool FcRefIsConst (FcRef *r) { return r->count == FC_REF_CONSTANT; }
+
+#endif /* _FCATOMIC_H_ */
diff --git a/src/fcint.h b/src/fcint.h
index a6d7e8a..d881a77 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -44,6 +44,8 @@
 #include <fontconfig/fontconfig.h>
 #include <fontconfig/fcprivate.h>
 #include "fcdeprecate.h"
+#include "fcmutex.h"
+#include "fcatomic.h"
 
 #ifndef FC_CONFIG_PATH
 #define FC_CONFIG_PATH "fonts.conf"
@@ -293,8 +295,6 @@ typedef struct _FcCharLeaf {
     FcChar32	map[256/32];
 } FcCharLeaf;
 
-#define FC_REF_CONSTANT	    -1
-
 struct _FcCharSet {
     int		    ref;	/* reference count */
     int		    num;	/* size of leaves and numbers arrays */
diff --git a/src/fcmutex.h b/src/fcmutex.h
new file mode 100644
index 0000000..dd4487d
--- /dev/null
+++ b/src/fcmutex.h
@@ -0,0 +1,126 @@
+/*
+ * Atomic int and pointer operations.  Originally copied from HarfBuzz.
+ *
+ * Copyright © 2007  Chris Wilson
+ * Copyright © 2009,2010  Red Hat, Inc.
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef _FCMUTEX_H_
+#define _FCMUTEX_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+/* mutex */
+
+/* We need external help for these */
+
+#if 0
+
+
+#elif !defined(FC_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+typedef CRITICAL_SECTION fc_mutex_impl_t;
+#define FC_MUTEX_IMPL_INIT	{ NULL, 0, 0, NULL, NULL, 0 }
+#define fc_mutex_impl_init(M)	InitializeCriticalSection (M)
+#define fc_mutex_impl_lock(M)	EnterCriticalSection (M)
+#define fc_mutex_impl_unlock(M)	LeaveCriticalSection (M)
+#define fc_mutex_impl_finish(M)	DeleteCriticalSection (M)
+
+
+#elif !defined(FC_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__))
+
+#include <pthread.h>
+typedef pthread_mutex_t fc_mutex_impl_t;
+#define FC_MUTEX_IMPL_INIT	PTHREAD_MUTEX_INITIALIZER
+#define fc_mutex_impl_init(M)	pthread_mutex_init (M, NULL)
+#define fc_mutex_impl_lock(M)	pthread_mutex_lock (M)
+#define fc_mutex_impl_unlock(M)	pthread_mutex_unlock (M)
+#define fc_mutex_impl_finish(M)	pthread_mutex_destroy (M)
+
+
+#elif !defined(FC_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define FC_SCHED_YIELD() sched_yield ()
+#else
+# define FC_SCHED_YIELD() FC_STMT_START {} FC_STMT_END
+#endif
+
+/* This actually is not a totally awful implementation. */
+typedef volatile int fc_mutex_impl_t;
+#define FC_MUTEX_IMPL_INIT	0
+#define fc_mutex_impl_init(M)	*(M) = 0
+#define fc_mutex_impl_lock(M)	FC_STMT_START { while (__sync_lock_test_and_set((M), 1)) FC_SCHED_YIELD (); } FC_STMT_END
+#define fc_mutex_impl_unlock(M)	__sync_lock_release (M)
+#define fc_mutex_impl_finish(M)	FC_STMT_START {} FC_STMT_END
+
+
+#elif !defined(FC_NO_MT)
+
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define FC_SCHED_YIELD() sched_yield ()
+#else
+# define FC_SCHED_YIELD() FC_STMT_START {} FC_STMT_END
+#endif
+
+#define FC_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */
+typedef volatile int fc_mutex_impl_t;
+#define FC_MUTEX_IMPL_INIT	0
+#define fc_mutex_impl_init(M)	*(M) = 0
+#define fc_mutex_impl_lock(M)	FC_STMT_START { while (*(M)) FC_SCHED_YIELD (); (*(M))++; } FC_STMT_END
+#define fc_mutex_impl_unlock(M)	(*(M))--;
+#define fc_mutex_impl_finish(M)	FC_STMT_START {} FC_STMT_END
+
+
+#else /* FC_NO_MT */
+
+typedef int fc_mutex_impl_t;
+#define FC_MUTEX_IMPL_INIT	0
+#define fc_mutex_impl_init(M)	FC_STMT_START {} FC_STMT_END
+#define fc_mutex_impl_lock(M)	FC_STMT_START {} FC_STMT_END
+#define fc_mutex_impl_unlock(M)	FC_STMT_START {} FC_STMT_END
+#define fc_mutex_impl_finish(M)	FC_STMT_START {} FC_STMT_END
+
+#endif
+
+
+#define FC_MUTEX_INIT		{FC_MUTEX_IMPL_INIT}
+typedef fc_mutex_impl_t FcMutex;
+static inline void FcMutexInit   (FcMutex *m) { fc_mutex_impl_init (m);   }
+static inline void FcMutexLock   (FcMutex *m) { fc_mutex_impl_lock (m);   }
+static inline void FcMutexUnlock (FcMutex *m) { fc_mutex_impl_unlock (m); }
+static inline void FcMutexFinish (FcMutex *m) { fc_mutex_impl_finish (m); }
+
+
+#endif /* _FCMUTEX_H_ */
commit f6d8306e566dd1a4b8a13f433d2bc1ffbe667db7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 7 13:49:45 2012 -0400

    Add build stuff for threadsafety primitives
    
    Copied over from HarfBuzz.

diff --git a/configure.ac b/configure.ac
index 5657bb5..fda5650 100644
--- a/configure.ac
+++ b/configure.ac
@@ -151,7 +151,7 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([[
 	}
 	]])],[
 		AC_MSG_RESULT([yes])
-		AC_DEFINE([HAVE_POSIX_FADVISE], [1], [Define to 1 if you have the `posix_fadvise' function.])
+		AC_DEFINE([HAVE_POSIX_FADVISE], [1], [Define to 1 if you have the 'posix_fadvise' function.])
 	],[AC_MSG_RESULT([no])])
 CFLAGS="$fc_saved_CFLAGS"
 
@@ -567,6 +567,42 @@ AC_SUBST(CONFIGDIR)
 AC_SUBST(xmldir)
 AC_SUBST(XMLDIR)
 
+
+dnl ===========================================================================
+
+#
+# Thread-safety primitives
+#
+
+AC_CACHE_CHECK([for Intel atomic primitives], fc_cv_have_intel_atomic_primitives, [
+	fc_cv_have_intel_atomic_primitives=false
+	AC_TRY_LINK([
+		void memory_barrier (void) { __sync_synchronize (); }
+		int atomic_add (int *i) { return __sync_fetch_and_add (i, 1); }
+		int mutex_trylock (int *m) { return __sync_lock_test_and_set (m, 1); }
+		void mutex_unlock (int *m) { __sync_lock_release (m); }
+		], [], fc_cv_have_intel_atomic_primitives=true
+	)
+])
+if $fc_cv_have_intel_atomic_primitives; then
+	AC_DEFINE(HAVE_INTEL_ATOMIC_PRIMITIVES, 1, [Have Intel __sync_* atomic primitives])
+fi
+
+AC_CHECK_HEADERS(sched.h)
+AC_CHECK_FUNCS(sched_yield)
+
+have_pthread=false
+if test "$os_win32" = no; then
+	AX_PTHREAD([have_pthread=true])
+fi
+if $have_pthread; then
+	AC_DEFINE(HAVE_PTHREAD, 1, [Have POSIX threads])
+fi
+AM_CONDITIONAL(HAVE_PTHREAD, $have_pthread)
+
+
+dnl ===========================================================================
+
 #
 # Let people not build/install docs if they don't have docbook
 #
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
new file mode 100644
index 0000000..d90de34
--- /dev/null
+++ b/m4/ax_pthread.m4
@@ -0,0 +1,309 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj at alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk at iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 18
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            ax_cv_PTHREAD_PRIO_INHERIT, [
+                AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+        if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+        fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
commit b53744383dbefb3f80fb8a7365487669a499ad76
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Oct 6 18:15:58 2012 -0400

    Fix build stuff

diff --git a/src/Makefile.am b/src/Makefile.am
index e6e5f98..b700f5a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,7 +91,8 @@ BUILT_SOURCES = $(ALIAS_FILES) \
 	../fc-glyphname/fcglyphname.h \
 	../fc-lang/fclang.h \
 	stamp-fcstdint \
-	fcobjshash.h
+	fcobjshash.h \
+	fcobjshash.gperf
 
 noinst_PROGRAMS = fcarch
 
@@ -116,7 +117,8 @@ fcobjshash.h: fcobjshash.gperf
 
 EXTRA_DIST += \
 	fcobjshash.gperf.h \
-	fcobjshash.gperf
+	fcobjshash.gperf \
+	fcobjshash.h
 
 libfontconfig_la_SOURCES = \
 	fcarch.h \
@@ -186,7 +188,7 @@ stamp-fcstdint: $(top_builddir)/config.status
 	$(SHELL) ./config.status src/fcstdint.h
 	@touch $@
 
-CLEANFILES = $(ALIAS_FILES)
+CLEANFILES = $(ALIAS_FILES) fontconfig.def
 DISTCLEANFILES = stamp-fcstdint fcstdint.h
 
 fontconfig.def: $(PUBLIC_FILES) $(PUBLIC_FT_FILES)
commit db6d86a6c1b5bb15511e4e4015af889d4206be1d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Oct 6 18:12:19 2012 -0400

    Remove shared-str pool
    
    We used to have a shared-str pool.  Removed to make thread-safety
    work easier.  My measurements show that the extra overhead is not
    significant by any means.

diff --git a/src/fcpat.c b/src/fcpat.c
index d93eb73..9d95266 100644
--- a/src/fcpat.c
+++ b/src/fcpat.c
@@ -1147,66 +1147,22 @@ bail0:
     return NULL;
 }
 
-#define OBJECT_HASH_SIZE    251
-static struct objectBucket {
-    struct objectBucket	*next;
-    FcChar32		hash;
-    int			ref_count;
-} *FcObjectBuckets[OBJECT_HASH_SIZE];
+
+/* We used to have a shared-str pool.  Removed to make thread-safety
+ * work easier.  My measurements show that the extra overhead is not
+ * significant by any means. */
 
 FcBool
 FcSharedStrFree (const FcChar8 *name)
 {
-    FcChar32		hash = FcStringHash (name);
-    struct objectBucket	**p;
-    struct objectBucket	*b;
-    int			size;
-
-    for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
-	if (b->hash == hash && ((char *)name == (char *) (b + 1)))
-	{
-	    b->ref_count--;
-	    if (!b->ref_count)
-	    {
-		*p = b->next;
-		size = sizeof (struct objectBucket) + strlen ((char *)name) + 1;
-		size = (size + 3) & ~3;
-		free (b);
-	    }
-            return FcTrue;
-	}
-    return FcFalse;
+  free (name);
+  return FcTrue;
 }
 
 const FcChar8 *
 FcSharedStr (const FcChar8 *name)
 {
-    FcChar32		hash = FcStringHash (name);
-    struct objectBucket	**p;
-    struct objectBucket	*b;
-    int			size;
-
-    for (p = &FcObjectBuckets[hash % OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
-	if (b->hash == hash && !strcmp ((char *)name, (char *) (b + 1)))
-	{
-	    b->ref_count++;
-	    return (FcChar8 *) (b + 1);
-	}
-    size = sizeof (struct objectBucket) + strlen ((char *)name) + 1;
-    /*
-     * workaround valgrind warning because glibc takes advantage of how it knows memory is
-     * allocated to implement strlen by reading in groups of 4
-     */
-    size = (size + 3) & ~3;
-    b = malloc (size);
-    if (!b)
-        return NULL;
-    b->next = 0;
-    b->hash = hash;
-    b->ref_count = 1;
-    strcpy ((char *) (b + 1), (char *)name);
-    *p = b;
-    return (FcChar8 *) (b + 1);
+  return strdup (name);
 }
 
 FcBool
commit ed41b237658ba290d86795904701ace09b46f6c4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sat Oct 6 17:52:39 2012 -0400

    Switch .gitignore to git.mk

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 32b9e43..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,100 +0,0 @@
-tags
-TAGS
-*.exe
-*.tar.gz
-.libs
-.deps
-*.o
-*.lo
-*.la
-*~
-*.orig
-*.rej
-ChangeLog
-ChangeLog-*
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache
-config.guess
-config.h
-config.h.in
-config.log
-config.status
-config.sub
-configure
-depcomp
-fontconfig-zip
-fontconfig.pc
-fontconfig.spec
-fonts.conf
-install-sh
-doltcompile
-libtool
-ltmain.sh
-missing
-stamp-h1
-manpage.log
-doc/*.3
-doc/confdir.sgml
-doc/edit-sgml
-doc/fcatomic.sgml
-doc/fcblanks.sgml
-doc/fccache.sgml
-doc/fccharset.sgml
-doc/fcconfig.sgml
-doc/fcconstant.sgml
-doc/fcdircache.sgml
-doc/fcfile.sgml
-doc/fcformat.sgml
-doc/fcfontset.sgml
-doc/fcfreetype.sgml
-doc/fclangset.sgml
-doc/fcinit.sgml
-doc/fcmatrix.sgml
-doc/fcobjectset.sgml
-doc/fcobjecttype.sgml
-doc/fcpattern.sgml
-doc/fcstring.sgml
-doc/fcstrset.sgml
-doc/fcvalue.sgml
-doc/fontconfig-devel.pdf
-doc/fontconfig-devel.txt
-doc/fontconfig-devel
-doc/fontconfig-user.html
-doc/fontconfig-user.pdf
-doc/fontconfig-user.txt
-doc/fonts-conf.5
-doc/func.refs
-doc/local-fontconfig-devel.sgml
-doc/local-fontconfig-user.sgml
-doc/version.sgml
-doc/devel-man
-fc-arch/fc-arch
-fc-arch/fcarch.h
-fc-cache/fc-cache
-fc-cache/fc-cache.1
-fc-case/fc-case
-fc-case/fccase.h
-fc-cat/fc-cat
-fc-cat/fc-cat.1
-fc-glyphname/fc-glyphname
-fc-glyphname/fcglyphname.h
-fc-lang/fc-lang
-fc-lang/fclang.h
-fc-list/fc-list
-fc-list/fc-list.1
-fc-match/fc-match
-fc-match/fc-match.1
-fc-pattern/fc-pattern
-fc-pattern/fc-pattern.1
-fc-query/fc-query
-fc-query/fc-query.1
-fc-scan/fc-scan
-fc-scan/fc-scan.1
-src/fcarch
-src/fontconfig.def
-fcalias.h
-fcaliastail.h
-src/fcftalias.h
-src/fcftaliastail.h
diff --git a/Makefile.am b/Makefile.am
index f4203d6..cbc3df0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,3 +148,5 @@ debuild-dirs: distdir
 	$(RM) -r $(distdir).orig
 	cp -a $(distdir) $(distdir).orig
 	$(RM) -r $(distdir).orig/debian
+
+-include $(top_srcdir)/git.mk
diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am
index 0f6f59c..e3a26f0 100644
--- a/conf.d/Makefile.am
+++ b/conf.d/Makefile.am
@@ -92,3 +92,5 @@ uninstall-local:
 	    echo $(RM) $$i;					\
 	    $(RM) $$i;						\
 	  done)
+
+-include $(top_srcdir)/git.mk
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 80674df..ad68c09 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -208,3 +208,5 @@ htmldoc_DATA += $(srcdir)/$(HTML_DIR)/*
 all-local:
 clean-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-cache/Makefile.am b/fc-cache/Makefile.am
index 61f5019..7f7d1b1 100644
--- a/fc-cache/Makefile.am
+++ b/fc-cache/Makefile.am
@@ -65,3 +65,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-case/Makefile.am b/fc-case/Makefile.am
index 6fe3a44..9bab1c7 100644
--- a/fc-case/Makefile.am
+++ b/fc-case/Makefile.am
@@ -66,3 +66,5 @@ CLEANFILES = $(ALIAS_FILES)
 DISTCLEANFILES = $(TARG)
 
 MAINTAINERCLEANFILES = $(TARG)
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-cat/Makefile.am b/fc-cat/Makefile.am
index 7b52d49..b426723 100644
--- a/fc-cat/Makefile.am
+++ b/fc-cat/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-glyphname/Makefile.am b/fc-glyphname/Makefile.am
index e33b3ba..f6aeea7 100644
--- a/fc-glyphname/Makefile.am
+++ b/fc-glyphname/Makefile.am
@@ -61,3 +61,5 @@ CLEANFILES = $(ALIAS_FILES)
 DISTCLEANFILES = $(TARG)
 
 MAINTAINERCLEANFILES = $(TARG)
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-lang/Makefile.am b/fc-lang/Makefile.am
index 3336511..35e94e5 100644
--- a/fc-lang/Makefile.am
+++ b/fc-lang/Makefile.am
@@ -312,3 +312,5 @@ ORTH = \
 	doi.orth \
 	mni.orth
 #	^-------------- Add new orth files here
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-list/Makefile.am b/fc-list/Makefile.am
index 8b13568..b2c499d 100644
--- a/fc-list/Makefile.am
+++ b/fc-list/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-match/Makefile.am b/fc-match/Makefile.am
index 4066c9e..0e9e8fd 100644
--- a/fc-match/Makefile.am
+++ b/fc-match/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-pattern/Makefile.am b/fc-pattern/Makefile.am
index c97db12..92b0433 100644
--- a/fc-pattern/Makefile.am
+++ b/fc-pattern/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-query/Makefile.am b/fc-query/Makefile.am
index 4271252..b3ea1e6 100644
--- a/fc-query/Makefile.am
+++ b/fc-query/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fc-scan/Makefile.am b/fc-scan/Makefile.am
index 22136b1..2063405 100644
--- a/fc-scan/Makefile.am
+++ b/fc-scan/Makefile.am
@@ -56,3 +56,5 @@ CLEANFILES += ${man_MANS}
 else
 all-local:
 endif
+
+-include $(top_srcdir)/git.mk
diff --git a/fontconfig/Makefile.am b/fontconfig/Makefile.am
index 72fec33..99bb4c3 100644
--- a/fontconfig/Makefile.am
+++ b/fontconfig/Makefile.am
@@ -6,3 +6,5 @@ fontconfig_headers=		\
 	fcprivate.h
 
 fontconfiginclude_HEADERS = $(fontconfig_headers)
+
+-include $(top_srcdir)/git.mk
diff --git a/git.mk b/git.mk
new file mode 100644
index 0000000..d5bf7b8
--- /dev/null
+++ b/git.mk
@@ -0,0 +1,227 @@
+# git.mk
+#
+# Copyright 2009, Red Hat, Inc.
+# Copyright 2010,2011 Behdad Esfahbod
+# Written by Behdad Esfahbod
+#
+# Copying and distribution of this file, with or without modification,
+# is permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+#
+# The latest version of this file can be downloaded from:
+#   https://raw.github.com/behdad/git.mk/master/git.mk
+# Bugs, etc, should be reported upstream at:
+#   https://github.com/behdad/git.mk
+#
+# To use in your project, import this file in your git repo's toplevel,
+# then do "make -f git.mk".  This modifies all Makefile.am files in
+# your project to -include git.mk.  Remember to add that line to new
+# Makefile.am files you create in your project, or just rerun the
+# "make -f git.mk".
+#
+# This enables automatic .gitignore generation.  If you need to ignore
+# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
+# But think twice before doing that.  If a file has to be in .gitignore,
+# chances are very high that it's a generated file and should be in one
+# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
+#
+# The only case that you need to manually add a file to GITIGNOREFILES is
+# when remove files in one of mostlyclean-local, clean-local, distclean-local,
+# or maintainer-clean-local make targets.
+#
+# Note that for files like editor backup, etc, there are better places to
+# ignore them.  See "man gitignore".
+#
+# If "make maintainer-clean" removes the files but they are not recognized
+# by this script (that is, if "git status" shows untracked files still), send
+# me the output of "git status" as well as your Makefile.am and Makefile for
+# the directories involved and I'll diagnose.
+#
+# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
+# Makefile.am.sample in the git.mk git repo.
+#
+# Don't EXTRA_DIST this file.  It is supposed to only live in git clones,
+# not tarballs.  It serves no useful purpose in tarballs and clutters the
+# build dir.
+#
+# This file knows how to handle autoconf, automake, libtool, gtk-doc,
+# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu.
+#
+# This makefile provides the following targets:
+#
+# - all: "make all" will build all gitignore files.
+# - gitignore: makes all gitignore files in the current dir and subdirs.
+# - .gitignore: make gitignore file for the current dir.
+# - gitignore-recurse: makes all gitignore files in the subdirs.
+#
+# KNOWN ISSUES:
+#
+# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
+#   submodule doesn't find us.  If you have configure.{in,ac} files in
+#   subdirs, add a proxy git.mk file in those dirs that simply does:
+#   "include $(top_srcdir)/../git.mk".  Add more ..'s to your taste.
+#   And add those files to git.  See vte/gnome-pty-helper/git.mk for
+#   example.
+#
+
+git-all: git-mk-install
+
+git-mk-install:
+	@echo Installing git makefile
+	@any_failed=; \
+		find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \
+		if grep 'include .*/git.mk' $$x >/dev/null; then \
+			echo $$x already includes git.mk; \
+		else \
+			failed=; \
+			echo "Updating $$x"; \
+			{ cat $$x; \
+			  echo ''; \
+			  echo '-include $$(top_srcdir)/git.mk'; \
+			} > $$x.tmp || failed=1; \
+			if test x$$failed = x; then \
+				mv $$x.tmp $$x || failed=1; \
+			fi; \
+			if test x$$failed = x; then : else \
+				echo Failed updating $$x; >&2 \
+				any_failed=1; \
+			fi; \
+	fi; done; test -z "$$any_failed"
+
+.PHONY: git-all git-mk-install
+
+
+### .gitignore generation
+
+$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
+	$(AM_V_GEN) \
+	{ \
+		if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
+			for x in \
+				$(DOC_MODULE)-decl-list.txt \
+				$(DOC_MODULE)-decl.txt \
+				tmpl/$(DOC_MODULE)-unused.sgml \
+				"tmpl/*.bak" \
+				xml html \
+			; do echo /$$x; done; \
+		fi; \
+		if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
+			for lc in $(DOC_LINGUAS); do \
+				for x in \
+					$(if $(DOC_MODULE),$(DOC_MODULE).xml) \
+					$(DOC_PAGES) \
+					$(DOC_INCLUDES) \
+				; do echo /$$lc/$$x; done; \
+			done; \
+			for x in \
+				$(_DOC_OMF_ALL) \
+				$(_DOC_DSK_ALL) \
+				$(_DOC_HTML_ALL) \
+				$(_DOC_MOFILES) \
+				$(DOC_H_FILE) \
+				"*/.xml2po.mo" \
+				"*/*.omf.out" \
+			; do echo /$$x; done; \
+		fi; \
+		if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
+			for lc in $(HELP_LINGUAS); do \
+				for x in \
+					$(HELP_FILES) \
+					"$$lc.stamp" \
+					"$$lc.mo" \
+				; do echo /$$lc/$$x; done; \
+			done; \
+		fi; \
+		if test "x$(gsettings_SCHEMAS)" = x; then :; else \
+			for x in \
+				$(gsettings_SCHEMAS:.xml=.valid) \
+				$(gsettings__enum_file) \
+			; do echo /$$x; done; \
+		fi; \
+		if test -f $(srcdir)/po/Makefile.in.in; then \
+			for x in \
+				po/Makefile.in.in \
+				po/Makefile.in \
+				po/Makefile \
+				po/POTFILES \
+				po/stamp-it \
+				po/.intltool-merge-cache \
+				"po/*.gmo" \
+				"po/*.mo" \
+				po/$(GETTEXT_PACKAGE).pot \
+				intltool-extract.in \
+				intltool-merge.in \
+				intltool-update.in \
+			; do echo /$$x; done; \
+		fi; \
+		if test -f $(srcdir)/configure; then \
+			for x in \
+				autom4te.cache \
+				configure \
+				config.h \
+				stamp-h1 \
+				libtool \
+				config.lt \
+			; do echo /$$x; done; \
+		fi; \
+		if test "x$(DEJATOOL)" = x; then :; else \
+			for x in \
+				$(DEJATOOL) \
+			; do echo /$$x.sum; echo /$$x.log; done; \
+			echo /site.exp; \
+		fi; \
+		for x in \
+			.gitignore \
+			$(GITIGNOREFILES) \
+			$(CLEANFILES) \
+			$(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \
+			$(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \
+			$(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \
+			so_locations \
+			.libs _libs \
+			$(MOSTLYCLEANFILES) \
+			"*.$(OBJEXT)" \
+			"*.lo" \
+			$(DISTCLEANFILES) \
+			$(am__CONFIG_DISTCLEAN_FILES) \
+			$(CONFIG_CLEAN_FILES) \
+			TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
+			"*.tab.c" \
+			$(MAINTAINERCLEANFILES) \
+			$(BUILT_SOURCES) \
+			$(DEPDIR) \
+			Makefile \
+			Makefile.in \
+			"*.orig" \
+			"*.rej" \
+			"*.bak" \
+			"*~" \
+			".*.sw[nop]" \
+			".dirstamp" \
+		; do echo /$$x; done; \
+	} | \
+	sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
+	sed 's@/[.]/@/@g' | \
+	LC_ALL=C sort | uniq > $@.tmp && \
+	mv $@.tmp $@;
+
+all: $(srcdir)/.gitignore gitignore-recurse-maybe
+gitignore: $(srcdir)/.gitignore gitignore-recurse
+
+gitignore-recurse-maybe:
+	@for subdir in $(DIST_SUBDIRS); do \
+	  case " $(SUBDIRS) " in \
+	    *" $$subdir "*) :;; \
+	    *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse-maybe || echo "Skipping $$subdir");; \
+	  esac; \
+	done
+gitignore-recurse:
+	@for subdir in $(DIST_SUBDIRS); do \
+	    test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir"); \
+	done
+
+maintainer-clean: gitignore-clean
+gitignore-clean:
+	-rm -f $(srcdir)/.gitignore
+
+.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
diff --git a/src/Makefile.am b/src/Makefile.am
index 57c34a2..e6e5f98 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -198,3 +198,5 @@ fontconfig.def: $(PUBLIC_FILES) $(PUBLIC_FT_FILES)
 	echo LIBRARY libfontconfig- at LIBT_CURRENT_MINUS_AGE@.dll; \
 	echo VERSION @LIBT_CURRENT at .@LIBT_REVISION@) >$@
 	@ ! grep -q FcERROR $@ || ($(RM) $@; false)
+
+-include $(top_srcdir)/git.mk
diff --git a/test/Makefile.am b/test/Makefile.am
index 4adcb17..3fd343a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,3 +7,5 @@ TESTDATA=4x6.pcf 8x16.pcf out.expected fonts.conf.in
 EXTRA_DIST=$(check_SCRIPTS) $(TESTDATA)
 
 CLEANFILES=
+
+-include $(top_srcdir)/git.mk
commit d58c31e6dcfd8c5e6fe3ead4a69216b059558223
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Sep 20 14:42:31 2012 -0400

    Use a static perfect hash table for object-name lookup
    
    The hash table is generated by gperf.  For runtime element types, we use
    a append-only linked list.
    
    A bit clumsy, but I think I got it right.

diff --git a/src/Makefile.am b/src/Makefile.am
index dc082b7..57c34a2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,6 +21,8 @@
 # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
+EXTRA_DIST =
+
 if OS_WIN32
 
 export_symbols = -export-symbols fontconfig.def
@@ -78,7 +80,7 @@ INCLUDES = 						\
 	-DFC_CACHEDIR='"$(FC_CACHEDIR)"'                \
 	-DFONTCONFIG_PATH='"$(BASECONFIGDIR)"'
 
-EXTRA_DIST = makealias
+EXTRA_DIST += makealias
 
 noinst_HEADERS=fcint.h fcftint.h fcdeprecate.h fcstdint.h
 
@@ -88,7 +90,8 @@ BUILT_SOURCES = $(ALIAS_FILES) \
 	../fc-case/fccase.h \
 	../fc-glyphname/fcglyphname.h \
 	../fc-lang/fclang.h \
-	stamp-fcstdint
+	stamp-fcstdint \
+	fcobjshash.h
 
 noinst_PROGRAMS = fcarch
 
@@ -99,6 +102,22 @@ noinst_PROGRAMS = fcarch
 ../fc-lang/fclang.h:
 	cd ../fc-lang && $(MAKE) $(AM_MAKEFLAGS) fclang.h
 
+fcobjshash.gperf: fcobjshash.gperf.h fcobjs.h
+	$(AM_V_GEN) $(CPP) -I$(top_srcdir) $< | $(GREP) '^[^#]' | awk ' \
+	/CUT_OUT_BEGIN/ { no_write=1; next; }; \
+	/CUT_OUT_END/ { no_write=0; next; }; \
+	{ if (!no_write) print; next; }; \
+	' - > $@.tmp && \
+	mv -f $@.tmp $@
+
+fcobjshash.h: fcobjshash.gperf
+	$(AM_V_GEN) $(top_srcdir)/missing --run gperf -m 100 $< > $@.tmp && \
+	mv -f $@.tmp $@
+
+EXTRA_DIST += \
+	fcobjshash.gperf.h \
+	fcobjshash.gperf
+
 libfontconfig_la_SOURCES = \
 	fcarch.h \
 	fcatomic.c \
@@ -118,6 +137,9 @@ libfontconfig_la_SOURCES = \
 	fcmatch.c \
 	fcmatrix.c \
 	fcname.c \
+	fcobjs.c \
+	fcobjs.h \
+	fcobjshash.h \
 	fcpat.c \
 	fcserialize.c \
 	fcstat.c \
diff --git a/src/fcinit.c b/src/fcinit.c
index 3a3c518..31578ad 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -155,7 +155,6 @@ FcFini (void)
     if (_fcConfig)
 	FcConfigDestroy (_fcConfig);
 
-    FcObjectFini ();
     FcCacheFini ();
 }
 
diff --git a/src/fcint.h b/src/fcint.h
index 76b95ca..a6d7e8a 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -819,54 +819,14 @@ FcListPatternMatchAny (const FcPattern *p,
 
 /* fcname.c */
 
-/*
- * NOTE -- this ordering is part of the cache file format.
- * It must also match the ordering in fcname.c
- */
-
-#define FC_FAMILY_OBJECT	1
-#define FC_FAMILYLANG_OBJECT	2
-#define FC_STYLE_OBJECT		3
-#define FC_STYLELANG_OBJECT	4
-#define FC_FULLNAME_OBJECT	5
-#define FC_FULLNAMELANG_OBJECT	6
-#define FC_SLANT_OBJECT		7
-#define FC_WEIGHT_OBJECT	8
-#define FC_WIDTH_OBJECT		9
-#define FC_SIZE_OBJECT		10
-#define FC_ASPECT_OBJECT	11
-#define FC_PIXEL_SIZE_OBJECT	12
-#define FC_SPACING_OBJECT	13
-#define FC_FOUNDRY_OBJECT	14
-#define FC_ANTIALIAS_OBJECT	15
-#define FC_HINT_STYLE_OBJECT	16
-#define FC_HINTING_OBJECT	17
-#define FC_VERTICAL_LAYOUT_OBJECT	18
-#define FC_AUTOHINT_OBJECT	19
-#define FC_GLOBAL_ADVANCE_OBJECT	20	/* deprecated */
-#define FC_FILE_OBJECT		21
-#define FC_INDEX_OBJECT		22
-#define FC_RASTERIZER_OBJECT	23
-#define FC_OUTLINE_OBJECT	24
-#define FC_SCALABLE_OBJECT	25
-#define FC_DPI_OBJECT		26
-#define FC_RGBA_OBJECT		27
-#define FC_SCALE_OBJECT		28
-#define FC_MINSPACE_OBJECT	29
-#define FC_CHAR_WIDTH_OBJECT	30
-#define FC_CHAR_HEIGHT_OBJECT	31
-#define FC_MATRIX_OBJECT	32
-#define FC_CHARSET_OBJECT	33
-#define FC_LANG_OBJECT		34
-#define FC_FONTVERSION_OBJECT	35
-#define FC_CAPABILITY_OBJECT	36
-#define FC_FONTFORMAT_OBJECT	37
-#define FC_EMBOLDEN_OBJECT	38
-#define FC_EMBEDDED_BITMAP_OBJECT	39
-#define FC_DECORATIVE_OBJECT	40
-#define FC_LCD_FILTER_OBJECT	41
-#define FC_NAMELANG_OBJECT	42
-#define FC_MAX_BASE_OBJECT	FC_NAMELANG_OBJECT
+enum {
+  FC_INVALID_OBJECT = 0,
+#define FC_OBJECT(NAME, Type) FC_##NAME##_OBJECT,
+#include "fcobjs.h"
+#undef FC_OBJECT
+  FC_ONE_AFTER_MAX_BASE_OBJECT
+#define FC_MAX_BASE_OBJECT (FC_ONE_AFTER_MAX_BASE_OBJECT - 1)
+};
 
 FcPrivate FcBool
 FcNameBool (const FcChar8 *v, FcBool *result);
@@ -883,12 +843,6 @@ FcObjectName (FcObject object);
 FcPrivate FcObjectSet *
 FcObjectGetSet (void);
 
-FcPrivate FcBool
-FcObjectInit (void);
-
-FcPrivate void
-FcObjectFini (void);
-
 #define FcObjectCompare(a, b)	((int) a - (int) b)
 
 /* fcpat.c */
@@ -1102,4 +1056,21 @@ FcStrSerializeAlloc (FcSerialize *serialize, const FcChar8 *str);
 FcPrivate FcChar8 *
 FcStrSerialize (FcSerialize *serialize, const FcChar8 *str);
 
+/* fcobjs.c */
+
+FcPrivate FcObject
+FcObjectLookupIdByName (const char *str);
+
+FcPrivate FcObject
+FcObjectLookupBuiltinIdByName (const char *str);
+
+FcPrivate const char *
+FcObjectLookupOtherNameById (FcObject id);
+
+FcPrivate const FcObjectType *
+FcObjectLookupOtherTypeById (FcObject id);
+
+FcPrivate const FcObjectType *
+FcObjectLookupOtherTypeByName (const char *str);
+
 #endif /* _FC_INT_H_ */
diff --git a/src/fcname.c b/src/fcname.c
index bc53794..1bfc3b7 100644
--- a/src/fcname.c
+++ b/src/fcname.c
@@ -28,209 +28,20 @@
 #include <string.h>
 #include <stdio.h>
 
-/*
- * Please do not change this list, it is used to initialize the object
- * list in this order to match the FC_foo_OBJECT constants. Those
- * constants are written into cache files.
- */
-
-static const FcObjectType _FcBaseObjectTypes[] = {
-    { FC_FAMILY,	FcTypeString, },    /* 1 */
-    { FC_FAMILYLANG,	FcTypeString, },
-    { FC_STYLE,		FcTypeString, },
-    { FC_STYLELANG,	FcTypeString, },
-    { FC_FULLNAME,	FcTypeString, },
-    { FC_FULLNAMELANG,	FcTypeString, },
-    { FC_SLANT,		FcTypeInteger, },
-    { FC_WEIGHT,	FcTypeInteger, },
-    { FC_WIDTH,		FcTypeInteger, },
-    { FC_SIZE,		FcTypeDouble, },
-    { FC_ASPECT,	FcTypeDouble, },
-    { FC_PIXEL_SIZE,	FcTypeDouble, },
-    { FC_SPACING,	FcTypeInteger, },
-    { FC_FOUNDRY,	FcTypeString, },
-    { FC_ANTIALIAS,	FcTypeBool, },
-    { FC_HINT_STYLE,    FcTypeInteger, },
-    { FC_HINTING,	FcTypeBool, },
-    { FC_VERTICAL_LAYOUT,   FcTypeBool, },
-    { FC_AUTOHINT,	FcTypeBool, },
-    { FC_GLOBAL_ADVANCE,    FcTypeBool, },	/* deprecated */
-    { FC_FILE,		FcTypeString, },
-    { FC_INDEX,		FcTypeInteger, },
-    { FC_RASTERIZER,	FcTypeString, },
-    { FC_OUTLINE,	FcTypeBool, },
-    { FC_SCALABLE,	FcTypeBool, },
-    { FC_DPI,		FcTypeDouble },
-    { FC_RGBA,		FcTypeInteger, },
-    { FC_SCALE,		FcTypeDouble, },
-    { FC_MINSPACE,	FcTypeBool, },
-    { FC_CHAR_WIDTH,	FcTypeInteger },
-    { FC_CHAR_HEIGHT,	FcTypeInteger },
-    { FC_MATRIX,	FcTypeMatrix },
-    { FC_CHARSET,	FcTypeCharSet },
-    { FC_LANG,		FcTypeLangSet },
-    { FC_FONTVERSION,	FcTypeInteger },
-    { FC_CAPABILITY,	FcTypeString },
-    { FC_FONTFORMAT,	FcTypeString },
-    { FC_EMBOLDEN,	FcTypeBool },
-    { FC_EMBEDDED_BITMAP,   FcTypeBool },
-    { FC_DECORATIVE,	FcTypeBool },
-    { FC_LCD_FILTER,	FcTypeInteger }, /* 41 */
-    { FC_NAMELANG,	FcTypeString }, /* 42 */
-};
-
-#define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
-
-typedef struct _FcObjectTypeList    FcObjectTypeList;
-
-struct _FcObjectTypeList {
-    const FcObjectTypeList  *next;
-    const FcObjectType	    *types;
-    int			    ntypes;
-};
-
-static const FcObjectTypeList _FcBaseObjectTypesList = {
-    0,
-    _FcBaseObjectTypes,
-    NUM_OBJECT_TYPES,
+static const FcObjectType FcObjects[] = {
+#define FC_OBJECT(NAME, Type) { FC_##NAME, Type },
+#include "fcobjs.h"
+#undef FC_OBJECT
 };
 
-static const FcObjectTypeList	*_FcObjectTypes = &_FcBaseObjectTypesList;
+#define NUM_OBJECT_TYPES ((int) (sizeof FcObjects / sizeof FcObjects[0]))
 
-#define OBJECT_HASH_SIZE    31
-
-typedef struct _FcObjectBucket {
-    struct _FcObjectBucket  *next;
-    FcChar32		    hash;
-    FcObject		    id;
-} FcObjectBucket;
-
-static FcObjectBucket	*FcObjectBuckets[OBJECT_HASH_SIZE];
-
-static FcObjectType	*FcObjects = (FcObjectType *) _FcBaseObjectTypes;
-static int		FcObjectsNumber = NUM_OBJECT_TYPES;
-static int		FcObjectsSize = 0;
-static FcBool		FcObjectsInited;
-
-static FcObjectType *
-FcObjectInsert (const char *name, FcType type)
-{
-    FcObjectType    *o;
-    if (FcObjectsNumber >= FcObjectsSize)
-    {
-	int		newsize = FcObjectsNumber * 2;
-	FcObjectType	*newobjects;
-	
-	if (FcObjectsSize)
-	    newobjects = realloc (FcObjects, newsize * sizeof (FcObjectType));
-	else
-	{
-	    newobjects = malloc (newsize * sizeof (FcObjectType));
-	    if (newobjects)
-		memcpy (newobjects, FcObjects,
-			FcObjectsNumber * sizeof (FcObjectType));
-	}
-	if (!newobjects)
-	    return NULL;
-	FcObjects = newobjects;
-	FcObjectsSize = newsize;
-    }
-    o = &FcObjects[FcObjectsNumber];
-    o->object = name;
-    o->type = type;
-    ++FcObjectsNumber;
-    return o;
-}
-
-static FcObject
-FcObjectId (FcObjectType *o)
-{
-    return o - FcObjects + 1;
-}
-
-static FcObjectType *
-FcObjectFindByName (const char *object, FcBool insert)
-{
-    FcChar32	    hash = FcStringHash ((const FcChar8 *) object);
-    FcObjectBucket  **p;
-    FcObjectBucket  *b;
-    FcObjectType    *o;
-
-    if (!FcObjectsInited)
-	FcObjectInit ();
-    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
-    {
-	o = FcObjects + b->id - 1;
-        if (b->hash == hash && !strcmp (object, (o->object)))
-            return o;
-    }
-    if (!insert)
-	return NULL;
-    /*
-     * Hook it into the hash chain
-     */
-    b = malloc (sizeof(FcObjectBucket));
-    if (!b)
-	return NULL;
-    object = (const char *) FcStrCopy ((FcChar8 *) object);
-    if (!object) {
-	free (b);
-	return NULL;
-    }
-    o = FcObjectInsert (object, -1);
-    b->next = NULL;
-    b->hash = hash;
-    b->id = FcObjectId (o);
-    *p = b;
-    return o;
-}
-
-static FcObjectType *
+static const FcObjectType *
 FcObjectFindById (FcObject object)
 {
-    if (1 <= object && object <= FcObjectsNumber)
-	return FcObjects + object - 1;
-    return NULL;
-}
-
-static FcBool
-FcObjectHashInsert (const FcObjectType *object, FcBool copy)
-{
-    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
-    FcObjectBucket  **p;
-    FcObjectBucket  *b;
-    FcObjectType    *o;
-
-    if (!FcObjectsInited)
-	FcObjectInit ();
-    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
-    {
-	o = FcObjects + b->id - 1;
-        if (b->hash == hash && !strcmp (object->object, o->object))
-            return FcFalse;
-    }
-    /*
-     * Hook it into the hash chain
-     */
-    b = malloc (sizeof(FcObjectBucket));
-    if (!b)
-	return FcFalse;
-    if (copy)
-    {
-	o = FcObjectInsert (object->object, object->type);
-	if (!o)
-	{
-	    free (b);
-	    return FcFalse;
-	}
-    }
-    else
-	o = (FcObjectType *) object;
-    b->next = NULL;
-    b->hash = hash;
-    b->id = FcObjectId (o);
-    *p = b;
-    return FcTrue;
+    if (1 <= object && object <= NUM_OBJECT_TYPES)
+	return &FcObjects[object - 1];
+    return FcObjectLookupOtherTypeById (object);
 }
 
 FcBool
@@ -250,13 +61,18 @@ FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
 const FcObjectType *
 FcNameGetObjectType (const char *object)
 {
-    return FcObjectFindByName (object, FcFalse);
+    int id = FcObjectLookupBuiltinIdByName (object);
+
+    if (!id)
+	return FcObjectLookupOtherTypeByName (object);
+
+    return &FcObjects[id - 1];
 }
 
 FcBool
 FcObjectValidType (FcObject object, FcType type)
 {
-    FcObjectType    *t = FcObjectFindById (object);
+    const FcObjectType    *t = FcObjectFindById (object);
 
     if (t) {
 	switch ((int) t->type) {
@@ -282,11 +98,7 @@ FcObjectValidType (FcObject object, FcType type)
 FcObject
 FcObjectFromName (const char * name)
 {
-    FcObjectType    *o = FcObjectFindByName (name, FcTrue);
-
-    if (o)
-	return FcObjectId (o);
-    return 0;
+    return FcObjectLookupIdByName (name);
 }
 
 FcObjectSet *
@@ -297,61 +109,21 @@ FcObjectGetSet (void)
 
 
     os = FcObjectSetCreate ();
-    for (i = 0; i < FcObjectsNumber; i++)
+    for (i = 0; i < NUM_OBJECT_TYPES; i++)
 	FcObjectSetAdd (os, FcObjects[i].object);
 
     return os;
 }
 
-FcBool
-FcObjectInit (void)
-{
-    unsigned int	i;
-
-    if (FcObjectsInited)
-	return FcTrue;
-
-    FcObjectsInited = FcTrue;
-    for (i = 0; i < NUM_OBJECT_TYPES; i++)
-	if (!FcObjectHashInsert (&_FcBaseObjectTypes[i], FcFalse))
-	    return FcFalse;
-    return FcTrue;
-}
-
-void
-FcObjectFini (void)
-{
-    int		    i;
-    FcObjectBucket  *b, *next;
-
-    for (i = 0; i < OBJECT_HASH_SIZE; i++)
-    {
-	for (b = FcObjectBuckets[i]; b; b = next)
-	{
-	    next = b->next;
-	    free (b);
-	}
-	FcObjectBuckets[i] = 0;
-    }
-    for (i = 0; i < FcObjectsNumber; i++)
-	if (FcObjects[i].type == (unsigned int) -1)
-	    free ((void*) FcObjects[i].object);
-    if (FcObjects != _FcBaseObjectTypes)
-	free (FcObjects);
-    FcObjects = (FcObjectType *) _FcBaseObjectTypes;
-    FcObjectsNumber = NUM_OBJECT_TYPES;
-    FcObjectsSize = 0;
-    FcObjectsInited = FcFalse;
-}
-
 const char *
 FcObjectName (FcObject object)
 {
-    FcObjectType    *o = FcObjectFindById (object);
+    const FcObjectType   *o = FcObjectFindById (object);
 
     if (o)
 	return o->object;
-    return NULL;
+
+    return FcObjectLookupOtherNameById (object);
 }
 
 static const FcConstant _FcBaseConstants[] = {
@@ -377,7 +149,7 @@ static const FcConstant _FcBaseConstants[] = {
     { (FcChar8 *) "ultracondensed", "width",	FC_WIDTH_ULTRACONDENSED },
     { (FcChar8 *) "extracondensed", "width",	FC_WIDTH_EXTRACONDENSED },
     { (FcChar8 *) "condensed",	    "width",	FC_WIDTH_CONDENSED },
-    { (FcChar8 *) "semicondensed", "width",	FC_WIDTH_SEMICONDENSED },
+    { (FcChar8 *) "semicondensed",  "width",	FC_WIDTH_SEMICONDENSED },
     { (FcChar8 *) "normal",	    "width",	FC_WIDTH_NORMAL },
     { (FcChar8 *) "semiexpanded",   "width",	FC_WIDTH_SEMIEXPANDED },
     { (FcChar8 *) "expanded",	    "width",	FC_WIDTH_EXPANDED },
@@ -437,7 +209,7 @@ FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
 const FcConstant *
 FcNameGetConstant (const FcChar8 *string)
 {
-    int	    i;
+    unsigned int	    i;
 
     for (i = 0; i < NUM_FC_CONSTANTS; i++)
 	if (!FcStrCmpIgnoreCase (string, _FcBaseConstants[i].name))
@@ -754,8 +526,6 @@ FcNameUnparseEscaped (FcPattern *pat, FcBool escape)
     FcChar8		    buf_static[8192];
     int			    i;
     FcPatternElt	    *e;
-    const FcObjectTypeList  *l;
-    const FcObjectType	    *o;
 
     FcStrBufInit (&buf, buf_static, sizeof (buf_static));
     e = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
@@ -772,28 +542,27 @@ FcNameUnparseEscaped (FcPattern *pat, FcBool escape)
 	if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? (FcChar8 *) FC_ESCAPE_FIXED : 0))
 	    goto bail0;
     }
-    for (l = _FcObjectTypes; l; l = l->next)
+    for (i = 0; i < NUM_OBJECT_TYPES; i++)
     {
-	for (i = 0; i < l->ntypes; i++)
+	FcObject id = i + 1;
+	const FcObjectType	    *o;
+	o = &FcObjects[i];
+	if (!strcmp (o->object, FC_FAMILY) ||
+	    !strcmp (o->object, FC_SIZE))
+	    continue;
+    
+	e = FcPatternObjectFindElt (pat, id);
+	if (e)
 	{
-	    o = &l->types[i];
-	    if (!strcmp (o->object, FC_FAMILY) ||
-		!strcmp (o->object, FC_SIZE))
-		continue;
-	
-	    e = FcPatternObjectFindElt (pat, FcObjectFromName (o->object));
-	    if (e)
-	    {
-		if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
-		    goto bail0;
-		if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, escape ? (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
-		    goto bail0;
-		if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
-		    goto bail0;
-		if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ?
-					     (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
-		    goto bail0;
-	    }
+	    if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
+		goto bail0;
+	    if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, escape ? (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
+		goto bail0;
+	    if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
+		goto bail0;
+	    if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ?
+					 (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
+		goto bail0;
 	}
     }
     return FcStrBufDone (&buf);
diff --git a/src/fcobjs.c b/src/fcobjs.c
new file mode 100644
index 0000000..e3ba2f8
--- /dev/null
+++ b/src/fcobjs.c
@@ -0,0 +1,130 @@
+/*
+ * fontconfig/src/fclist.c
+ *
+ * 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 "fcobjshash.h"
+
+#include <string.h>
+
+static int next_id = FC_MAX_BASE_OBJECT + 1;
+struct FcObjectOtherTypeInfo {
+    struct FcObjectOtherTypeInfo *next;
+    FcObjectType object;
+    int id;
+} *other_types;
+
+static FcObjectType *
+_FcObjectLookupOtherTypeByName (const char *str, FcObject *id)
+{
+    struct FcObjectOtherTypeInfo *ots, *ot;
+
+    /* XXX MT-unsafe */
+    ots = other_types;
+
+    for (ot = ots; ot; ot = ot->next)
+	if (0 == strcmp (ot->object.object, str))
+	    break;
+
+    if (!ot)
+    {
+	ot = malloc (sizeof (*ot));
+	if (!ot)
+	    return NULL;
+
+	ot->object.object = strdup (str);
+	ot->object.type = -1;
+	ot->id = next_id++; /* MT_unsafe */
+	ot->next = ot;
+
+	other_types = ot;
+    }
+
+    if (id)
+      *id = ot->id;
+
+    return &ot->object;
+}
+
+FcObject
+FcObjectLookupBuiltinIdByName (const char *str)
+{
+    const struct FcObjectTypeInfo *o = FcObjectTypeLookup (str, strlen (str));
+    FcObject id;
+    if (o)
+	return o->id;
+
+    return 0;
+}
+
+FcObject
+FcObjectLookupIdByName (const char *str)
+{
+    const struct FcObjectTypeInfo *o = FcObjectTypeLookup (str, strlen (str));
+    FcObject id;
+    if (o)
+	return o->id;
+
+    if (_FcObjectLookupOtherTypeByName (str, &id))
+	return id;
+
+    return 0;
+}
+
+const char *
+FcObjectLookupOtherNameById (FcObject id)
+{
+    /* XXX MT-unsafe */
+    struct FcObjectOtherTypeInfo *ot;
+
+    for (ot = other_types; ot; ot = ot->next)
+	if (ot->id == id)
+	    return ot->object.object;
+
+    return NULL;
+}
+
+const FcObjectType *
+FcObjectLookupOtherTypeByName (const char *str)
+{
+    return _FcObjectLookupOtherTypeByName (str, NULL);
+}
+
+FcPrivate const FcObjectType *
+FcObjectLookupOtherTypeById (FcObject id)
+{
+    /* XXX MT-unsafe */
+    struct FcObjectOtherTypeInfo *ot;
+
+    for (ot = other_types; ot; ot = ot->next)
+	if (ot->id == id)
+	    return &ot->object;
+
+    return NULL;
+}
+
+
+#define __fcobjs__
+#include "fcaliastail.h"
+#undef __fcobjs__
diff --git a/src/fcobjs.h b/src/fcobjs.h
new file mode 100644
index 0000000..1e9067e
--- /dev/null
+++ b/src/fcobjs.h
@@ -0,0 +1,44 @@
+/* DON'T REORDER!  The order is part of the cache signature. */
+FC_OBJECT (FAMILY,		FcTypeString)
+FC_OBJECT (FAMILYLANG,		FcTypeString)
+FC_OBJECT (STYLE,		FcTypeString)
+FC_OBJECT (STYLELANG,		FcTypeString)
+FC_OBJECT (FULLNAME,		FcTypeString)
+FC_OBJECT (FULLNAMELANG,	FcTypeString)
+FC_OBJECT (SLANT,		FcTypeInteger)
+FC_OBJECT (WEIGHT,		FcTypeInteger)
+FC_OBJECT (WIDTH,		FcTypeInteger)
+FC_OBJECT (SIZE,		FcTypeDouble)
+FC_OBJECT (ASPECT,		FcTypeDouble)
+FC_OBJECT (PIXEL_SIZE,		FcTypeDouble)
+FC_OBJECT (SPACING,		FcTypeInteger)
+FC_OBJECT (FOUNDRY,		FcTypeString)
+FC_OBJECT (ANTIALIAS,		FcTypeBool)
+FC_OBJECT (HINT_STYLE,		FcTypeInteger)
+FC_OBJECT (HINTING,		FcTypeBool)
+FC_OBJECT (VERTICAL_LAYOUT,	FcTypeBool)
+FC_OBJECT (AUTOHINT,		FcTypeBool)
+FC_OBJECT (GLOBAL_ADVANCE,	FcTypeBool)	/* deprecated */
+FC_OBJECT (FILE,		FcTypeString)
+FC_OBJECT (INDEX,		FcTypeInteger)
+FC_OBJECT (RASTERIZER,		FcTypeString)
+FC_OBJECT (OUTLINE,		FcTypeBool)
+FC_OBJECT (SCALABLE,		FcTypeBool)
+FC_OBJECT (DPI,			FcTypeDouble)
+FC_OBJECT (RGBA,		FcTypeInteger)
+FC_OBJECT (SCALE,		FcTypeDouble)
+FC_OBJECT (MINSPACE,		FcTypeBool)
+FC_OBJECT (CHAR_WIDTH,		FcTypeInteger)
+FC_OBJECT (CHAR_HEIGHT,		FcTypeInteger)
+FC_OBJECT (MATRIX,		FcTypeMatrix)
+FC_OBJECT (CHARSET,		FcTypeCharSet)
+FC_OBJECT (LANG,		FcTypeLangSet)
+FC_OBJECT (FONTVERSION,		FcTypeInteger)
+FC_OBJECT (CAPABILITY,		FcTypeString)
+FC_OBJECT (FONTFORMAT,		FcTypeString)
+FC_OBJECT (EMBOLDEN,		FcTypeBool)
+FC_OBJECT (EMBEDDED_BITMAP,	FcTypeBool)
+FC_OBJECT (DECORATIVE,		FcTypeBool)
+FC_OBJECT (LCD_FILTER,		FcTypeInteger)
+FC_OBJECT (NAMELANG,		FcTypeString)
+/* ^-------------- Add new objects here. */
diff --git a/src/fcobjshash.gperf.h b/src/fcobjshash.gperf.h
new file mode 100644
index 0000000..94002b1
--- /dev/null
+++ b/src/fcobjshash.gperf.h
@@ -0,0 +1,26 @@
+%{
+CUT_OUT_BEGIN
+#include <fontconfig/fontconfig.h>
+CUT_OUT_END
+%}
+%struct-type
+%language=C
+%includes
+%enum
+%readonly-tables
+%define slot-name name
+%define hash-function-name FcObjectTypeHash
+%define lookup-function-name FcObjectTypeLookup
+
+%pic
+%define string-pool-name FcObjectTypeNamePool
+
+struct FcObjectTypeInfo {
+	int name;
+	int id;
+};
+
+%%
+#define FC_OBJECT(NAME, Type) FC_##NAME, FC_##NAME##_OBJECT
+#include "fcobjs.h"
+#undef FC_OBJECT
commit 7c0f79c5fe9db50b55112a1048a8f1c6a80e96fa
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Sep 20 14:01:47 2012 -0400

    Deprecate FcName(Un)RegisterObjectTypes / FcName(Un)RegisterConstants
    
    These never worked as intended.  The problem is, if Fontconfig tries to
    read config files when these new types / constants are not registered,
    it errs.  As a result, no defined types / constants are usable from
    config files.  Which makes these really useless.  Xft was the only user
    of this API and even there it's not really used.  Just kill it.
    
    One inch closer to thread-safety since we can fix the object-type hash
    table at compile time.

diff --git a/doc/fcconstant.fncs b/doc/fcconstant.fncs
index 2ead0a6..8f4c878 100644
--- a/doc/fcconstant.fncs
+++ b/doc/fcconstant.fncs
@@ -27,9 +27,7 @@
 @TYPE2@		int%				@ARG2@		nconsts
 @PURPOSE@	Register symbolic constants
 @DESC@
-Register <parameter>nconsts</parameter> new symbolic constants. Returns
-FcFalse if the constants cannot be registered (due to allocation failure).
-Otherwise returns FcTrue.
+Deprecated.  Does nothing.  Returns FcFalse.
 @@
 
 @RET@		FcBool
@@ -38,9 +36,7 @@ Otherwise returns FcTrue.
 @TYPE2@		int%				@ARG2@		nconsts
 @PURPOSE@	Unregister symbolic constants
 @DESC@
-Unregister <parameter>nconsts</parameter> symbolic constants. Returns
-FcFalse if the specified constants were not registered. Otherwise returns
-FcTrue.
+Deprecated.  Does nothing.  Returns FcFalse.
 @@
 
 @RET@		const FcConstant *
diff --git a/doc/fcobjecttype.fncs b/doc/fcobjecttype.fncs
index 45f3a31..3f976e4 100644
--- a/doc/fcobjecttype.fncs
+++ b/doc/fcobjecttype.fncs
@@ -27,9 +27,7 @@
 @TYPE2@		int%				@ARG2@		ntype
 @PURPOSE@	Register object types
 @DESC@
-Register <parameter>ntype</parameter> new object types. Returns FcFalse if
-some of the names cannot be
-registered (due to allocation failure). Otherwise returns FcTrue.
+Deprecated.  Does nothing.  Returns FcFalse.
 @@
 
 @RET@		FcBool
@@ -38,7 +36,7 @@ registered (due to allocation failure). Otherwise returns FcTrue.
 @TYPE2@		int% 				@ARG2@		ntype
 @PURPOSE@	Unregister object types
 @DESC@
-Unregister <parameter>ntype</parameter> object types. Returns FcTrue.
+Deprecated.  Does nothing.  Returns FcFalse.
 @@
 
 @RET@		const FcObjectType *
diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 0c169ef..1af63ab 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -424,7 +424,7 @@ FcCharSetCreate (void);
 /* deprecated alias for FcCharSetCreate */
 FcPublic FcCharSet *
 FcCharSetNew (void);
-    
+
 FcPublic void
 FcCharSetDestroy (FcCharSet *fcs);
 
@@ -734,21 +734,25 @@ FcMatrixShear (FcMatrix *m, double sh, double sv);
 
 /* fcname.c */
 
+/* Deprecated.  Does nothing.  Returns FcFalse. */
 FcPublic FcBool
 FcNameRegisterObjectTypes (const FcObjectType *types, int ntype);
 
+/* Deprecated.  Does nothing.  Returns FcFalse. */
 FcPublic FcBool
 FcNameUnregisterObjectTypes (const FcObjectType *types, int ntype);
-    
+
 FcPublic const FcObjectType *
 FcNameGetObjectType (const char *object);
 
+/* Deprecated.  Does nothing.  Returns FcFalse. */
 FcPublic FcBool
 FcNameRegisterConstants (const FcConstant *consts, int nconsts);
 
+/* Deprecated.  Does nothing.  Returns FcFalse. */
 FcPublic FcBool
 FcNameUnregisterConstants (const FcConstant *consts, int nconsts);
-    
+
 FcPublic const FcConstant *
 FcNameGetConstant (const FcChar8 *string);
 
diff --git a/src/fcname.c b/src/fcname.c
index 3f63534..bc53794 100644
--- a/src/fcname.c
+++ b/src/fcname.c
@@ -233,55 +233,18 @@ FcObjectHashInsert (const FcObjectType *object, FcBool copy)
     return FcTrue;
 }
 
-static void
-FcObjectHashRemove (const FcObjectType *object, FcBool cleanobj)
-{
-    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
-    FcObjectBucket  **p;
-    FcObjectBucket  *b;
-    FcObjectType    *o;
-
-    if (!FcObjectsInited)
-	FcObjectInit ();
-    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
-    {
-	o = FcObjects + b->id - 1;
-        if (b->hash == hash && !strcmp (object->object, o->object))
-	{
-	    *p = b->next;
-	    free (b);
-	    if (cleanobj)
-	    {
-		/* Clean up object array */
-		o->object = NULL;
-		o->type = -1;
-		while (FcObjects[FcObjectsNumber-1].object == NULL)
-		    --FcObjectsNumber;
-	    }
-            break;
-	}
-    }
-}
-
 FcBool
 FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
 {
-    int	i;
-
-    for (i = 0; i < ntypes; i++)
-	if (!FcObjectHashInsert (&types[i], FcTrue))
-	    return FcFalse;
-    return FcTrue;
+    /* Deprecated. */
+    return FcFalse;
 }
 
 FcBool
 FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
 {
-    int	i;
-
-    for (i = 0; i < ntypes; i++)
-	FcObjectHashRemove (&types[i], FcTrue);
-    return FcTrue;
+    /* Deprecated. */
+    return FcFalse;
 }
 
 const FcObjectType *
@@ -457,68 +420,29 @@ static const FcConstant _FcBaseConstants[] = {
 
 #define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
 
-typedef struct _FcConstantList FcConstantList;
-
-struct _FcConstantList {
-    const FcConstantList    *next;
-    const FcConstant	    *consts;
-    int			    nconsts;
-};
-
-static const FcConstantList _FcBaseConstantList = {
-    0,
-    _FcBaseConstants,
-    NUM_FC_CONSTANTS
-};
-
-static const FcConstantList	*_FcConstants = &_FcBaseConstantList;
-
 FcBool
 FcNameRegisterConstants (const FcConstant *consts, int nconsts)
 {
-    FcConstantList	*l;
-
-    l = (FcConstantList *) malloc (sizeof (FcConstantList));
-    if (!l)
-	return FcFalse;
-    l->consts = consts;
-    l->nconsts = nconsts;
-    l->next = _FcConstants;
-    _FcConstants = l;
-    return FcTrue;
+    /* Deprecated. */
+    return FcFalse;
 }
 
 FcBool
 FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
 {
-    const FcConstantList	*l, **prev;
-
-    for (prev = &_FcConstants;
-	 (l = *prev);
-	 prev = (const FcConstantList **) &(l->next))
-    {
-	if (l->consts == consts && l->nconsts == nconsts)
-	{
-	    *prev = l->next;
-	    free ((void *) l);
-	    return FcTrue;
-	}
-    }
+    /* Deprecated. */
     return FcFalse;
 }
 
 const FcConstant *
 FcNameGetConstant (const FcChar8 *string)
 {
-    const FcConstantList    *l;
-    int			    i;
+    int	    i;
+
+    for (i = 0; i < NUM_FC_CONSTANTS; i++)
+	if (!FcStrCmpIgnoreCase (string, _FcBaseConstants[i].name))
+	    return &_FcBaseConstants[i];
 
-    for (l = _FcConstants; l; l = l->next)
-    {
-	for (i = 0; i < l->nconsts; i++)
-	    if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
-		return &l->consts[i];
-    }
     return 0;
 }
 


More information about the Fontconfig mailing list