From bbc8fb5ba705e5257693f3b266fce12d2f81b50c Mon Sep 17 00:00:00 2001 From: Akira TAGOH Date: Thu, 29 Mar 2012 20:25:20 +0900 Subject: [PATCH] Bug 32853 - Export API to get the default language Add a new API FcGetDefaultLangs() to export the string sets of the default languages. --- doc/fclangset.fncs | 9 +++ fc-lang/fc-lang.c | 6 ++ fontconfig/fontconfig.h | 3 + src/fcdefault.c | 102 +++++++++----------------- src/fcint.h | 6 ++ src/fclang.c | 159 ++++++++++++++++++++++++++++++++++++++++ src/fcstr.c | 44 +++++++++++ 7 files changed, 260 insertions(+), 69 deletions(-) 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) {