diff --git a/doc/fclangset.fncs b/doc/fclangset.fncs
index 0a44b38..e2a40b8 100644
--- a/doc/fclangset.fncs
+++ b/doc/fclangset.fncs
@@ -153,6 +153,15 @@ function returns FcLangDifferentTerritory. If ls
has no matching language, this function returns FcLangDifferentLang.
@@
+@RET@ FcStrSet *
+@FUNC@ FcGetDefaultLangs
+@TYPE1@ void
+@PURPOSE@ Get the default languages list
+@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.
+
@RET@ FcStrSet *
@FUNC@ FcLangSetGetLangs
@TYPE1@ const FcLangSet * @ARG1@ ls
diff --git a/fc-lang/fc-lang.c b/fc-lang/fc-lang.c
index 51717f9..93200c4 100644
--- a/fc-lang/fc-lang.c
+++ b/fc-lang/fc-lang.c
@@ -57,6 +57,12 @@ FcCacheObjectDereference (void *object)
{
}
+FcPrivate FcChar8 *
+FcLangNormalize (const FcChar8 *lang)
+{
+ return NULL;
+}
+
int FcDebugVal;
FcChar8 *
diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 2671da7..21a476a 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -497,6 +497,9 @@ FcPublic void
FcFontSetPrint (const FcFontSet *s);
/* fcdefault.c */
+FcPublic FcStrSet *
+FcGetDefaultLangs (void);
+
FcPublic void
FcDefaultSubstitute (FcPattern *pattern);
diff --git a/src/fcdefault.c b/src/fcdefault.c
index 170a8a4..674374c 100644
--- a/src/fcdefault.c
+++ b/src/fcdefault.c
@@ -23,7 +23,7 @@
*/
#include "fcint.h"
-#include
+#include
static const struct {
FcObject field;
@@ -39,81 +39,45 @@ static const struct {
#define NUM_FC_BOOL_DEFAULTS (int) (sizeof FcBoolDefaults / sizeof FcBoolDefaults[0])
+FcStrSet *
+FcGetDefaultLangs (void)
+{
+ FcStrSet *result = FcStrSetCreate ();
+ char *langs;
+
+ langs = getenv ("FC_LANG");
+ if (!langs)
+ langs = getenv ("LC_ALL");
+ if (!langs)
+ langs = getenv ("LC_CTYPE");
+ if (!langs)
+ langs = getenv ("LANG");
+ if (langs)
+ {
+ if (!FcStrSetAddLangs (result, langs))
+ FcStrSetAdd (result, (const FcChar8 *) "en");
+ }
+ else
+ FcStrSetAdd (result, (const FcChar8 *) "en");
+
+ return result;
+}
+
FcChar8 *
FcGetDefaultLang (void)
{
- static char lang_local [128] = {0};
- char *ctype;
- char *territory;
- char *after;
- int lang_len, territory_len;
+ static FcChar8 lang_local[128] = {0};
+ FcStrSet *langs;
- if (lang_local [0])
- return (FcChar8 *) lang_local;
-
- ctype = setlocale (LC_CTYPE, NULL);
-
- /*
- * Check if setlocale (LC_ALL, "") has been called
- */
- if (!ctype || !strcmp (ctype, "C"))
+ if (!lang_local[0])
{
- ctype = getenv ("LC_ALL");
- if (!ctype)
- {
- ctype = getenv ("LC_CTYPE");
- if (!ctype)
- ctype = getenv ("LANG");
- }
+ langs = FcGetDefaultLangs ();
+ strncpy ((char *)lang_local, (const char *)langs->strs[0], 127);
+ lang_local[127] = 0;
+ FcStrSetDestroy (langs);
}
- /* ignore missing or empty ctype */
- if (ctype && *ctype != '\0')
- {
- territory = strchr (ctype, '_');
- if (territory)
- {
- lang_len = territory - ctype;
- territory = territory + 1;
- after = strchr (territory, '.');
- if (!after)
- {
- after = strchr (territory, '@');
- if (!after)
- after = territory + strlen (territory);
- }
- territory_len = after - territory;
- if (lang_len + 1 + territory_len + 1 <= (int) sizeof (lang_local))
- {
- strncpy (lang_local, ctype, lang_len);
- lang_local[lang_len] = '-';
- strncpy (lang_local + lang_len + 1, territory, territory_len);
- lang_local[lang_len + 1 + territory_len] = '\0';
- }
- }
- else
- {
- after = strchr (ctype, '.');
- if (!after)
- {
- after = strchr (ctype, '@');
- if (!after)
- after = ctype + strlen (ctype);
- }
- lang_len = after - ctype;
- if (lang_len + 1 <= (int) sizeof (lang_local))
- {
- strncpy (lang_local, ctype, lang_len);
- lang_local[lang_len] = '\0';
- }
- }
- }
-
- /* set default lang to en */
- if (!lang_local [0])
- strcpy (lang_local, "en");
-
- return (FcChar8 *) lang_local;
+ return lang_local;
}
void
diff --git a/src/fcint.h b/src/fcint.h
index 3d06fc6..7cc4ed2 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -816,6 +816,9 @@ FcPrivate FcLangSet *
FcFreeTypeLangSet (const FcCharSet *charset,
const FcChar8 *exclusiveLang);
+FcPrivate FcChar8 *
+FcLangNormalize (const FcChar8 *lang);
+
FcPrivate FcLangResult
FcLangCompare (const FcChar8 *s1, const FcChar8 *s2);
@@ -1039,6 +1042,9 @@ FcPrivate FcBool
FcIsFsMtimeBroken (const FcChar8 *dir);
/* fcstr.c */
+FcPrivate FcBool
+FcStrSetAddLangs (FcStrSet *strs, const char *languages);
+
FcPrivate void
FcStrSetSort (FcStrSet * set);
diff --git a/src/fclang.c b/src/fclang.c
index be42b58..b7e70fc 100644
--- a/src/fclang.c
+++ b/src/fclang.c
@@ -22,6 +22,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
+#include
#include "fcint.h"
#include "fcftint.h"
@@ -43,6 +44,9 @@ struct _FcLangSet {
FcChar32 map[NUM_LANG_SET_MAP];
};
+static int FcLangSetIndex (const FcChar8 *lang);
+
+
static void
FcLangSetBitSet (FcLangSet *ls,
unsigned int id)
@@ -173,6 +177,161 @@ FcFreeTypeLangSet (const FcCharSet *charset,
return ls;
}
+FcChar8 *
+FcLangNormalize (const FcChar8 *lang)
+{
+ FcChar8 *result = NULL, *s, *orig;
+ char *territory, *encoding, *modifier;
+ size_t llen, tlen = 0, mlen = 0;
+
+ if (!lang || !*lang)
+ return NULL;
+
+ if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
+ FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
+ {
+ result = FcStrCopy ((const FcChar8 *)"en");
+ goto bail;
+ }
+
+ s = FcStrCopy (lang);
+ if (!s)
+ goto bail;
+
+ /* from the comments in glibc:
+ *
+ * LOCALE can consist of up to four recognized parts for the XPG syntax:
+ *
+ * language[_territory[.codeset]][@modifier]
+ *
+ * Beside the first all of them are allowed to be missing. If the
+ * full specified locale is not found, the less specific one are
+ * looked for. The various part will be stripped off according to
+ * the following order:
+ * (1) codeset
+ * (2) normalized codeset
+ * (3) territory
+ * (4) modifier
+ *
+ * So since we don't take care of the codeset part here, what patterns
+ * we need to deal with is:
+ *
+ * 1. language_territory@modifier
+ * 2. language@modifier
+ * 3. language
+ *
+ * then. and maybe no need to try language_territory here.
+ */
+ modifier = strchr ((const char *) s, '@');
+ if (modifier)
+ {
+ *modifier = 0;
+ modifier++;
+ mlen = strlen (modifier);
+ }
+ encoding = strchr ((const char *) s, '.');
+ if (encoding)
+ {
+ *encoding = 0;
+ encoding++;
+ if (modifier)
+ {
+ memmove (encoding, modifier, mlen + 1);
+ modifier = encoding;
+ }
+ }
+ territory = strchr ((const char *) s, '_');
+ if (!territory)
+ territory = strchr ((const char *) s, '-');
+ if (territory)
+ {
+ *territory = 0;
+ territory++;
+ tlen = strlen (territory);
+ }
+ llen = strlen ((const char *) s);
+ if (llen < 2 || llen > 3)
+ {
+ fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
+ lang);
+ goto bail0;
+ }
+ if (territory && (tlen < 2 || tlen > 3))
+ {
+ fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
+ lang);
+ goto bail0;
+ }
+ if (territory)
+ territory[-1] = '-';
+ if (modifier)
+ modifier[-1] = '@';
+ orig = FcStrDowncase (s);
+ if (!orig)
+ goto bail0;
+ if (territory)
+ {
+ if (FcDebug () & FC_DBG_LANGSET)
+ printf("Checking the existence of %s.orth\n", s);
+ if (FcLangSetIndex (s) < 0)
+ {
+ memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
+ if (modifier)
+ modifier = territory;
+ }
+ else
+ {
+ result = s;
+ s = NULL;
+ goto bail1;
+ }
+ }
+ if (modifier)
+ {
+ if (FcDebug () & FC_DBG_LANGSET)
+ printf("Checking the existence of %s.orth\n", s);
+ if (FcLangSetIndex (s) < 0)
+ modifier[-1] = 0;
+ else
+ {
+ result = s;
+ s = NULL;
+ goto bail1;
+ }
+ }
+ if (FcDebug () & FC_DBG_LANGSET)
+ printf("Checking the existence of %s.orth\n", s);
+ if (FcLangSetIndex (s) < 0)
+ {
+ /* there seems no languages matched in orth.
+ * add the language as is for fallback.
+ */
+ result = orig;
+ orig = NULL;
+ }
+ else
+ {
+ result = s;
+ s = NULL;
+ }
+ bail1:
+ if (orig)
+ free (orig);
+ bail0:
+ if (s)
+ free (s);
+ bail:
+ if (FcDebug () & FC_DBG_LANGSET)
+ {
+ if (result)
+ printf ("normalized: %s -> %s\n", lang, result);
+ else
+ printf ("Unable to normalize %s\n", lang);
+ }
+
+ return result;
+}
+
#define FcLangEnd(c) ((c) == '-' || (c) == '\0')
FcLangResult
diff --git a/src/fcstr.c b/src/fcstr.c
index e372af0..c446bcd 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -1176,6 +1176,50 @@ FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s)
return FcTrue;
}
+FcBool
+FcStrSetAddLangs (FcStrSet *strs, const char *languages)
+{
+ const char *p = languages, *next;
+ FcChar8 lang[128] = {0}, *normalized_lang;
+ size_t len;
+ FcBool ret = FcFalse;
+
+ if (!languages)
+ return FcFalse;
+
+ while ((next = strchr (p, ':')))
+ {
+ len = next - p;
+ len = FC_MIN (len, 128);
+ strncpy ((char *) lang, p, len);
+ lang[len] = 0;
+ /* ignore an empty item */
+ if (*lang)
+ {
+ normalized_lang = FcLangNormalize ((const FcChar8 *) lang);
+ if (normalized_lang)
+ {
+ FcStrSetAdd (strs, normalized_lang);
+ free (normalized_lang);
+ ret = FcTrue;
+ }
+ }
+ p = next + 1;
+ }
+ if (*p)
+ {
+ normalized_lang = FcLangNormalize ((const FcChar8 *) p);
+ if (normalized_lang)
+ {
+ FcStrSetAdd (strs, normalized_lang);
+ free (normalized_lang);
+ ret = FcTrue;
+ }
+ }
+
+ return ret;
+}
+
FcBool
FcStrSetDel (FcStrSet *set, const FcChar8 *s)
{