From d209e108b190a06cdf85451200304db408cbfcf4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 7 Sep 2004 18:37:10 +0000 Subject: [PATCH] Add a function to return a list of applicable locale names. (#95587, 2004-09-07 Matthias Clasen * glib/gutils.h: * glib/gutils.c (g_get_language_names): Add a function to return a list of applicable locale names. (#95587, Hidetoshi Tajima) (guess_category_value, compute_locale_variants): (explode_locale, unalias_lang, read_aliases): Helper functions for g_get_language_names() * tests/testglib.c (main): Show the results of g_get_language_names() --- ChangeLog | 13 ++ ChangeLog.pre-2-10 | 13 ++ ChangeLog.pre-2-12 | 13 ++ ChangeLog.pre-2-6 | 13 ++ ChangeLog.pre-2-8 | 13 ++ docs/reference/ChangeLog | 4 + docs/reference/glib/glib-sections.txt | 3 +- glib/gutils.c | 291 ++++++++++++++++++++++++++++++++++ glib/gutils.h | 2 + tests/testglib.c | 2 + 10 files changed, 366 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 25ad832..e4a470c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2004-09-07 Matthias Clasen + + * glib/gutils.h: + * glib/gutils.c (g_get_language_names): Add a function to + return a list of applicable locale names. (#95587, + Hidetoshi Tajima) + (guess_category_value, compute_locale_variants): + (explode_locale, unalias_lang, read_aliases): Helper + functions for g_get_language_names() + + * tests/testglib.c (main): Show the results of + g_get_language_names() + Sun Sep 5 01:46:11 2004 Matthias Clasen * glib/glib.def: diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 25ad832..e4a470c 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,16 @@ +2004-09-07 Matthias Clasen + + * glib/gutils.h: + * glib/gutils.c (g_get_language_names): Add a function to + return a list of applicable locale names. (#95587, + Hidetoshi Tajima) + (guess_category_value, compute_locale_variants): + (explode_locale, unalias_lang, read_aliases): Helper + functions for g_get_language_names() + + * tests/testglib.c (main): Show the results of + g_get_language_names() + Sun Sep 5 01:46:11 2004 Matthias Clasen * glib/glib.def: diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 25ad832..e4a470c 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,16 @@ +2004-09-07 Matthias Clasen + + * glib/gutils.h: + * glib/gutils.c (g_get_language_names): Add a function to + return a list of applicable locale names. (#95587, + Hidetoshi Tajima) + (guess_category_value, compute_locale_variants): + (explode_locale, unalias_lang, read_aliases): Helper + functions for g_get_language_names() + + * tests/testglib.c (main): Show the results of + g_get_language_names() + Sun Sep 5 01:46:11 2004 Matthias Clasen * glib/glib.def: diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 25ad832..e4a470c 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,16 @@ +2004-09-07 Matthias Clasen + + * glib/gutils.h: + * glib/gutils.c (g_get_language_names): Add a function to + return a list of applicable locale names. (#95587, + Hidetoshi Tajima) + (guess_category_value, compute_locale_variants): + (explode_locale, unalias_lang, read_aliases): Helper + functions for g_get_language_names() + + * tests/testglib.c (main): Show the results of + g_get_language_names() + Sun Sep 5 01:46:11 2004 Matthias Clasen * glib/glib.def: diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 25ad832..e4a470c 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,16 @@ +2004-09-07 Matthias Clasen + + * glib/gutils.h: + * glib/gutils.c (g_get_language_names): Add a function to + return a list of applicable locale names. (#95587, + Hidetoshi Tajima) + (guess_category_value, compute_locale_variants): + (explode_locale, unalias_lang, read_aliases): Helper + functions for g_get_language_names() + + * tests/testglib.c (main): Show the results of + g_get_language_names() + Sun Sep 5 01:46:11 2004 Matthias Clasen * glib/glib.def: diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 34bd0a2..43f7cbd 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,7 @@ +2004-09-07 Matthias Clasen + + * glib/glib-sections.txt: Add g_get_language_names(). + Mon Sep 6 01:56:13 2004 Matthias Clasen * glib/tmpl/messages.sgml: Remove excess markup and fix a markup diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index f5eb2c2..89557b7 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2096,5 +2096,6 @@ _ Q_ N_ g_strip_context - + +g_get_language_names diff --git a/glib/gutils.c b/glib/gutils.c index 398dc75..2afa8d2 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #ifdef HAVE_PWD_H @@ -1520,6 +1521,296 @@ g_get_system_config_dirs (void) return (G_CONST_RETURN gchar * G_CONST_RETURN *) conf_dir_vector; } +static GHashTable *alias_table = NULL; + +/* read an alias file for the locales */ +static void +read_aliases (gchar *file) +{ + FILE *fp; + char buf[256]; + + if (!alias_table) + alias_table = g_hash_table_new (g_str_hash, g_str_equal); + fp = fopen (file,"r"); + if (!fp) + return; + while (fgets (buf, 256, fp)) + { + char *p, *q; + + g_strstrip (buf); + + /* Line is a comment */ + if ((buf[0] == '#') || (buf[0] == '\0')) + continue; + + /* Reads first column */ + for (p = buf, q = NULL; *p; p++) { + if ((*p == '\t') || (*p == ' ') || (*p == ':')) { + *p = '\0'; + q = p+1; + while ((*q == '\t') || (*q == ' ')) { + q++; + } + break; + } + } + /* The line only had one column */ + if (!q || *q == '\0') + continue; + + /* Read second column */ + for (p = q; *p; p++) { + if ((*p == '\t') || (*p == ' ')) { + *p = '\0'; + break; + } + } + + /* Add to alias table if necessary */ + if (!g_hash_table_lookup (alias_table, buf)) { + g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q)); + } + } + fclose (fp); +} + +static char * +unalias_lang (char *lang) +{ + char *p; + int i; + + if (!alias_table) + read_aliases ("/usr/share/locale/locale.alias"); + + i = 0; + while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0)) + { + lang = p; + if (i++ == 30) + { + static gboolean said_before = FALSE; + if (!said_before) + g_warning ("Too many alias levels for a locale, " + "may indicate a loop"); + said_before = TRUE; + return lang; + } + } + return lang; +} + +/* Mask for components of locale spec. The ordering here is from + * least significant to most significant + */ +enum +{ + COMPONENT_CODESET = 1 << 0, + COMPONENT_TERRITORY = 1 << 1, + COMPONENT_MODIFIER = 1 << 2 +}; + +/* Break an X/Open style locale specification into components + */ +static guint +explode_locale (const gchar *locale, + gchar **language, + gchar **territory, + gchar **codeset, + gchar **modifier) +{ + const gchar *uscore_pos; + const gchar *at_pos; + const gchar *dot_pos; + + guint mask = 0; + + uscore_pos = strchr (locale, '_'); + dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.'); + at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@'); + + if (at_pos) + { + mask |= COMPONENT_MODIFIER; + *modifier = g_strdup (at_pos); + } + else + at_pos = locale + strlen (locale); + + if (dot_pos) + { + mask |= COMPONENT_CODESET; + *codeset = g_strndup (dot_pos, at_pos - dot_pos); + } + else + dot_pos = at_pos; + + if (uscore_pos) + { + mask |= COMPONENT_TERRITORY; + *territory = g_strndup (uscore_pos, dot_pos - uscore_pos); + } + else + uscore_pos = dot_pos; + + *language = g_strndup (locale, uscore_pos - locale); + + return mask; +} + +/* + * Compute all interesting variants for a given locale name - + * by stripping off different components of the value. + * + * For simplicity, we assume that the locale is in + * X/Open format: language[_territory][.codeset][@modifier] + * + * TODO: Extend this to handle the CEN format (see the GNUlibc docs) + * as well. We could just copy the code from glibc wholesale + * but it is big, ugly, and complicated, so I'm reluctant + * to do so when this should handle 99% of the time... + */ +static GSList * +compute_locale_variants (const gchar *locale) +{ + GSList *retval = NULL; + + gchar *language; + gchar *territory; + gchar *codeset; + gchar *modifier; + + guint mask; + guint i; + + g_return_val_if_fail (locale != NULL, NULL); + + mask = explode_locale (locale, &language, &territory, &codeset, &modifier); + + /* Iterate through all possible combinations, from least attractive + * to most attractive. + */ + for (i = 0; i <= mask; i++) + if ((i & ~mask) == 0) + { + gchar *val = g_strconcat (language, + (i & COMPONENT_TERRITORY) ? territory : "", + (i & COMPONENT_CODESET) ? codeset : "", + (i & COMPONENT_MODIFIER) ? modifier : "", + NULL); + retval = g_slist_prepend (retval, val); + } + + g_free (language); + if (mask & COMPONENT_CODESET) + g_free (codeset); + if (mask & COMPONENT_TERRITORY) + g_free (territory); + if (mask & COMPONENT_MODIFIER) + g_free (modifier); + + return retval; +} + +/* The following is (partly) taken from the gettext package. + Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */ + +static const gchar * +guess_category_value (const gchar *category_name) +{ + const gchar *retval; + + /* The highest priority value is the `LANGUAGE' environment + variable. This is a GNU extension. */ + retval = g_getenv ("LANGUAGE"); + if ((retval != NULL) && (retval[0] != '\0')) + return retval; + + /* `LANGUAGE' is not set. So we have to proceed with the POSIX + methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some + systems this can be done by the `setlocale' function itself. */ + + /* Setting of LC_ALL overwrites all other. */ + retval = g_getenv ("LC_ALL"); + if ((retval != NULL) && (retval[0] != '\0')) + return retval; + + /* Next comes the name of the desired category. */ + retval = g_getenv (category_name); + if ((retval != NULL) && (retval[0] != '\0')) + return retval; + + /* Last possibility is the LANG environment variable. */ + retval = g_getenv ("LANG"); + if ((retval != NULL) && (retval[0] != '\0')) + return retval; + + return NULL; +} + +static gchar **languages = NULL; + +/** + * g_get_language_names: + * + * Computes a list of applicable locale names, which can be used to + * e.g. construct locale-dependent filenames or search paths. The returned + * list is sorted from most desirable to least desirable and always contains + * the default locale "C". + * + * For example, if LANGUAGE=de:en_US, then the returned list is + * "de", "en_US", "en", "C". + * + * This function consults the environment variables LANGUAGE, + * LC_ALL, LC_MESSAGES and LANG + * to find the list of locales specified by the user. + * + * Return value: a %NULL-terminated array of strings owned by GLib + * that must not be modified or freed. + * + * Since: 2.6 + **/ +G_CONST_RETURN gchar * G_CONST_RETURN * +g_get_language_names () +{ + G_LOCK (g_utils_global); + + if (!languages) + { + const gchar *value; + gchar **alist, **a; + GSList *list, *l; + gint i; + + value = guess_category_value ("LC_MESSAGES"); + if (!value) + value = "C"; + + alist = g_strsplit (value, ":", 0); + list = NULL; + for (a = alist; *a; a++) + { + gchar *b = unalias_lang (*a); + list = g_slist_concat (list, compute_locale_variants (b)); + } + g_strfreev (alist); + list = g_slist_append (list, "C"); + + languages = g_new (gchar *, g_slist_length (list) + 1); + for (l = list, i = 0; l; l = l->next, i++) + languages[i] = l->data; + languages[i] = NULL; + + g_slist_free (list); + } + + G_UNLOCK (g_utils_global); + + return (G_CONST_RETURN gchar * G_CONST_RETURN *) languages; +} + guint g_direct_hash (gconstpointer v) { diff --git a/glib/gutils.h b/glib/gutils.h index 73084af..c8ba172 100644 --- a/glib/gutils.h +++ b/glib/gutils.h @@ -128,6 +128,8 @@ G_CONST_RETURN gchar* g_get_user_cache_dir (void); G_CONST_RETURN gchar * G_CONST_RETURN * g_get_system_data_dirs (void); G_CONST_RETURN gchar * G_CONST_RETURN * g_get_system_config_dirs (void); +G_CONST_RETURN gchar * G_CONST_RETURN * g_get_language_names (void); + typedef struct _GDebugKey GDebugKey; struct _GDebugKey { diff --git a/tests/testglib.c b/tests/testglib.c index 83ee2cf..e8f9f8a 100644 --- a/tests/testglib.c +++ b/tests/testglib.c @@ -451,6 +451,8 @@ main (int argc, sv = (gchar **) g_get_system_config_dirs (); g_print ("system_config: %s\n", s ? g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv) : "NULL!"); g_print ("tmp-dir: %s\n", g_get_tmp_dir ()); + sv = (gchar **) g_get_language_names (); + g_print ("languages: %s\n", s ? g_strjoinv (":", sv) : "NULL!"); /* type sizes */ g_print ("checking size of gint8: %" G_GSIZE_FORMAT, sizeof (gint8)); -- 2.7.4