fontconfig: Branch 'main' - 8 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu May 22 12:16:37 UTC 2025


 conf.d/50-user.conf      |    3 -
 doc/fclangset.fncs       |   13 ++++
 doc/fcpattern.fncs       |   25 +++++++++
 fc-match/fc-match.c      |    2 
 fc-pattern/fc-pattern.c  |    2 
 fontconfig/fontconfig.h  |    7 ++
 src/fccache.c            |   14 +++--
 src/fccfg.c              |   46 ++++++++++++++---
 src/fcdefault.c          |  124 +++++++++++++++++++++++++++--------------------
 src/fcinit.c             |    3 -
 src/fcint.h              |   19 ++++++-
 src/fclist.c             |    2 
 src/fcobjs.c             |   23 ++++++++
 src/fcxml.c              |  116 +++----------------------------------------
 test/meson.build         |    1 
 test/test-bz106618.c     |    4 -
 test/test-bz106632.c     |    1 
 test/test-conf.c         |    2 
 test/test-crbug1004254.c |    2 
 test/test-mt-fccfg.c     |   74 ++++++++++++++++++++++++++++
 test/test-pthread.c      |    2 
 21 files changed, 295 insertions(+), 190 deletions(-)

New commits:
commit 61e603f2a5096f7557f0f41c20d250770e893851
Merge: 1c63ed3 dbead4c
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu May 22 12:16:34 2025 +0000

    Merge branch 'issues/448' into 'main'
    
    Fix some memory leaks around FcFini()
    
    Closes #448
    
    See merge request fontconfig/fontconfig!410

commit dbead4c954f6d2146cfb23542aa40183165456fb
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu May 22 20:32:00 2025 +0900

    Increase a reference count for default FcConfig instance with FcInit()
    
    This allows us to decrease a reference count with FcFini
    without explicit FcConfigReference.
    FcFini() needs to be brought up as many as a number of call
    of FcInit().
    
    Fixes https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/448
    
    Changelog: changed

diff --git a/src/fccfg.c b/src/fccfg.c
index 9d94b7a..0d3d573 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -117,7 +117,13 @@ FcDestroyAsRuleSet (void *data)
 FcBool
 FcConfigInit (void)
 {
-    return FcConfigEnsure() ? FcTrue : FcFalse;
+    FcBool is_new = !!(_fcConfig == NULL);
+    FcBool ret;
+
+    ret = FcConfigEnsure() ? FcTrue : FcFalse;
+    if (ret && !is_new)
+	FcConfigReference (_fcConfig);
+    return ret;
 }
 
 void
diff --git a/test/test-mt-fccfg.c b/test/test-mt-fccfg.c
index da1569a..24ad758 100644
--- a/test/test-mt-fccfg.c
+++ b/test/test-mt-fccfg.c
@@ -10,8 +10,7 @@
 #define NTHR 100
 
 struct thr_arg_s {
-    int       thr_num;
-    FcConfig *config;
+    int thr_num;
 };
 
 static void *
@@ -20,6 +19,7 @@ run_test_in_thread (void *arg)
     FcPattern *pat, *m;
     FcResult   result;
 
+    FcInit();
     pat = FcNameParse ((const FcChar8 *)"sans-serif");
     FcConfigSubstitute (NULL, pat, FcMatchPattern);
     FcConfigSetDefaultSubstitute (NULL, pat);
@@ -43,7 +43,6 @@ test (void)
     for (i = 0; i < NTHR; i++) {
 	int result;
 	thr_args[i].thr_num = i;
-	thr_args[i].config = FcConfigReference (c1);
 
 	result = pthread_create (&threads[i], NULL, run_test_in_thread, (void *)&thr_args[i]);
 	if (result != 0) {
commit 76ba00801447a4c50bba3a3539402aa450cffd50
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu May 22 15:43:30 2025 +0900

    Free the mutex object only when there are no references to the default FcConfig instance
    
    Changelog: changed

diff --git a/src/fccfg.c b/src/fccfg.c
index 0d52101..9d94b7a 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -123,10 +123,21 @@ FcConfigInit (void)
 void
 FcConfigFini (void)
 {
-    FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
-    if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
-	FcConfigDestroy (cfg);
-    free_lock();
+    FcConfig *cfg;
+
+retry:
+    cfg = fc_atomic_ptr_get (&_fcConfig);
+    if (cfg) {
+	if (cfg->ref.count > 1)
+	    FcConfigDestroy (cfg);
+	else {
+	    if (fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
+		FcConfigDestroy (cfg);
+	    else
+		goto retry;
+	    free_lock();
+	}
+    }
 }
 
 FcConfig *
diff --git a/test/meson.build b/test/meson.build
index bd958a6..05a6932 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -34,6 +34,7 @@ if host_machine.system() != 'windows'
   tests_not_parallel += [
     # FIXME: this needs NotoSans-hinted.zip font downloaded and unpacked into test build directory! see run-test.sh
     ['test-crbug1004254.c', {'dependencies': dependency('threads')}], # for pthread
+    ['test-mt-fccfg.c', {'include_directories': include_directories('../src'), 'dependencies': dependency('threads')}],
   ]
 
   if get_option('default_library') == 'static'
diff --git a/test/test-mt-fccfg.c b/test/test-mt-fccfg.c
new file mode 100644
index 0000000..da1569a
--- /dev/null
+++ b/test/test-mt-fccfg.c
@@ -0,0 +1,75 @@
+/* Copyright (C) 2025 fontconfig Authors */
+/* SPDX-License-Identifier: HPND */
+#include <fontconfig/fontconfig.h>
+
+#include <stdio.h>
+#define __USE_XOPEN
+#include <pthread.h>
+#include <stdlib.h>
+
+#define NTHR 100
+
+struct thr_arg_s {
+    int       thr_num;
+    FcConfig *config;
+};
+
+static void *
+run_test_in_thread (void *arg)
+{
+    FcPattern *pat, *m;
+    FcResult   result;
+
+    pat = FcNameParse ((const FcChar8 *)"sans-serif");
+    FcConfigSubstitute (NULL, pat, FcMatchPattern);
+    FcConfigSetDefaultSubstitute (NULL, pat);
+    m = FcFontMatch (NULL, pat, &result);
+    FcPatternDestroy (pat);
+    FcPatternDestroy (m);
+    FcFini();
+
+    return NULL;
+}
+
+int
+test (void)
+{
+    pthread_t        threads[NTHR];
+    struct thr_arg_s thr_args[NTHR];
+    FcConfig        *c1, *c2, *c3;
+    int              i, j;
+
+    c1 = FcConfigGetCurrent();
+    for (i = 0; i < NTHR; i++) {
+	int result;
+	thr_args[i].thr_num = i;
+	thr_args[i].config = FcConfigReference (c1);
+
+	result = pthread_create (&threads[i], NULL, run_test_in_thread, (void *)&thr_args[i]);
+	if (result != 0) {
+	    fprintf (stderr, "Cannot create thread %d\n", i);
+	    break;
+	}
+    }
+    for (j = 0; j < i; j++) {
+	pthread_join (threads[j], NULL);
+    }
+    FcFini();
+    c3 = FcConfigCreate(); /* To avoid allocation at the same place */
+    c2 = FcConfigGetCurrent();
+    FcConfigDestroy (c3);
+    printf ("cur: %p\n", c2);
+    if (c1 == c2)
+	return 1;
+    /* To make visible if we have any references */
+    putenv ("FC_DEBUG=16");
+    FcFini();
+
+    return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+    return test();
+}
commit e0f39a6f027877fd917ab993d89d2cfc17dbcc8b
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu May 22 15:18:59 2025 +0900

    Free the mutex object only when all cache objects isn't referenced
    
    Changelog: changed

diff --git a/src/fccache.c b/src/fccache.c
index f3e4320..c63976e 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -754,18 +754,20 @@ FcCacheAllocate (FcCache *cache, size_t len)
 void
 FcCacheFini (void)
 {
-    int i;
+    int    i;
+    FcBool res = FcTrue;
 
-    if (FcDebug() & FC_DBG_CACHE) {
-	for (i = 0; i < FC_CACHE_MAX_LEVEL; i++) {
-	    if (fcCacheChains[i] != NULL) {
+    for (i = 0; i < FC_CACHE_MAX_LEVEL; i++) {
+	if (fcCacheChains[i] != NULL) {
+	    res = FcFalse;
+	    if (FcDebug() & FC_DBG_CACHE) {
 		FcCacheSkip *s = fcCacheChains[i];
 		fprintf (stderr, "Fontconfig error: not freed %p (dir: %s, refcount %" FC_ATOMIC_INT_FORMAT ")\n", s->cache, FcCacheDir (s->cache), s->ref.count);
 	    }
 	}
     }
-
-    free_lock();
+    if (res)
+	free_lock();
 }
 
 static FcBool
commit dbbb46207e243d07d2e4c4d35547fc7521c9c62f
Author: Akira TAGOH <akira at tagoh.org>
Date:   Fri May 16 19:45:41 2025 +0900

    Drop FcObjectFini() from FcFini() to fix memory leaks
    
    Initialization around FcObject has been integrated into
    FcConfig instance. they are freed once all FcConfig instances
    has been destroyed.  So we don't need to call FcObjectFini()
    from FcFini() anymore.
    
    Changelog: fixed

diff --git a/src/fccfg.c b/src/fccfg.c
index 0756698..0d52101 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -210,6 +210,7 @@ FcConfigCreate (void)
     config->prefer_app_fonts = FcFalse;
 
     FcRefInit (&config->ref, 1);
+    FcObjectInit();
 
     return config;
 
@@ -356,6 +357,7 @@ FcConfigDestroy (FcConfig *config)
 	if (FcRefDec (&config->ref) != 1)
 	    return;
 
+	FcObjectFini();
 	(void)fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
 
 	FcStrSetDestroy (config->configDirs);
diff --git a/src/fcinit.c b/src/fcinit.c
index 33e1d6e..9c66515 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -214,7 +214,6 @@ void
 FcFini (void)
 {
     FcConfigFini();
-    FcObjectFini();
     FcCacheFini();
 }
 
diff --git a/src/fcint.h b/src/fcint.h
index 2dd0bf5..67a1604 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -1413,6 +1413,8 @@ FcPrivate FcChar8 *
 FcStrSerialize (FcSerialize *serialize, const FcChar8 *str);
 
 /* fcobjs.c */
+FcPrivate void
+FcObjectInit (void);
 
 FcPrivate void
 FcObjectFini (void);
diff --git a/src/fcobjs.c b/src/fcobjs.c
index e21d7a3..c7bcb0c 100644
--- a/src/fcobjs.c
+++ b/src/fcobjs.c
@@ -43,6 +43,13 @@ struct FcObjectOtherTypeInfo {
     FcObjectType                  object;
     FcObject                      id;
 } *other_types;
+static FcRef obj_ref = { .count = 0 };
+
+void
+FcObjectInit (void)
+{
+    FcRefInc (&obj_ref);
+}
 
 void
 FcObjectFini (void)
@@ -50,11 +57,17 @@ FcObjectFini (void)
     struct FcObjectOtherTypeInfo *ots, *ot;
 
 retry:
+    if (obj_ref.count < 1)
+	fprintf (stderr, "Fontconfig warning: too many caller of FcObjectFini()\n");
+    if (obj_ref.count >= 1 && FcRefDec (&obj_ref) != 1)
+	return;
     ots = fc_atomic_ptr_get (&other_types);
     if (!ots)
 	return;
-    if (!fc_atomic_ptr_cmpexch (&other_types, ots, NULL))
+    if (!fc_atomic_ptr_cmpexch (&other_types, ots, NULL)) {
+	FcRefInc (&obj_ref);
 	goto retry;
+    }
 
     while (ots) {
 	ot = ots->next;
@@ -69,9 +82,17 @@ static FcObjectType *
 _FcObjectLookupOtherTypeByName (const char *str, FcObject *id)
 {
     struct FcObjectOtherTypeInfo *ots, *ot;
+    static FcBool                 warn = FcFalse;
 
 retry:
     ots = fc_atomic_ptr_get (&other_types);
+    if (obj_ref.count < 1) {
+	if (!warn) {
+	    fprintf (stderr, "Fontconfig warning: using without calling FcInit()\n");
+	    warn = FcTrue;
+	}
+	FcObjectInit();
+    }
 
     for (ot = ots; ot; ot = ot->next)
 	if (0 == strcmp (ot->object.object, str))
diff --git a/test/test-bz106632.c b/test/test-bz106632.c
index 3c56cb7..1e18d5c 100644
--- a/test/test-bz106632.c
+++ b/test/test-bz106632.c
@@ -305,6 +305,7 @@ bail:
 	FcStrFree (fontdir);
     if (cachedir)
 	FcStrFree (cachedir);
+    FcFini();
 
     return ret;
 }
commit aaa8aca60993ae635dba4ed9fa8bd06ad0b52128
Author: Akira TAGOH <akira at tagoh.org>
Date:   Fri May 16 17:56:33 2025 +0900

    Drop the configuration path migration code
    
    We have added a code to encourage users to migrate
    user configuration file/directory to XDG-based one over
    10 years ago.
    I think it served a function enough and we don't need
    to keep it furthermore for that purpose.
    So let's just drop them.
    
    Changelog: changed

diff --git a/conf.d/50-user.conf b/conf.d/50-user.conf
index d019f4d..f38d651 100644
--- a/conf.d/50-user.conf
+++ b/conf.d/50-user.conf
@@ -10,7 +10,4 @@
 	-->
 	<include ignore_missing="yes" prefix="xdg">fontconfig/conf.d</include>
 	<include ignore_missing="yes" prefix="xdg">fontconfig/fonts.conf</include>
-	<!-- the following elements will be removed in the future -->
-	<include ignore_missing="yes" deprecated="yes">~/.fonts.conf.d</include>
-	<include ignore_missing="yes" deprecated="yes">~/.fonts.conf</include>
 </fontconfig>
diff --git a/src/fcinit.c b/src/fcinit.c
index 361f80d..33e1d6e 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -214,7 +214,6 @@ void
 FcFini (void)
 {
     FcConfigFini();
-    FcConfigPathFini();
     FcObjectFini();
     FcCacheFini();
 }
diff --git a/src/fcxml.c b/src/fcxml.c
index 328e109..a84fdcd 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -66,9 +66,6 @@ static void
 _ensureWin32GettersReady ();
 #endif
 
-static FcChar8 *__fc_userdir = NULL;
-static FcChar8 *__fc_userconf = NULL;
-
 static void
 FcExprDestroy (FcExpr *e);
 static FcBool
@@ -2309,37 +2306,15 @@ bail:
 	FcStrFree (data);
 }
 
-void
-FcConfigPathFini (void)
-{
-    FcChar8 *s;
-
-retry_dir:
-    s = fc_atomic_ptr_get (&__fc_userdir);
-    if (!fc_atomic_ptr_cmpexch (&__fc_userdir, s, NULL))
-	goto retry_dir;
-    free (s);
-
-retry_conf:
-    s = fc_atomic_ptr_get (&__fc_userconf);
-    if (!fc_atomic_ptr_cmpexch (&__fc_userconf, s, NULL))
-	goto retry_conf;
-    free (s);
-}
-
 static void
 FcParseInclude (FcConfigParse *parse)
 {
     FcChar8       *s;
     const FcChar8 *attr;
     FcBool         ignore_missing = FcFalse;
-#ifndef _WIN32
-    FcBool deprecated = FcFalse;
-#endif
-    FcChar8    *prefix = NULL, *p;
-    FcChar8    *userdir = NULL, *userconf = NULL;
-    FcRuleSet  *ruleset;
-    FcMatchKind k;
+    FcChar8       *prefix = NULL, *p;
+    FcRuleSet     *ruleset;
+    FcMatchKind    k;
 
     s = FcStrBufDoneStatic (&parse->pstack->str);
     if (!s) {
@@ -2349,11 +2324,12 @@ FcParseInclude (FcConfigParse *parse)
     attr = FcConfigGetAttribute (parse, "ignore_missing");
     if (attr && FcConfigLexBool (parse, (FcChar8 *)attr) == FcTrue)
 	ignore_missing = FcTrue;
+    /* deprecated attribute has ever been used to mark
+     * old configuration path as deprecated.
+     * We don't have any code for it but just keep it for
+     * backward compatibility.
+     */
     attr = FcConfigGetAttribute (parse, "deprecated");
-#ifndef _WIN32
-    if (attr && FcConfigLexBool (parse, (FcChar8 *)attr) == FcTrue)
-	deprecated = FcTrue;
-#endif
     attr = FcConfigGetAttribute (parse, "prefix");
     if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0) {
 	prefix = FcConfigXdgConfigHome();
@@ -2366,7 +2342,6 @@ FcParseInclude (FcConfigParse *parse)
     if (prefix) {
 	size_t   plen = strlen ((const char *)prefix);
 	size_t   dlen = strlen ((const char *)s);
-	FcChar8 *u;
 
 	p = realloc (prefix, plen + 1 + dlen + 1);
 	if (!p) {
@@ -2378,37 +2353,6 @@ FcParseInclude (FcConfigParse *parse)
 	memcpy (&prefix[plen + 1], s, dlen);
 	prefix[plen + 1 + dlen] = 0;
 	s = prefix;
-	if (FcFileIsDir (s)) {
-	userdir:
-	    userdir = fc_atomic_ptr_get (&__fc_userdir);
-	    if (!userdir) {
-		u = FcStrdup (s);
-		if (!fc_atomic_ptr_cmpexch (&__fc_userdir, userdir, u)) {
-		    free (u);
-		    goto userdir;
-		}
-		userdir = u;
-	    }
-	} else if (FcFileIsFile (s)) {
-	userconf:
-	    userconf = fc_atomic_ptr_get (&__fc_userconf);
-	    if (!userconf) {
-		u = FcStrdup (s);
-		if (!fc_atomic_ptr_cmpexch (&__fc_userconf, userconf, u)) {
-		    free (u);
-		    goto userconf;
-		}
-		userconf = u;
-	    }
-	} else {
-	    /* No config dir nor file on the XDG directory spec compliant place
-	     * so need to guess what it is supposed to be.
-	     */
-	    if (FcStrStr (s, (const FcChar8 *)"conf.d") != NULL)
-		goto userdir;
-	    else
-		goto userconf;
-	}
     }
     /* flush the ruleset into the queue */
     ruleset = parse->ruleset;
@@ -2428,50 +2372,6 @@ FcParseInclude (FcConfigParse *parse)
     FcRuleSetDestroy (ruleset);
     if (!_FcConfigParse (parse->config, s, !ignore_missing, !parse->scanOnly))
 	parse->error = FcTrue;
-#ifndef _WIN32
-    else {
-	FcChar8      *filename;
-	static FcBool warn_conf = FcFalse, warn_confd = FcFalse;
-
-	filename = FcConfigGetFilename (parse->config, s);
-	if (deprecated == FcTrue &&
-	    filename != NULL &&
-	    userdir != NULL &&
-	    !FcFileIsLink (filename)) {
-	    if (FcFileIsDir (filename)) {
-		FcChar8 *parent = FcStrDirname (userdir);
-
-		if (!FcFileIsDir (parent))
-		    FcMakeDirectory (parent);
-		FcStrFree (parent);
-		if (FcFileIsDir (userdir) ||
-		    rename ((const char *)filename, (const char *)userdir) != 0 ||
-		    symlink ((const char *)userdir, (const char *)filename) != 0) {
-		    if (!warn_confd) {
-			FcConfigMessage (parse, FcSevereWarning, "reading configurations from %s is deprecated. please move it to %s manually", s, userdir);
-			warn_confd = FcTrue;
-		    }
-		}
-	    } else {
-		FcChar8 *parent = FcStrDirname (userconf);
-
-		if (!FcFileIsDir (parent))
-		    FcMakeDirectory (parent);
-		FcStrFree (parent);
-		if (FcFileIsFile (userconf) ||
-		    rename ((const char *)filename, (const char *)userconf) != 0 ||
-		    symlink ((const char *)userconf, (const char *)filename) != 0) {
-		    if (!warn_conf) {
-			FcConfigMessage (parse, FcSevereWarning, "reading configurations from %s is deprecated. please move it to %s manually", s, userconf);
-			warn_conf = FcTrue;
-		    }
-		}
-	    }
-	}
-	if (filename)
-	    FcStrFree (filename);
-    }
-#endif
     FcStrBufDestroy (&parse->pstack->str);
 
 bail:
commit d547d2cc11a7f38f5a8609a49aa8abb7cb46357b
Author: Akira TAGOH <akira at tagoh.org>
Date:   Thu May 8 16:20:21 2025 +0900

    test: do not free FcFontSet From FcConfigGetFonts

diff --git a/test/test-bz106618.c b/test/test-bz106618.c
index 575d9c8..14e7e87 100644
--- a/test/test-bz106618.c
+++ b/test/test-bz106618.c
@@ -28,7 +28,7 @@
 int
 main (int argc, char **argv)
 {
-    FcFontSet *fs = FcConfigGetFonts (NULL, FcSetSystem);
+    const FcFontSet *fs = FcConfigGetFonts (NULL, FcSetSystem);
     int        i;
 
     if (!fs)
@@ -41,7 +41,7 @@ main (int argc, char **argv)
 	    return 1;
 	printf ("%s\n", file);
     }
-    FcFontSetDestroy (fs);
+    FcFini();
 
     return 0;
 }
commit 90a84c477c4006b5539973a5487f28b3ed09711d
Author: Akira TAGOH <akira at tagoh.org>
Date:   Wed May 7 16:08:50 2025 +0900

    Drop FcDefaultFini() from FcFini() to fix memory leaks
    
    All relevant variables has been moved into FcConfig instance and
    to deal with this change, new APIs, FcConfigGetDefaultLangs(),
    FcConfigSetDefaultSubstitute() has been added. FcGetDefaultLangs()
    and FcDefaultSubstitute() still works and keep available for backward
    compatibility though, they are referring the default FcConfig instance
    from FcConfigGetCurrent() from now on.
    
    If a code creates multiple FcConfig instances, this change may affects.
    
    Changelog: changed

diff --git a/doc/fclangset.fncs b/doc/fclangset.fncs
index 401abc0..2839a53 100644
--- a/doc/fclangset.fncs
+++ b/doc/fclangset.fncs
@@ -164,9 +164,22 @@ has no matching language, this function returns FcLangDifferentLang.
 Returns a string set of the default languages according to the environment variables on the system.
 This function looks for them in order of FC_LANG, LC_ALL, LC_CTYPE and LANG then.
 If there are no valid values in those environment variables, "en" will be set as fallback.
+  </para><para>
+This function has been replaced by <function>FcConfigGetDefaultLangs</function> and will be deprecated.
 @SINCE@         2.9.91
 @@
 
+ at RET@           FcStrSet *
+ at FUNC@          FcConfigGetDefaultLangs
+ at TYPE1@         FcConfig *                      @ARG1@          config
+ at PURPOSE@       Get the default languages list
+ at DESC@
+Returns a string set of the default languages according to the environment variables on the system.
+This function looks for them in order of FC_LANG, LC_ALL, LC_CTYPE and LANG then.
+If there are no valid values in those environment variables, "en" will be set as fallback.
+ at SINCE@         2.17.0
+@@
+
 @RET@           FcStrSet *
 @FUNC@          FcLangSetGetLangs
 @TYPE1@         const FcLangSet *               @ARG1@          ls
diff --git a/doc/fcpattern.fncs b/doc/fcpattern.fncs
index d32f3dc..294562a 100644
--- a/doc/fcpattern.fncs
+++ b/doc/fcpattern.fncs
@@ -521,6 +521,31 @@ Patterns without a specified pixel size are given one computed from any
 specified point size (default 12), dpi (default 75) and scale (default 1).
 </para></listitem>
 </itemizedlist>
+  </para><para>
+This function has been replaced by <function>FcConfigSetDefaultSubstitute</function>
+and will be deprecated.
+@@
+
+ at RET@           void
+ at FUNC@          FcConfigSetDefaultSubstitute
+ at TYPE1@         FcConfig *                      @ARG1@          config
+ at TYPE2@         FcPattern *                     @ARG2@          pattern
+ at PURPOSE@       Perform default substitutions in a pattern
+ at DESC@
+Supplies default values for underspecified font patterns:
+<itemizedlist>
+<listitem><para>
+Patterns without a specified style or weight are set to Medium
+</para></listitem>
+<listitem><para>
+Patterns without a specified style or slant are set to Roman
+</para></listitem>
+<listitem><para>
+Patterns without a specified pixel size are given one computed from any
+specified point size (default 12), dpi (default 75) and scale (default 1).
+</para></listitem>
+</itemizedlist>
+ at SINCE@         2.17.0
 @@
 
 @RET@           FcPattern *
diff --git a/fc-match/fc-match.c b/fc-match/fc-match.c
index ceced5f..268b34c 100644
--- a/fc-match/fc-match.c
+++ b/fc-match/fc-match.c
@@ -183,7 +183,7 @@ main (int argc, char **argv)
 	return 1;
 
     FcConfigSubstitute (0, pat, FcMatchPattern);
-    FcDefaultSubstitute (pat);
+    FcConfigSetDefaultSubstitute (0, pat);
 
     fs = FcFontSetCreate();
 
diff --git a/fc-pattern/fc-pattern.c b/fc-pattern/fc-pattern.c
index 792d7da..8de2722 100644
--- a/fc-pattern/fc-pattern.c
+++ b/fc-pattern/fc-pattern.c
@@ -168,7 +168,7 @@ main (int argc, char **argv)
     if (do_config)
 	FcConfigSubstitute (0, pat, FcMatchPattern);
     if (do_default)
-	FcDefaultSubstitute (pat);
+	FcConfigSetDefaultSubstitute (0, pat);
 
     if (os) {
 	FcPattern *new;
diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 6326636..891b6d4 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -608,9 +608,16 @@ FcPublic void
 FcFontSetPrint (const FcFontSet *s);
 
 /* fcdefault.c */
+FcPublic FcStrSet *
+FcConfigGetDefaultLangs (FcConfig *config);
+
 FcPublic FcStrSet *
 FcGetDefaultLangs (void);
 
+FcPublic void
+FcConfigSetDefaultSubstitute (FcConfig  *config,
+                              FcPattern *pattern);
+
 FcPublic void
 FcDefaultSubstitute (FcPattern *pattern);
 
diff --git a/src/fccfg.c b/src/fccfg.c
index 16deaf2..0756698 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -202,6 +202,10 @@ FcConfigCreate (void)
     config->filter_func = NULL;
     config->filter_data = NULL;
     config->destroy_data_func = NULL;
+    config->default_lang = NULL;
+    config->default_langs = NULL;
+    config->prgname = NULL;
+    config->desktop_name = NULL;
 
     config->prefer_app_fonts = FcFalse;
 
@@ -383,6 +387,15 @@ FcConfigDestroy (FcConfig *config)
 	if (config->filter_data && config->destroy_data_func)
 	    config->destroy_data_func (config->filter_data);
 
+	if (config->default_lang)
+	    FcStrFree (config->default_lang);
+	if (config->default_langs)
+	    FcStrSetDestroy (config->default_langs);
+	if (config->prgname)
+	    FcStrFree (config->prgname);
+	if (config->desktop_name)
+	    FcStrFree (config->desktop_name);
+
 	free (config);
     }
 }
@@ -1935,7 +1948,7 @@ FcConfigSubstituteWithPat (FcConfig   *config,
 
     s = config->subst[kind];
     if (kind == FcMatchPattern) {
-	strs = FcGetDefaultLangs();
+	strs = FcConfigGetDefaultLangs (config);
 	if (strs) {
 	    FcStrList *l = FcStrListCreate (strs);
 	    FcChar8   *lang;
@@ -1982,7 +1995,7 @@ FcConfigSubstituteWithPat (FcConfig   *config,
 	    FcLangSetDestroy (lsund);
 	}
 	if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch) {
-	    FcChar8 *prgname = FcGetPrgname();
+	    FcChar8 *prgname = FcConfigGetPrgname (config);
 	    if (prgname)
 		FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
 	}
diff --git a/src/fcdefault.c b/src/fcdefault.c
index b3aed4d..f742fc1 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -48,11 +48,16 @@ static const struct {
 FcStrSet *default_langs;
 
 FcStrSet *
-FcGetDefaultLangs (void)
+FcConfigGetDefaultLangs (FcConfig *config)
 {
     FcStrSet *result;
+
+    if (!config) {
+	config = FcConfigGetCurrent();
+    }
+    FcConfigReference (config);
 retry:
-    result = (FcStrSet *)fc_atomic_ptr_get (&default_langs);
+    result = (FcStrSet *)fc_atomic_ptr_get (&config->default_langs);
     if (!result) {
 	char *langs;
 
@@ -80,45 +85,65 @@ retry:
 	    FcStrSetAdd (result, (const FcChar8 *)"en");
 
 	FcRefSetConst (&result->ref);
-	if (!fc_atomic_ptr_cmpexch (&default_langs, NULL, result)) {
+	if (!fc_atomic_ptr_cmpexch (&config->default_langs, NULL, result)) {
 	    FcRefInit (&result->ref, 1);
 	    FcStrSetDestroy (result);
 	    goto retry;
 	}
     }
+    FcConfigDestroy (config);
 
     return result;
 }
 
-static FcChar8 *default_lang; /* MT-safe */
+FcStrSet *
+FcGetDefaultLangs (void)
+{
+    return FcConfigGetDefaultLangs (NULL);
+}
 
 FcChar8 *
-FcGetDefaultLang (void)
+FcConfigGetDefaultLang (FcConfig *config)
 {
     FcChar8 *lang;
+
+    if (!config) {
+	config = FcConfigGetCurrent();
+    }
+    FcConfigReference (config);
 retry:
-    lang = fc_atomic_ptr_get (&default_lang);
+    lang = fc_atomic_ptr_get (&config->default_lang);
     if (!lang) {
-	FcStrSet *langs = FcGetDefaultLangs();
+	FcStrSet *langs = FcConfigGetDefaultLangs (config);
 	lang = FcStrdup (langs->strs[0]);
 
-	if (!fc_atomic_ptr_cmpexch (&default_lang, NULL, lang)) {
+	if (!fc_atomic_ptr_cmpexch (&config->default_lang, NULL, lang)) {
 	    free (lang);
 	    goto retry;
 	}
     }
+    FcConfigDestroy (config);
 
     return lang;
 }
 
-static FcChar8 *default_prgname;
+FcChar8 *
+FcGetDefaultLang (void)
+{
+    return FcConfigGetDefaultLang (NULL);
+}
 
 FcChar8 *
-FcGetPrgname (void)
+FcConfigGetPrgname (FcConfig *config)
 {
     FcChar8 *prgname;
+
+    if (!config) {
+	config = FcConfigGetCurrent();
+    }
+    FcConfigReference (config);
 retry:
-    prgname = fc_atomic_ptr_get (&default_prgname);
+    prgname = fc_atomic_ptr_get (&config->prgname);
     if (!prgname) {
 #ifdef _WIN32
 	char buf[MAX_PATH + 1];
@@ -197,26 +222,38 @@ retry:
 	    free (p);
 #endif
 
-	if (!fc_atomic_ptr_cmpexch (&default_prgname, NULL, prgname)) {
+	if (!fc_atomic_ptr_cmpexch (&config->prgname, NULL, prgname)) {
 	    free (prgname);
 	    goto retry;
 	}
     }
 
-    if (prgname && !prgname[0])
-	return NULL;
+    if (prgname && !prgname[0]) {
+	free (prgname);
+	prgname = NULL;
+    }
+    FcConfigDestroy (config);
 
     return prgname;
 }
 
-static FcChar8 *default_desktop_name;
+FcChar8 *
+FcGetPrgname (void)
+{
+    return FcConfigGetPrgname (NULL);
+}
 
 FcChar8 *
-FcGetDesktopName (void)
+FcConfigGetDesktopName (FcConfig *config)
 {
     FcChar8 *desktop_name;
+
+    if (!config) {
+	config = FcConfigGetCurrent();
+    }
+    FcConfigReference (config);
 retry:
-    desktop_name = fc_atomic_ptr_get (&default_desktop_name);
+    desktop_name = fc_atomic_ptr_get (&config->desktop_name);
     if (!desktop_name) {
 	char *s = getenv ("XDG_CURRENT_DESKTOP");
 
@@ -230,49 +267,28 @@ retry:
 	    return NULL;
 	}
 
-	if (!fc_atomic_ptr_cmpexch (&default_desktop_name, NULL, desktop_name)) {
+	if (!fc_atomic_ptr_cmpexch (&config->desktop_name, NULL, desktop_name)) {
 	    free (desktop_name);
 	    goto retry;
 	}
     }
-    if (desktop_name && !desktop_name[0])
-	return NULL;
+    if (desktop_name && !desktop_name[0]) {
+	desktop_name = NULL;
+    }
+    FcConfigDestroy (config);
 
     return desktop_name;
 }
 
-void
-FcDefaultFini (void)
+FcChar8 *
+FcGetDesktopName (void)
 {
-    FcChar8  *lang;
-    FcStrSet *langs;
-    FcChar8  *prgname;
-    FcChar8  *desktop;
-
-    lang = fc_atomic_ptr_get (&default_lang);
-    if (lang && fc_atomic_ptr_cmpexch (&default_lang, lang, NULL)) {
-	free (lang);
-    }
-
-    langs = fc_atomic_ptr_get (&default_langs);
-    if (langs && fc_atomic_ptr_cmpexch (&default_langs, langs, NULL)) {
-	FcRefInit (&langs->ref, 1);
-	FcStrSetDestroy (langs);
-    }
-
-    prgname = fc_atomic_ptr_get (&default_prgname);
-    if (prgname && fc_atomic_ptr_cmpexch (&default_prgname, prgname, NULL)) {
-	free (prgname);
-    }
-
-    desktop = fc_atomic_ptr_get (&default_desktop_name);
-    if (desktop && fc_atomic_ptr_cmpexch (&default_desktop_name, desktop, NULL)) {
-	free (desktop);
-    }
+    return FcConfigGetDesktopName (NULL);
 }
 
 void
-FcDefaultSubstitute (FcPattern *pattern)
+FcConfigSetDefaultSubstitute (FcConfig  *config,
+                              FcPattern *pattern)
 {
     FcPatternIter iter;
     FcValue       v, namelang, v2;
@@ -331,7 +347,7 @@ FcDefaultSubstitute (FcPattern *pattern)
 	FcPatternObjectAddInteger (pattern, FC_HINT_STYLE_OBJECT, FC_HINT_FULL);
 
     if (!FcPatternFindObjectIter (pattern, &iter, FC_NAMELANG_OBJECT))
-	FcPatternObjectAddString (pattern, FC_NAMELANG_OBJECT, FcGetDefaultLang());
+	FcPatternObjectAddString (pattern, FC_NAMELANG_OBJECT, FcConfigGetDefaultLang (config));
 
     /* shouldn't be failed. */
     FcPatternObjectGet (pattern, FC_NAMELANG_OBJECT, 0, &namelang);
@@ -362,13 +378,13 @@ FcDefaultSubstitute (FcPattern *pattern)
     }
 
     if (FcPatternObjectGet (pattern, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch) {
-	FcChar8 *prgname = FcGetPrgname();
+	FcChar8 *prgname = FcConfigGetPrgname (config);
 	if (prgname)
 	    FcPatternObjectAddString (pattern, FC_PRGNAME_OBJECT, prgname);
     }
 
     if (FcPatternObjectGet (pattern, FC_DESKTOP_NAME_OBJECT, 0, &v) == FcResultNoMatch) {
-	FcChar8 *desktop = FcGetDesktopName();
+	FcChar8 *desktop = FcConfigGetDesktopName (config);
 	if (desktop)
 	    FcPatternObjectAddString (pattern, FC_DESKTOP_NAME_OBJECT, desktop);
     }
@@ -376,6 +392,12 @@ FcDefaultSubstitute (FcPattern *pattern)
     if (!FcPatternFindObjectIter (pattern, &iter, FC_ORDER_OBJECT))
 	FcPatternObjectAddInteger (pattern, FC_ORDER_OBJECT, 0);
 }
+
+void
+FcDefaultSubstitute (FcPattern *pattern)
+{
+    FcConfigSetDefaultSubstitute (NULL, pattern);
+}
 #define __fcdefault__
 #include "fcaliastail.h"
 #undef __fcdefault__
diff --git a/src/fcinit.c b/src/fcinit.c
index 11afa28..361f80d 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -215,7 +215,6 @@ FcFini (void)
 {
     FcConfigFini();
     FcConfigPathFini();
-    FcDefaultFini();
     FcObjectFini();
     FcCacheFini();
 }
diff --git a/src/fcint.h b/src/fcint.h
index d3c2c5a..2dd0bf5 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -620,6 +620,11 @@ struct _FcConfig {
 
     FcBool prefer_app_fonts; /* Whether FcSetApplication has a priority than
                                 FcSetSystem for lookup */
+
+    FcChar8  *default_lang;  /* Primary language in default_langs */
+    FcStrSet *default_langs; /* String sets of the default languages */
+    FcChar8  *prgname;       /* Program name of current process */
+    FcChar8  *desktop_name;  /* Current desktop name */
 };
 
 typedef struct _FcFileTime {
@@ -943,17 +948,23 @@ FcPrivate void
 FcInitDebug (void);
 
 /* fcdefault.c */
+FcPrivate FcChar8 *
+FcConfigGetDefaultLang (FcConfig *config);
+
 FcPrivate FcChar8 *
 FcGetDefaultLang (void);
 
+FcPrivate FcChar8 *
+FcConfigGetPrgname (FcConfig *config);
+
 FcPrivate FcChar8 *
 FcGetPrgname (void);
 
 FcPrivate FcChar8 *
-FcGetDesktopName (void);
+FcConfigGetDesktopName (FcConfig *config);
 
-FcPrivate void
-FcDefaultFini (void);
+FcPrivate FcChar8 *
+FcGetDesktopName (void);
 
 /* fcdir.c */
 
diff --git a/src/fclist.c b/src/fclist.c
index bd672b8..c369ea9 100644
--- a/src/fclist.c
+++ b/src/fclist.c
@@ -491,7 +491,7 @@ FcFontSetList (FcConfig    *config,
 		FcChar8 *lang;
 
 		if (FcPatternObjectGetString (p, FC_NAMELANG_OBJECT, 0, &lang) != FcResultMatch) {
-		    lang = FcGetDefaultLang();
+		    lang = FcConfigGetDefaultLang (config);
 		}
 		if (!FcListAppend (&table, s->fonts[f], os, lang))
 		    goto bail1;
diff --git a/test/test-conf.c b/test/test-conf.c
index 54e5d70..725cd7f 100644
--- a/test/test-conf.c
+++ b/test/test-conf.c
@@ -455,7 +455,7 @@ run_test (FcConfig *config, json_object *root)
 		goto bail;
 	    }
 	    FcConfigSubstitute (config, query, FcMatchPattern);
-	    FcDefaultSubstitute (query);
+	    FcConfigSetDefaultSubstitute (config, query);
 	    match = FcFontMatch (config, query, &res);
 	    if (match) {
 		FcPatternIter iter;
diff --git a/test/test-crbug1004254.c b/test/test-crbug1004254.c
index 6970c4c..e14c2a4 100644
--- a/test/test-crbug1004254.c
+++ b/test/test-crbug1004254.c
@@ -42,7 +42,7 @@ run_query (void)
     FcPatternAddString (pat, FC_FAMILY, (const FcChar8 *)"sans-serif");
     FcPatternAddBool (pat, FC_SCALABLE, FcTrue);
     FcConfigSubstitute (NULL, pat, FcMatchPattern);
-    FcDefaultSubstitute (pat);
+    FcConfigSetDefaultSubstitute (NULL, pat);
     match = FcFontMatch (NULL, pat, &result);
     if (result != FcResultMatch || !match) {
 	fprintf (stderr, "ERROR: No matches found\n");
diff --git a/test/test-pthread.c b/test/test-pthread.c
index 41debad..6628d83 100644
--- a/test/test-pthread.c
+++ b/test/test-pthread.c
@@ -48,7 +48,7 @@ test_match (int thr_num, int test_num)
     pat = FcNameParse ((const FcChar8 *)"New Century Schoolbook");
 
     FcConfigSubstitute (0, pat, FcMatchPattern);
-    FcDefaultSubstitute (pat);
+    FcConfigSetDefaultSubstitute (0, pat);
 
     match = FcFontMatch (0, pat, &result);
 


More information about the Fontconfig mailing list