[gspell] language: replace implementation by the GtkHtml one



commit f3f683328573e2af66101ec63f4f4bebee221f41
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Thu Dec 10 18:19:40 2015 +0100

    language: replace implementation by the GtkHtml one
    
    It permits to do two things:
    - get rid of the libxml2 dependency.
    - re-license the code to LGPLv2.1+ (the other files will be re-licensed
      too).

 README                                 |    5 +-
 configure.ac                           |    3 +-
 docs/reference/gspell-1.0-sections.txt |    1 +
 gspell/gspell-language.c               |  695 ++++++++++++++++----------------
 gspell/gspell-language.h               |   43 ++-
 5 files changed, 369 insertions(+), 378 deletions(-)
---
diff --git a/README b/README
index 2634976..cff0578 100644
--- a/README
+++ b/README
@@ -60,11 +60,10 @@ Dependencies
 * GLib >= 2.44
 * GTK+ >= 3.16
 * GtkSourceView >= 3.16
-* libxml2
 * iso-codes
 
-It is planned to remove the dependencies to GtkSourceView and libxml2 in a
-future version of gspell.
+It is planned to remove the dependency to GtkSourceView in a future version of
+gspell.
 
 
 Installation from a tarball
diff --git a/configure.ac b/configure.ac
index 54c00b9..ff96b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,7 +38,6 @@ iso_codes_req=0.35
 glib_req=2.44
 gtk_req=3.16
 gtksourceview_req=3.16
-libxml_req=2.5.0
 
 AC_CONFIG_SRCDIR([gspell/gspell-checker.c])
 AC_CONFIG_HEADER([config.h])
@@ -76,7 +75,7 @@ AX_COMPILER_FLAGS([WARN_CFLAGS], [WARN_LDFLAGS])
 AX_REQUIRE_DEFINED([AX_PKG_CHECK_MODULES])
 AX_PKG_CHECK_MODULES([DEP],
                     [glib-2.0 >= $glib_req  gtk+-3.0 >= $gtk_req  gtksourceview-3.0 >= $gtksourceview_req],
-                    [enchant >= $enchant_req  libxml-2.0 >= $libxml_req])
+                    [enchant >= $enchant_req])
 
 # iso-codes
 AX_REQUIRE_DEFINED([PKG_CHECK_EXISTS])
diff --git a/docs/reference/gspell-1.0-sections.txt b/docs/reference/gspell-1.0-sections.txt
index f1b6d11..102f85a 100644
--- a/docs/reference/gspell-1.0-sections.txt
+++ b/docs/reference/gspell-1.0-sections.txt
@@ -49,6 +49,7 @@ gspell_language_get_available
 gspell_language_lookup
 gspell_language_get_code
 gspell_language_get_name
+gspell_language_compare
 gspell_language_copy
 gspell_language_free
 <SUBSECTION Standard>
diff --git a/gspell/gspell-language.c b/gspell/gspell-language.c
index 96c31b2..dba23f0 100644
--- a/gspell/gspell-language.c
+++ b/gspell/gspell-language.c
@@ -1,44 +1,38 @@
 /*
- * This file is part of gspell.
+ * This file is part of gspell, a spell-checking library.
  *
  * Copyright 2006 - Paolo Maggi
+ * Copyright 2008 - Novell, Inc.
+ * Copyright 2015 - Sébastien Wilmet
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/* Part of the code taken from Epiphany.
- * Copyright 2003, 2004 - Christian Persch
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 #include "gspell-language.h"
 #include <string.h>
-#include <enchant.h>
 #include <glib/gi18n-lib.h>
-#include <libxml/xmlreader.h>
-
-#ifdef OS_OSX
-#include "gspell-osx.h"
-#endif
+#include <enchant.h>
 
 #define ISO_639_DOMAIN "iso_639"
 #define ISO_3166_DOMAIN        "iso_3166"
 
 struct _GspellLanguage
 {
-       gchar *abrev;
+       gchar *code;
        gchar *name;
+       gchar *ckey;
 };
 
 G_DEFINE_BOXED_TYPE (GspellLanguage,
@@ -46,384 +40,332 @@ G_DEFINE_BOXED_TYPE (GspellLanguage,
                     gspell_language_copy,
                     gspell_language_free)
 
-static gboolean available_languages_initialized = FALSE;
-static GList *available_languages = NULL;
-
 static GHashTable *iso_639_table = NULL;
 static GHashTable *iso_3166_table = NULL;
 
-static gchar *
-get_iso_codes_locale_dir (void)
-{
-       gchar *locale_dir = NULL;
+#define ISOCODESLOCALEDIR ISO_CODES_PREFIX "/share/locale"
 
 #ifdef G_OS_WIN32
-       gchar *win32_dir;
-
-       win32_dir = g_win32_get_package_installation_directory_of_module (NULL);
-
-       locale_dir = g_build_filename (win32_dir,
-                                      "share",
-                                      "locale",
-                                      NULL);
-#else
-#ifdef OS_OSX
-       gchar *res_dir = _gspell_osx_get_resource_path ();
-
-       if (res_dir != NULL)
-       {
-               locale_dir = g_build_filename (res_dir, "share", "locale", NULL);
-               g_free (res_dir);
-       }
+#ifdef DATADIR
+#undef DATADIR
 #endif
-       if (locale_dir == NULL)
+#include <shlobj.h>
+static HMODULE hmodule;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+         DWORD     fdwReason,
+         LPVOID    lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+        DWORD     fdwReason,
+        LPVOID    lpvReserved)
+{
+       switch (fdwReason)
        {
-               locale_dir = g_build_filename (ISO_CODES_PREFIX,
-                                              "share",
-                                              "locale",
-                                              NULL);
+               case DLL_PROCESS_ATTACH:
+                       hmodule = hinstDLL;
+                       break;
        }
-#endif
 
-       return locale_dir;
+       return TRUE;
 }
 
 static gchar *
-get_iso_codes_xml_name (gint iso)
+_get_iso_codes_prefix (void)
 {
-       gchar *share_dir = NULL;
-       gchar *filename;
-       gchar *xml;
-
-#ifdef G_OS_WIN32
-       gchar *win32_dir;
-
-       win32_dir = g_win32_get_package_installation_directory_of_module (NULL);
-
-       share_dir = g_build_filename (win32_dir,
-                                     "share",
-                                     NULL);
-#else
-#ifdef OS_OSX
-       gchar *res_dir = _gspell_osx_get_resource_path ();
+       static gchar retval[1000];
+       static gboolean beenhere = FALSE;
+       gchar *temp_dir = 0;
 
-       if (res_dir != NULL)
+       if (beenhere)
        {
-               share_dir = g_build_filename (res_dir, "share", NULL);
-               g_free (res_dir);
+               return retval;
        }
-#endif
-       if (share_dir == NULL)
+
+       if (!(temp_dir = g_win32_get_package_installation_directory_of_module ((gpointer) hmodule)))
        {
-               share_dir = g_build_filename (ISO_CODES_PREFIX,
-                                             "share",
-                                             NULL);
+               strcpy (retval, ISO_CODES_PREFIX);
+               return retval;
        }
-#endif
-       xml = g_strdup_printf ("iso_%d.xml", iso);
-
-       filename = g_build_filename (share_dir,
-                                    "xml",
-                                    "iso-codes",
-                                    xml,
-                                    NULL);
 
-       g_free (xml);
-       g_free (share_dir);
-
-       return filename;
+       strcpy (retval, temp_dir);
+       g_free (temp_dir);
+       beenhere = TRUE;
+       return retval;
 }
 
-static void
-bind_iso_domains (void)
+static gchar *
+_get_isocodeslocaledir (void)
 {
-       static gboolean bound = FALSE;
+       static gchar retval[1000];
+       static gboolean beenhere = FALSE;
 
-       if (bound == FALSE)
+       if (beenhere)
        {
-               gchar *locale_dir;
+               return retval;
+       }
 
-               locale_dir = get_iso_codes_locale_dir ();
+       strcpy (retval, _get_iso_codes_prefix ());
+       strcat (retval, "\\share\\locale");
+       beenhere = TRUE;
+       return retval;
+}
 
-               bindtextdomain (ISO_639_DOMAIN, locale_dir);
-               bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
+#undef ISO_CODES_PREFIX
+#define ISO_CODES_PREFIX _get_iso_codes_prefix ()
 
-               bindtextdomain(ISO_3166_DOMAIN, locale_dir);
-               bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
+#undef ISOCODESLOCALEDIR
+#define ISOCODESLOCALEDIR _get_isocodeslocaledir ()
 
-               g_free (locale_dir);
-
-               bound = TRUE;
-       }
-}
+#endif /* G_OS_WIN32 */
 
 static void
-read_iso_639_entry (xmlTextReaderPtr  reader,
-                   GHashTable       *table)
+iso_639_start_element (GMarkupParseContext  *context,
+                      const gchar          *element_name,
+                      const gchar         **attribute_names,
+                      const gchar         **attribute_values,
+                      gpointer              data,
+                      GError              **error)
 {
-       xmlChar *code, *name;
-
-       code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_1_code");
-       name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
-
-       /* Get iso-639-2 code */
-       if (code == NULL || code[0] == '\0')
+       GHashTable *hash_table = data;
+       const gchar *iso_639_1_code = NULL;
+       const gchar *iso_639_2_code = NULL;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
+
+       if (strcmp (element_name, "iso_639_entry") != 0)
        {
-               xmlFree (code);
-               /* FIXME: use the 2T or 2B code? */
-               code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "iso_639_2T_code");
+               return;
        }
 
-       if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
-       {
-               g_hash_table_insert (table, code, name);
-       }
-       else
+       for (ii = 0; attribute_names[ii] != NULL; ii++)
        {
-               xmlFree (code);
-               xmlFree (name);
+               if (strcmp (attribute_names[ii], "name") == 0)
+               {
+                       name = attribute_values[ii];
+               }
+               else if (strcmp (attribute_names[ii], "iso_639_1_code") == 0)
+               {
+                       iso_639_1_code = attribute_values[ii];
+               }
+               else if (strcmp (attribute_names[ii], "iso_639_2T_code") == 0)
+               {
+                       iso_639_2_code = attribute_values[ii];
+               }
        }
-}
 
-static void
-read_iso_3166_entry (xmlTextReaderPtr  reader,
-                    GHashTable       *table)
-{
-       xmlChar *code, *name;
-
-       code = xmlTextReaderGetAttribute (reader, (const xmlChar *) "alpha_2_code");
-       name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name");
-
-       if (code != NULL && code[0] != '\0' && name != NULL && name[0] != '\0')
-       {
-               char *lcode;
+       code = (iso_639_1_code != NULL) ? iso_639_1_code : iso_639_2_code;
 
-               lcode = g_ascii_strdown ((char *) code, -1);
-               xmlFree (code);
-
-               /* g_print ("%s -> %s\n", lcode, name); */
-
-               g_hash_table_insert (table, lcode, name);
-       }
-       else
+       if (code != NULL && *code != '\0' &&
+           name != NULL && *name != '\0')
        {
-               xmlFree (code);
-               xmlFree (name);
+               g_hash_table_insert (hash_table,
+                                    g_strdup (code),
+                                    g_strdup (dgettext (ISO_639_DOMAIN, name)));
        }
 }
 
-typedef enum
-{
-       STATE_START,
-       STATE_STOP,
-       STATE_ENTRIES,
-} ParserState;
-
 static void
-load_iso_entries (int      iso,
-                 GFunc    read_entry_func,
-                 gpointer user_data)
+iso_3166_start_element (GMarkupParseContext  *context,
+                       const gchar          *element_name,
+                       const gchar         **attribute_names,
+                       const gchar         **attribute_values,
+                       gpointer              data,
+                       GError              **error)
 {
-       xmlTextReaderPtr reader;
-       ParserState state = STATE_START;
-       xmlChar iso_entries[32], iso_entry[32];
-       char *filename;
-       int ret = -1;
-
-       filename = get_iso_codes_xml_name (iso);
-       reader = xmlNewTextReaderFilename (filename);
-       if (reader == NULL) goto out;
-
-       xmlStrPrintf (iso_entries, sizeof (iso_entries), (const xmlChar *)"iso_%d_entries", iso);
-       xmlStrPrintf (iso_entry, sizeof (iso_entry), (const xmlChar *)"iso_%d_entry", iso);
-
-       ret = xmlTextReaderRead (reader);
+       GHashTable *hash_table = data;
+       const gchar *name = NULL;
+       const gchar *code = NULL;
+       gint ii;
 
-       while (ret == 1)
+       if (strcmp (element_name, "iso_3166_entry") != 0)
        {
-               const xmlChar *tag;
-               xmlReaderTypes type;
-
-               tag = xmlTextReaderConstName (reader);
-               type = xmlTextReaderNodeType (reader);
+               return;
+       }
 
-               if (state == STATE_ENTRIES &&
-                   type == XML_READER_TYPE_ELEMENT &&
-                   xmlStrEqual (tag, iso_entry))
-               {
-                       read_entry_func (reader, user_data);
-               }
-               else if (state == STATE_START &&
-                        type == XML_READER_TYPE_ELEMENT &&
-                        xmlStrEqual (tag, iso_entries))
-               {
-                       state = STATE_ENTRIES;
-               }
-               else if (state == STATE_ENTRIES &&
-                        type == XML_READER_TYPE_END_ELEMENT &&
-                        xmlStrEqual (tag, iso_entries))
-               {
-                       state = STATE_STOP;
-               }
-               else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE ||
-                        type == XML_READER_TYPE_WHITESPACE ||
-                        type == XML_READER_TYPE_TEXT ||
-                        type == XML_READER_TYPE_COMMENT)
+       for (ii = 0; attribute_names[ii] != NULL; ii++)
+       {
+               if (strcmp (attribute_names[ii], "name") == 0)
                {
-                       /* eat it */
+                       name = attribute_values[ii];
                }
-               else
+               else if (strcmp (attribute_names[ii], "alpha_2_code") == 0)
                {
-                       /* ignore it */
+                       code = attribute_values[ii];
                }
-
-               ret = xmlTextReaderRead (reader);
        }
 
-       xmlFreeTextReader (reader);
-
-out:
-       if (ret < 0 || state != STATE_STOP)
+       if (code != NULL && *code != '\0' &&
+           name != NULL && *name != '\0')
        {
-               g_warning ("Failed to load ISO-%d codes from %s!\n",
-                          iso, filename);
+               g_hash_table_insert (hash_table,
+                                    g_ascii_strdown (code, -1),
+                                    g_strdup (dgettext (ISO_3166_DOMAIN, name)));
        }
-
-       g_free (filename);
 }
 
-static GHashTable *
-create_iso_639_table (void)
+static GMarkupParser iso_639_parser =
 {
-       GHashTable *table;
-
-       bind_iso_domains ();
-       table = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                      (GDestroyNotify) xmlFree,
-                                      (GDestroyNotify) xmlFree);
-
-       load_iso_entries (639, (GFunc) read_iso_639_entry, table);
+       iso_639_start_element,
+       NULL, NULL, NULL, NULL
+};
 
-       return table;
-}
+static GMarkupParser iso_3166_parser =
+{
+       iso_3166_start_element,
+       NULL, NULL, NULL, NULL
+};
 
-static GHashTable *
-create_iso_3166_table (void)
+static void
+iso_codes_parse (const GMarkupParser *parser,
+                const gchar         *basename,
+                GHashTable          *hash_table)
 {
-       GHashTable *table;
+       GMappedFile *mapped_file;
+       gchar *filename;
+       GError *error = NULL;
+
+       filename = g_build_filename (ISO_CODES_PREFIX,
+                                    "share",
+                                    "xml",
+                                    "iso-codes",
+                                    basename,
+                                    NULL);
 
-       bind_iso_domains ();
-       table = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                      (GDestroyNotify) g_free,
-                                      (GDestroyNotify) xmlFree);
+       mapped_file = g_mapped_file_new (filename, FALSE, &error);
+       g_free (filename);
 
-       load_iso_entries (3166, (GFunc) read_iso_3166_entry, table);
+       if (mapped_file != NULL)
+       {
+               GMarkupParseContext *context;
+               const gchar *contents;
+               gsize length;
+
+               context = g_markup_parse_context_new (parser, 0, hash_table, NULL);
+               contents = g_mapped_file_get_contents (mapped_file);
+               length = g_mapped_file_get_length (mapped_file);
+               g_markup_parse_context_parse (context, contents, length, &error);
+               g_markup_parse_context_free (context);
+               g_mapped_file_unref (mapped_file);
+       }
 
-       return table;
+       if (error != NULL)
+       {
+               g_warning ("%s: %s", basename, error->message);
+               g_error_free (error);
+       }
 }
 
-static char *
-create_name_for_language (const char *code)
+static void
+spell_language_dict_describe_cb (const gchar * const language_code,
+                                 const gchar * const provider_name,
+                                 const gchar * const provider_desc,
+                                 const gchar * const provider_file,
+                                 GTree *tree)
 {
-       char **str;
-       char *name = NULL;
-       const char *langname, *localename;
-       int len;
+       const gchar *iso_639_name;
+       const gchar *iso_3166_name;
+       gchar *language_name;
+       gchar *lowercase;
+       gchar **tokens;
 
-       g_return_val_if_fail (iso_639_table != NULL, NULL);
-       g_return_val_if_fail (iso_3166_table != NULL, NULL);
+       /* Split language code into lowercase tokens. */
+       lowercase = g_ascii_strdown (language_code, -1);
+       tokens = g_strsplit (lowercase, "_", -1);
+       g_free (lowercase);
 
-       str = g_strsplit (code, "_", -1);
-       len = g_strv_length (str);
-       g_return_val_if_fail (len != 0, NULL);
+       g_return_if_fail (tokens != NULL);
 
-       langname = (const char *) g_hash_table_lookup (iso_639_table, str[0]);
+       iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
 
-       if (len == 1 && langname != NULL)
+       if (iso_639_name == NULL)
        {
-               name = g_strdup (dgettext (ISO_639_DOMAIN, langname));
+               /* Translators: %s is the language ISO code. */
+               language_name = g_strdup_printf (C_("language", "Unknown (%s)"), language_code);
+               goto exit;
        }
-       else if (len == 2 && langname != NULL)
+
+       if (g_strv_length (tokens) < 2)
        {
-               gchar *locale_code = g_ascii_strdown (str[1], -1);
+               language_name = g_strdup (iso_639_name);
+               goto exit;
+       }
 
-               localename = (const char *) g_hash_table_lookup (iso_3166_table, locale_code);
-               g_free (locale_code);
+       iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
 
-               if (localename != NULL)
-               {
-                       /* Translators: the first %s is the language name, and
-                        * the second %s is the locale name. Example:
-                        * "French (France)"
-                        */
-                       name = g_strdup_printf (C_("language", "%s (%s)"),
-                                               dgettext (ISO_639_DOMAIN, langname),
-                                               dgettext (ISO_3166_DOMAIN, localename));
-               }
-               else
-               {
-                       name = g_strdup_printf (C_("language", "%s (%s)"),
-                                               dgettext (ISO_639_DOMAIN, langname), str[1]);
-               }
+       if (iso_3166_name != NULL)
+       {
+               /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)".
+                */
+               language_name = g_strdup_printf (C_("language", "%s (%s)"),
+                                                iso_639_name,
+                                                iso_3166_name);
        }
        else
        {
-               /* Translators: this refers to an unknown language code
-                * (one which isn't in our built-in list).
+               /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)".
                 */
-               name = g_strdup_printf (C_("language", "Unknown (%s)"), code);
+               language_name = g_strdup_printf (C_("language", "%s (%s)"),
+                                                iso_639_name,
+                                                tokens[1]);
        }
 
-       g_strfreev (str);
+exit:
+       g_strfreev (tokens);
 
-       return name;
+       g_tree_replace (tree, g_strdup (language_code), language_name);
 }
 
-static void
-enumerate_dicts (const char * const lang_tag,
-                const char * const provider_name,
-                const char * const provider_desc,
-                const char * const provider_file,
-                void * user_data)
+static const GspellLanguage *
+spell_language_lookup (const gchar *language_code)
 {
-       gchar *lang_name;
+       const GspellLanguage *closest_match = NULL;
+       const GList *available_languages;
 
-       GTree *dicts = (GTree *)user_data;
+       available_languages = gspell_language_get_available ();
 
-       lang_name = create_name_for_language (lang_tag);
-       g_return_if_fail (lang_name != NULL);
+       while (available_languages != NULL && language_code != NULL)
+       {
+               GspellLanguage *language = available_languages->data;
+               const gchar *code = language->code;
+               gsize length = strlen (code);
 
-       /* g_print ("%s - %s\n", lang_tag, lang_name); */
+               if (g_ascii_strcasecmp (language_code, code) == 0)
+               {
+                       return language;
+               }
 
-       g_tree_replace (dicts, g_strdup (lang_tag), lang_name);
-}
+               if (g_ascii_strncasecmp (language_code, code, length) == 0)
+               {
+                       closest_match = language;
+               }
 
-static gint
-key_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
-{
-       return strcmp (a, b);
-}
+               available_languages = g_list_next (available_languages);
+       }
 
-static gint
-lang_cmp (const GspellLanguage *a,
-         const GspellLanguage *b)
-{
-       return g_utf8_collate (a->name, b->name);
+       return closest_match;
 }
 
 static gboolean
-build_langs_list (const gchar *key,
-                 const gchar *value,
-                 gpointer     data)
+spell_language_traverse_cb (const gchar  *code,
+                           const gchar  *name,
+                           GList       **available_languages)
 {
-       GspellLanguage *lang = g_new (GspellLanguage, 1);
+       GspellLanguage *language;
 
-       lang->abrev = g_strdup (key);
-       lang->name = g_strdup (value);
+       language = g_slice_new (GspellLanguage);
+       language->code = g_strdup (code);
+       language->name = g_strdup (name);
+       language->ckey = g_utf8_collate_key (name, -1);
 
-       available_languages = g_list_insert_sorted (available_languages,
-                                                   lang,
-                                                   (GCompareFunc)lang_cmp);
+       *available_languages = g_list_insert_sorted (*available_languages,
+                                                    language,
+                                                    (GCompareFunc) gspell_language_compare);
 
        return FALSE;
 }
@@ -437,113 +379,156 @@ build_langs_list (const gchar *key,
 const GList *
 gspell_language_get_available (void)
 {
+       static gboolean initialized = FALSE;
+       static GList *available_languages = NULL;
        EnchantBroker *broker;
-       GTree *dicts;
+       GTree *tree;
 
-       if (available_languages_initialized)
+       if (initialized)
+       {
                return available_languages;
+       }
 
-       g_return_val_if_fail (available_languages == NULL, NULL);
-
-       available_languages_initialized = TRUE;
+       initialized = TRUE;
 
-       broker = enchant_broker_init ();
-       g_return_val_if_fail (broker != NULL, NULL);
+       bindtextdomain (ISO_639_DOMAIN, ISOCODESLOCALEDIR);
+       bind_textdomain_codeset (ISO_639_DOMAIN, "UTF-8");
 
-       /* Use a GTree to efficiently remove duplicates while building the list */
-       dicts = g_tree_new_full (key_cmp,
-                                NULL,
-                                (GDestroyNotify)g_free,
-                                (GDestroyNotify)g_free);
+       bindtextdomain (ISO_3166_DOMAIN, ISOCODESLOCALEDIR);
+       bind_textdomain_codeset (ISO_3166_DOMAIN, "UTF-8");
 
-       iso_639_table = create_iso_639_table ();
-       iso_3166_table = create_iso_3166_table ();
+       iso_639_table = g_hash_table_new_full (g_str_hash,
+                                              g_str_equal,
+                                              (GDestroyNotify) g_free,
+                                              (GDestroyNotify) g_free);
 
-       enchant_broker_list_dicts (broker, enumerate_dicts, dicts);
+       iso_3166_table = g_hash_table_new_full (g_str_hash,
+                                               g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) g_free);
 
-       enchant_broker_free (broker);
+       iso_codes_parse (&iso_639_parser, "iso_639.xml", iso_639_table);
+       iso_codes_parse (&iso_3166_parser, "iso_3166.xml", iso_3166_table);
 
-       g_hash_table_destroy (iso_639_table);
-       g_hash_table_destroy (iso_3166_table);
+       tree = g_tree_new_full ((GCompareDataFunc) strcmp,
+                               NULL,
+                               (GDestroyNotify) g_free,
+                               (GDestroyNotify) g_free);
 
-       iso_639_table = NULL;
-       iso_3166_table = NULL;
+       broker = enchant_broker_init ();
+       enchant_broker_list_dicts (broker,
+                                  (EnchantDictDescribeFn) spell_language_dict_describe_cb,
+                                  tree);
+       enchant_broker_free (broker);
 
-       g_tree_foreach (dicts, (GTraverseFunc)build_langs_list, NULL);
+       g_tree_foreach (tree,
+                       (GTraverseFunc) spell_language_traverse_cb,
+                       &available_languages);
 
-       g_tree_destroy (dicts);
+       g_tree_destroy (tree);
 
        return available_languages;
 }
 
-const gchar *
-gspell_language_get_name (const GspellLanguage *lang)
+static const GspellLanguage *
+spell_language_pick_default (void)
 {
-       if (lang == NULL)
-               /* Translators: this refers the Default language used by the
-                * spell checker
-                */
-               return C_("language", "Default");
+       const GspellLanguage *language = NULL;
+       const gchar * const *language_names;
+       const GList *available_languages;
+       gint ii;
 
-       return lang->name;
-}
+       language_names = g_get_language_names ();
+       available_languages = gspell_language_get_available ();
 
-const gchar *
-gspell_language_get_code (const GspellLanguage *lang)
-{
-       g_return_val_if_fail (lang != NULL, NULL);
+       for (ii = 0; language == NULL && language_names[ii] != NULL; ii++)
+       {
+               language = spell_language_lookup (language_names[ii]);
+       }
 
-       return lang->abrev;
+       if (language == NULL)
+       {
+               language = spell_language_lookup ("en_US");
+       }
+
+       if (language == NULL && available_languages != NULL)
+       {
+               language = available_languages->data;
+       }
+
+       return language;
 }
 
 const GspellLanguage *
-gspell_language_lookup (const gchar *key)
+gspell_language_lookup (const gchar *language_code)
 {
-       const GList *langs;
-
-       g_return_val_if_fail (key != NULL, NULL);
+       const GspellLanguage *language = NULL;
 
-       langs = gspell_language_get_available ();
+       language = spell_language_lookup (language_code);
 
-       while (langs != NULL)
+       if (language == NULL)
        {
-               const GspellLanguage *l = (const GspellLanguage *)langs->data;
+               language = spell_language_pick_default ();
+       }
+
+       return language;
+}
 
-               if (g_ascii_strcasecmp (key, l->abrev) == 0)
-                       return l;
+const gchar *
+gspell_language_get_code (const GspellLanguage *language)
+{
+       g_return_val_if_fail (language != NULL, NULL);
+
+       return language->code;
+}
 
-               langs = langs->next;
+const gchar *
+gspell_language_get_name (const GspellLanguage *language)
+{
+       if (language == NULL)
+       {
+               /* Translators: This refers to the default language used by the
+                * spell checker.
+                */
+               return C_("language", "Default");
        }
 
-       return NULL;
+       return language->name;
+}
+
+gint
+gspell_language_compare (const GspellLanguage *language_a,
+                         const GspellLanguage *language_b)
+{
+       return strcmp (language_a->ckey, language_b->ckey);
 }
 
 /**
  * gspell_language_copy:
- * @lang: a #GspellLanguage.
+ * @language: a #GspellLanguage.
  *
  * Used by language bindings.
  *
  * Returns: a copy of @lang.
  */
 GspellLanguage *
-gspell_language_copy (const GspellLanguage *lang)
+gspell_language_copy (const GspellLanguage *language)
 {
-       g_return_val_if_fail (lang != NULL, NULL);
+       g_return_val_if_fail (language != NULL, NULL);
 
-       return (GspellLanguage *) lang;
+       return (GspellLanguage *) language;
 }
 
 /**
  * gspell_language_free:
- * @lang: a #GspellLanguage.
+ * @language: a #GspellLanguage.
  *
  * Used by language bindings.
  */
 void
-gspell_language_free (GspellLanguage *lang)
+gspell_language_free (GspellLanguage *language)
 {
-       g_return_if_fail (lang != NULL);
+       g_return_if_fail (language != NULL);
 }
 
 /* ex:set ts=8 noet: */
diff --git a/gspell/gspell-language.h b/gspell/gspell-language.h
index 1dccc30..f9df49e 100644
--- a/gspell/gspell-language.h
+++ b/gspell/gspell-language.h
@@ -1,21 +1,27 @@
 /*
- * This file is part of gspell.
+ * This file is part of gspell, a spell-checking library.
  *
  * Copyright 2006 - Paolo Maggi
+ * Copyright 2008 - Novell, Inc.
  * Copyright 2015 - Sébastien Wilmet
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Based on GtkhtmlSpellLanguage (Novell), which was based on Marco Barisione's
+ * GSpellLanguage, which was based on GeditSpellCheckerLanguage, which was based
+ * partly on Epiphany's code.
  */
 
 #ifndef __GSPELL_LANGUAGE_H__
@@ -38,17 +44,18 @@ GType               gspell_language_get_type                (void) G_GNUC_CONST;
 const GList *  gspell_language_get_available           (void);
 
 const GspellLanguage *
-               gspell_language_lookup                  (const gchar *key);
+               gspell_language_lookup                  (const gchar *language_code);
 
-const gchar *  gspell_language_get_code                (const GspellLanguage *lang);
+const gchar *  gspell_language_get_code                (const GspellLanguage *language);
 
-const gchar *  gspell_language_get_name                (const GspellLanguage *lang);
+const gchar *  gspell_language_get_name                (const GspellLanguage *language);
 
-/* These should not be used, they are just to make GObject Introspection
- * bindings happy.
- */
-GspellLanguage *gspell_language_copy                   (const GspellLanguage *lang);
-void           gspell_language_free                    (GspellLanguage *lang);
+gint           gspell_language_compare                 (const GspellLanguage *language_a,
+                                                        const GspellLanguage *language_b);
+
+GspellLanguage *gspell_language_copy                   (const GspellLanguage *language);
+
+void           gspell_language_free                    (GspellLanguage *language);
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]