user-accounts: Update gdm-languages.c from GDM

Fixes a number of bugs, and keeps the 2 versions in sync.
This commit is contained in:
Bastien Nocera 2011-01-21 13:51:00 +00:00
parent fdd13a9f83
commit 0bbdc8727f

View file

@ -45,8 +45,9 @@
#endif #endif
#include "locarchive.h" #include "locarchive.h"
#define ALIASES_FILE LIBLOCALEDIR "/locale.alias" #define ALIASES_FILE DATADIR "/gdm/locale.alias"
#define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive" #define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive"
#define SYSTEM_ARCHIVE_FILE "/usr/lib/locale/locale-archive"
#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes" #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale" #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
@ -63,6 +64,13 @@ static GHashTable *gdm_languages_map;
static GHashTable *gdm_territories_map; static GHashTable *gdm_territories_map;
static GHashTable *gdm_available_locales_map; static GHashTable *gdm_available_locales_map;
static char * construct_language_name (const char *language,
const char *territory,
const char *codeset,
const char *modifier);
static gboolean language_name_is_valid (const char *language_name);
static void static void
gdm_locale_free (GdmLocale *locale) gdm_locale_free (GdmLocale *locale)
{ {
@ -119,6 +127,12 @@ gdm_parse_language_name (const char *name,
GMatchInfo *match_info; GMatchInfo *match_info;
gboolean res; gboolean res;
GError *error; GError *error;
gchar *normalized_codeset = NULL;
gchar *normalized_name = NULL;
gboolean retval;
match_info = NULL;
retval = FALSE;
error = NULL; error = NULL;
re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)" re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)"
@ -127,26 +141,24 @@ gdm_parse_language_name (const char *name,
"(@(?P<modifier>[[:ascii:]]+))?$", "(@(?P<modifier>[[:ascii:]]+))?$",
0, 0, &error); 0, 0, &error);
if (re == NULL) { if (re == NULL) {
g_critical ("%s", error->message); g_warning ("%s", error->message);
return FALSE; goto out;
} }
if (!g_regex_match (re, name, 0, &match_info) || if (!g_regex_match (re, name, 0, &match_info) ||
g_match_info_is_partial_match (match_info)) { g_match_info_is_partial_match (match_info)) {
g_match_info_free (match_info);
g_regex_unref (re);
g_warning ("locale %s isn't valid\n", name); g_warning ("locale %s isn't valid\n", name);
return FALSE; goto out;
} }
res = g_match_info_matches (match_info); res = g_match_info_matches (match_info);
if (! res) { if (! res) {
g_match_info_free (match_info);
g_regex_unref (re);
g_warning ("Unable to parse locale: %s", name); g_warning ("Unable to parse locale: %s", name);
return FALSE; goto out;
} }
retval = TRUE;
if (language_codep != NULL) { if (language_codep != NULL) {
*language_codep = g_match_info_fetch_named (match_info, "language"); *language_codep = g_match_info_fetch_named (match_info, "language");
} }
@ -169,14 +181,6 @@ gdm_parse_language_name (const char *name,
g_free (*codesetp); g_free (*codesetp);
*codesetp = NULL; *codesetp = NULL;
} }
if (*codesetp != NULL) {
char *codeset;
codeset = normalize_codeset (*codesetp);
g_free (*codesetp);
*codesetp = codeset;
}
} }
if (modifierp != NULL) { if (modifierp != NULL) {
@ -189,10 +193,27 @@ gdm_parse_language_name (const char *name,
} }
} }
if (codesetp != NULL && *codesetp != NULL) {
normalized_codeset = normalize_codeset (*codesetp);
normalized_name = construct_language_name (language_codep ? *language_codep : NULL,
territory_codep ? *territory_codep : NULL,
normalized_codeset,
modifierp ? *modifierp : NULL);
if (language_name_is_valid (normalized_name)) {
g_free (*codesetp);
*codesetp = normalized_codeset;
} else {
g_free (normalized_codeset);
}
g_free (normalized_name);
}
out:
g_match_info_free (match_info); g_match_info_free (match_info);
g_regex_unref (re); g_regex_unref (re);
return TRUE; return retval;
} }
static char * static char *
@ -220,24 +241,6 @@ construct_language_name (const char *language,
return name; return name;
} }
static void
make_codeset_canonical_for_locale (const char *name,
char **codeset)
{
char *old_locale;
old_locale = setlocale (LC_CTYPE, NULL);
if (setlocale (LC_CTYPE, name) == NULL) {
return;
}
g_free (*codeset);
*codeset = g_strdup (nl_langinfo (CODESET));
setlocale (LC_CTYPE, old_locale);
}
char * char *
gdm_normalize_language_name (const char *name) gdm_normalize_language_name (const char *name)
{ {
@ -256,10 +259,6 @@ gdm_normalize_language_name (const char *name)
&territory_code, &territory_code,
&codeset, &modifier); &codeset, &modifier);
if (codeset != NULL) {
make_codeset_canonical_for_locale (name, &codeset);
}
normalized_name = construct_language_name (language_code, normalized_name = construct_language_name (language_code,
territory_code, territory_code,
codeset, modifier); codeset, modifier);
@ -276,38 +275,50 @@ language_name_is_valid (const char *language_name)
{ {
char *old_locale; char *old_locale;
gboolean is_valid; gboolean is_valid;
#ifdef WITH_INCOMPLETE_LOCALES
int lc_type_id = LC_CTYPE;
#else
int lc_type_id = LC_MESSAGES;
#endif
old_locale = g_strdup (setlocale (LC_MESSAGES, NULL)); old_locale = g_strdup (setlocale (lc_type_id, NULL));
is_valid = setlocale (LC_MESSAGES, language_name) != NULL; is_valid = setlocale (lc_type_id, language_name) != NULL;
setlocale (LC_MESSAGES, old_locale); setlocale (lc_type_id, old_locale);
g_free (old_locale); g_free (old_locale);
return is_valid; return is_valid;
} }
static gboolean static void
language_name_is_utf8 (const char *language_name) language_name_get_codeset_details (const char *language_name,
char **pcodeset,
gboolean *is_utf8)
{ {
char *old_locale; char *old_locale;
char *codeset; char *codeset;
gboolean is_utf8;
old_locale = g_strdup (setlocale (LC_CTYPE, NULL)); old_locale = g_strdup (setlocale (LC_CTYPE, NULL));
if (setlocale (LC_CTYPE, language_name) == NULL) { if (setlocale (LC_CTYPE, language_name) == NULL) {
g_free (old_locale); g_free (old_locale);
return FALSE; return;
} }
codeset = normalize_codeset (nl_langinfo (CODESET)); codeset = nl_langinfo (CODESET);
is_utf8 = strcmp (codeset, "utf8") == 0; if (pcodeset != NULL) {
g_free (codeset); *pcodeset = g_strdup (codeset);
}
if (is_utf8 != NULL) {
codeset = normalize_codeset (codeset);
*is_utf8 = strcmp (codeset, "utf8") == 0;
g_free (codeset);
}
setlocale (LC_CTYPE, old_locale); setlocale (LC_CTYPE, old_locale);
g_free (old_locale); g_free (old_locale);
return is_utf8;
} }
static gboolean static gboolean
@ -347,29 +358,38 @@ out:
} }
static gboolean static gboolean
add_locale (const char *language_name) add_locale (const char *language_name,
gboolean utf8_only)
{ {
GdmLocale *locale; GdmLocale *locale;
GdmLocale *old_locale; GdmLocale *old_locale;
char *name; char *name;
gboolean is_utf8;
if (language_name_is_utf8 (language_name)) { g_return_val_if_fail (language_name != NULL, FALSE);
language_name_get_codeset_details (language_name, NULL, &is_utf8);
if (is_utf8) {
name = g_strdup (language_name); name = g_strdup (language_name);
} else { } else if (utf8_only) {
name = g_strdup_printf ("%s.utf8", language_name); name = g_strdup_printf ("%s.utf8", language_name);
if (!language_name_is_utf8 (name)) { language_name_get_codeset_details (name, NULL, &is_utf8);
if (!is_utf8) {
g_free (name); g_free (name);
return FALSE; return FALSE;
} }
} else {
name = g_strdup (language_name);
} }
if (!language_name_is_valid (name)) { if (!language_name_is_valid (name)) {
g_debug ("Ignoring '%s' as a locale, since it's invalid", name);
g_free (name); g_free (name);
return FALSE; return FALSE;
} }
locale = g_new0 (GdmLocale, 1); locale = g_new0 (GdmLocale, 1);
gdm_parse_language_name (name, gdm_parse_language_name (name,
&locale->language_code, &locale->language_code,
@ -379,17 +399,35 @@ add_locale (const char *language_name)
g_free (name); g_free (name);
name = NULL; name = NULL;
#ifdef WITH_INCOMPLETE_LOCALES
if (utf8_only) {
if (locale->territory_code == NULL || locale->modifier) {
gdm_locale_free (locale);
return FALSE;
}
}
#endif
locale->id = construct_language_name (locale->language_code, locale->territory_code, locale->id = construct_language_name (locale->language_code, locale->territory_code,
NULL, locale->modifier); NULL, locale->modifier);
locale->name = construct_language_name (locale->language_code, locale->territory_code, locale->name = construct_language_name (locale->language_code, locale->territory_code,
locale->codeset, locale->modifier); locale->codeset, locale->modifier);
#ifndef WITH_INCOMPLETE_LOCALES
if (!language_name_has_translations (locale->name) && if (!language_name_has_translations (locale->name) &&
!language_name_has_translations (locale->id) && !language_name_has_translations (locale->id) &&
!language_name_has_translations (locale->language_code)) { !language_name_has_translations (locale->language_code) &&
utf8_only) {
g_debug ("Ignoring '%s' as a locale, since it lacks translations", locale->name);
gdm_locale_free (locale); gdm_locale_free (locale);
return FALSE; return FALSE;
} }
#endif
if (!utf8_only) {
g_free (locale->id);
locale->id = g_strdup (locale->name);
}
old_locale = g_hash_table_lookup (gdm_available_locales_map, locale->id); old_locale = g_hash_table_lookup (gdm_available_locales_map, locale->id);
if (old_locale != NULL) { if (old_locale != NULL) {
@ -427,9 +465,13 @@ collect_locales_from_archive (void)
error = NULL; error = NULL;
mapped = g_mapped_file_new (ARCHIVE_FILE, FALSE, &error); mapped = g_mapped_file_new (ARCHIVE_FILE, FALSE, &error);
if (mapped == NULL) { if (mapped == NULL) {
g_warning ("Mapping failed for %s: %s", ARCHIVE_FILE, error->message); mapped = g_mapped_file_new (SYSTEM_ARCHIVE_FILE, FALSE, NULL);
if (mapped == NULL) {
g_warning ("Mapping failed for %s: %s", ARCHIVE_FILE, error->message);
g_error_free (error);
return FALSE;
}
g_error_free (error); g_error_free (error);
return FALSE;
} }
locales_collected = FALSE; locales_collected = FALSE;
@ -456,7 +498,7 @@ collect_locales_from_archive (void)
} }
for (cnt = 0; cnt < used; ++cnt) { for (cnt = 0; cnt < used; ++cnt) {
add_locale (names[cnt].name); add_locale (names[cnt].name, TRUE);
} }
g_free (names); g_free (names);
@ -508,7 +550,7 @@ collect_locales_from_directory (void)
ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort); ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort);
for (cnt = 0; cnt < ndirents; ++cnt) { for (cnt = 0; cnt < ndirents; ++cnt) {
add_locale (dirents[cnt]->d_name); add_locale (dirents[cnt]->d_name, TRUE);
} }
if (ndirents > 0) { if (ndirents > 0) {
@ -516,6 +558,59 @@ collect_locales_from_directory (void)
} }
} }
static void
collect_locales_from_locale_file (const char *locale_file)
{
FILE *langlist;
char curline[256];
char *getsret;
if (locale_file == NULL)
return;
langlist = fopen (locale_file, "r");
if (langlist == NULL)
return;
for (;;) {
char *name;
char *lang;
char **lang_list;
int i;
getsret = fgets (curline, sizeof (curline), langlist);
if (getsret == NULL)
break;
if (curline[0] <= ' ' ||
curline[0] == '#')
continue;
name = strtok (curline, " \t\r\n");
if (name == NULL)
continue;
lang = strtok (NULL, " \t\r\n");
if (lang == NULL)
continue;
lang_list = g_strsplit (lang, ",", -1);
if (lang_list == NULL)
continue;
lang = NULL;
for (i = 0; lang_list[i] != NULL; i++) {
if (add_locale (lang_list[i], FALSE)) {
break;
}
}
g_strfreev (lang_list);
}
fclose (langlist);
}
static void static void
collect_locales (void) collect_locales (void)
{ {
@ -524,15 +619,16 @@ collect_locales (void)
gdm_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free); gdm_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free);
} }
if (collect_locales_from_archive ()) { if (!collect_locales_from_archive ()) {
return; #ifndef WITH_INCOMPLETE_LOCALES
} else {
g_warning ("Could not read list of available locales from libc, " g_warning ("Could not read list of available locales from libc, "
"guessing possible locales from available translations, " "guessing possible locales from available translations, "
"but list may be incomplete!"); "but list may be incomplete!");
#endif
collect_locales_from_directory (); collect_locales_from_directory ();
} }
collect_locales_from_locale_file (ALIASES_FILE);
} }
static gboolean static gboolean
@ -610,7 +706,7 @@ get_translated_language (const char *code,
} }
if (is_fallback_language (code)) { if (is_fallback_language (code)) {
name = _("Unspecified"); name = g_strdup (_("Unspecified"));
} else { } else {
translated_name = dgettext ("iso_639", language); translated_name = dgettext ("iso_639", language);
name = get_first_item_in_semicolon_list (translated_name); name = get_first_item_in_semicolon_list (translated_name);
@ -643,12 +739,12 @@ get_territory (const char *code)
return name; return name;
} }
static const char * static char *
get_translated_territory (const char *code, get_translated_territory (const char *code,
const char *locale) const char *locale)
{ {
const char *territory; const char *territory;
char *name; char *name;
territory = get_territory (code); territory = get_territory (code);
@ -929,11 +1025,20 @@ char *
gdm_get_language_from_name (const char *name, gdm_get_language_from_name (const char *name,
const char *locale) const char *locale)
{ {
char *full_language; GString *full_language;
char *language_code; char *language_code;
char *territory_code; char *territory_code;
const char *language; char *codeset_code;
const char *territory; char *langinfo_codeset;
char *translated_language;
char *translated_territory;
gboolean is_utf8 = TRUE;
translated_territory = NULL;
translated_language = NULL;
langinfo_codeset = NULL;
full_language = g_string_new (NULL);
if (gdm_languages_map == NULL) { if (gdm_languages_map == NULL) {
languages_init (); languages_init ();
@ -945,36 +1050,60 @@ gdm_get_language_from_name (const char *name,
language_code = NULL; language_code = NULL;
territory_code = NULL; territory_code = NULL;
full_language = NULL; codeset_code = NULL;
gdm_parse_language_name (name, &language_code, &territory_code, gdm_parse_language_name (name,
NULL, NULL); &language_code,
&territory_code,
&codeset_code,
NULL);
if (language_code == NULL) { if (language_code == NULL) {
goto out; goto out;
} }
language = get_translated_language (language_code, locale); translated_language = get_translated_language (language_code, locale);
if (translated_language == NULL) {
if (territory_code != NULL) { goto out;
territory = get_translated_territory (territory_code, locale);
} else {
territory = NULL;
} }
if (territory != NULL) { full_language = g_string_append (full_language, translated_language);
full_language = g_strdup_printf ("%s (%s)",
language ? language : "", if (territory_code != NULL) {
territory ? territory : ""); translated_territory = get_translated_territory (territory_code, locale);
} else { }
full_language = g_strdup (language); if (translated_territory != NULL) {
g_string_append_printf (full_language,
" (%s)",
translated_territory);
}
language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
if (codeset_code == NULL && langinfo_codeset != NULL) {
codeset_code = g_strdup (langinfo_codeset);
}
if (!is_utf8 && codeset_code) {
g_string_append_printf (full_language,
" [%s]",
codeset_code);
} }
out: out:
g_free (language_code); g_free (language_code);
g_free (territory_code); g_free (territory_code);
g_free (codeset_code);
g_free (langinfo_codeset);
g_free (translated_language);
g_free (translated_territory);
return full_language; if (full_language->len == 0) {
g_string_free (full_language, TRUE);
return NULL;
}
return g_string_free (full_language, FALSE);
} }
char ** char **