1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2003, 2004 Dom Lachowicz
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, Dom Lachowicz
21 * gives permission to link the code of this program with
22 * non-LGPL Spelling Provider libraries (eg: a MSFT Office
23 * spell checker backend) and distribute linked combinations including
24 * the two. You must obey the GNU Lesser General Public License in all
25 * respects for all of the code used other than said providers. If you modify
26 * this file, you may extend this exception to your version of the
27 * file, but you are not obligated to do so. If you do not wish to
28 * do so, delete this exception statement from your version.
38 #include <glib/gstdio.h>
42 #include "enchant-provider.h"
45 #ifdef XP_TARGET_COCOA
46 #import "enchant_cocoa.h"
49 #ifdef XP_TARGET_COCOA
50 #define ENCHANT_USER_PATH_EXTENSION "Library", "Application Support", "Enchant"
52 #define ENCHANT_USER_PATH_EXTENSION "enchant"
54 #define ENCHANT_USER_PATH_EXTENSION ".enchant"
57 #ifdef ENABLE_BINRELOC
61 ENCHANT_PLUGIN_DECLARE("Enchant")
64 enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key);
66 /********************************************************************************/
67 /********************************************************************************/
69 struct str_enchant_broker
71 GSList *provider_list; /* list of all of the spelling backend providers */
72 GHashTable *dict_map; /* map of language tag -> dictionary */
73 GHashTable *provider_ordering; /* map of language tag -> provider order */
79 typedef struct str_enchant_session
81 GHashTable *session_include;
82 GHashTable *session_exclude;
86 char * personal_filename;
87 char * exclude_filename;
94 EnchantProvider * provider;
97 typedef struct str_enchant_dict_private_data
99 unsigned int reference_count;
100 EnchantSession* session;
101 } EnchantDictPrivateData;
103 typedef EnchantProvider *(*EnchantProviderInitFunc) (void);
104 typedef void (*EnchantPreConfigureFunc) (EnchantProvider * provider, const char * module_dir);
106 /********************************************************************************/
107 /********************************************************************************/
110 #define path_cmp g_utf8_collate
112 #define path_cmp strcmp
115 static GSList* enchant_slist_prepend_unique_path (GSList *slist, gchar* data)
117 if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp))
119 return g_slist_prepend (slist, data);
128 static GSList* enchant_slist_append_unique_path (GSList *slist, gchar* data)
130 if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp))
132 return g_slist_append (slist, data);
142 _enchant_get_user_home_dirs (void)
145 const char* home_dir;
148 tmp = enchant_get_registry_value_ex (1, "Config", "Home_Dir");
150 dirs = enchant_slist_append_unique_path (dirs, tmp);
152 home_dir = g_get_home_dir ();
154 dirs = enchant_slist_append_unique_path (dirs, g_strdup (home_dir));
160 _enchant_ensure_dir_exists (const char* dir)
162 if (dir && !g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
164 (void)g_remove (dir);
165 g_mkdir_with_parents (dir, 0700);
170 enchant_get_user_dirs (void)
172 GSList *user_dirs = NULL;
175 const char * user_config_dir;
177 user_config_dir = g_get_user_config_dir();
180 user_dirs = enchant_slist_append_unique_path (user_dirs, g_build_filename (user_config_dir,
186 GSList *home_dirs = NULL, *dir;
187 home_dirs = _enchant_get_user_home_dirs ();
189 for (dir = home_dirs; dir; dir = dir->next)
191 user_dirs = enchant_slist_append_unique_path (user_dirs,
192 g_build_filename (dir->data,
193 ENCHANT_USER_PATH_EXTENSION,
197 g_slist_foreach (home_dirs, (GFunc)g_free, NULL);
198 g_slist_free (home_dirs);
204 /* place to look for system level providers */
206 enchant_get_module_dirs (void)
208 GSList *module_dirs = NULL;
210 char * module_dir = NULL;
211 char * prefix = NULL;
214 char* user_module_dir;
216 user_module_dir = enchant_get_registry_value_ex (1, "Config", "Module_Dir");
218 module_dirs = enchant_slist_append_unique_path (module_dirs, user_module_dir);
221 #ifdef XP_TARGET_COCOA
222 module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup ([[EnchantResourceProvider instance] moduleFolder]));
226 GSList *user_dirs, *iter;
228 user_dirs = enchant_get_user_dirs();
230 for (iter = user_dirs; iter; iter = iter->next)
231 module_dirs = enchant_slist_append_unique_path (module_dirs, iter->data);
233 g_slist_free (user_dirs);
236 /* Look for explicitly set registry values */
237 module_dir = enchant_get_registry_value_ex (0, "Config", "Module_Dir");
239 module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir);
241 #if defined(ENCHANT_GLOBAL_MODULE_DIR)
242 module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup (ENCHANT_GLOBAL_MODULE_DIR));
244 /* Dynamically locate library and search for modules relative to it. */
245 prefix = enchant_get_prefix_dir();
248 module_dir = g_build_filename(prefix,"lib","enchant",NULL);
250 module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir);
258 enchant_get_conf_dirs (void)
260 GSList *conf_dirs = NULL, *user_conf_dirs, *iter;
261 char * ordering_dir = NULL, * prefix = NULL;
263 user_conf_dirs = enchant_get_user_config_dirs();
265 for (iter = user_conf_dirs; iter != NULL; iter = iter->next)
267 conf_dirs = enchant_slist_append_unique_path (conf_dirs, iter->data);
270 g_slist_free (user_conf_dirs);
272 #ifdef XP_TARGET_COCOA
273 conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup ([[EnchantResourceProvider instance] configFolder]));
276 /* Look for explicitly set registry values */
277 ordering_dir = enchant_get_registry_value_ex (0, "Config", "Data_Dir");
279 conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir);
281 /* Dynamically locate library and search for files relative to it. */
282 prefix = enchant_get_prefix_dir();
285 ordering_dir = g_build_filename(prefix,"share","enchant",NULL);
287 conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir);
290 #if defined(ENCHANT_GLOBAL_ORDERING)
291 conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup (ENCHANT_GLOBAL_ORDERING));
297 ENCHANT_MODULE_EXPORT(FILE *)
298 enchant_fopen (const gchar *filename, const gchar *mode)
301 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
306 if (wfilename == NULL)
312 wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
321 retval = _wfopen (wfilename, wmode);
330 return fopen (filename, mode);
335 * enchant_get_user_config_dir
337 * Returns: the user's enchant directory, or %null. Returned value
340 * The enchant directory is the place where enchant finds user
341 * dictionaries and settings related to enchant
343 * This API is private to the providers.
345 ENCHANT_MODULE_EXPORT (GSList *)
346 enchant_get_user_config_dirs (void)
351 dirs = enchant_get_user_dirs();
353 user_config = enchant_get_registry_value_ex (1, "Config", "Data_Dir");
355 dirs = enchant_slist_prepend_unique_path (dirs, user_config);
361 * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd.
364 enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key)
367 /* TODO: GConf? KConfig? */
375 WCHAR* wszValue = NULL;
376 char* szValue = NULL;
377 gunichar2 * uKeyName;
381 baseKey = HKEY_CURRENT_USER;
383 baseKey = HKEY_LOCAL_MACHINE;
385 keyName = g_strdup_printf("Software\\Enchant\\%s", prefix);
386 uKeyName = g_utf8_to_utf16 (keyName, -1, NULL, NULL, NULL);
387 uKey = g_utf8_to_utf16 (key, -1, NULL, NULL, NULL);
389 if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
391 /* Determine size of string */
392 if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS)
394 wszValue = g_new0(WCHAR, dwSize + 1);
395 RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize);
400 if(wszValue && *wszValue)
401 szValue = g_utf16_to_utf8 (wszValue, -1, NULL, NULL, NULL);
413 * enchant_get_registry_value
414 * @prefix: Your category, such as "Ispell" or "Myspell"
415 * @key: The tag within your category that you're interested in
417 * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd.
419 * This API is private to the providers.
421 ENCHANT_MODULE_EXPORT (char *)
422 enchant_get_registry_value (const char * const prefix, const char * const key)
426 g_return_val_if_fail (prefix, NULL);
427 g_return_val_if_fail (key, NULL);
429 val = enchant_get_registry_value_ex(1, prefix, key);
431 val = enchant_get_registry_value_ex (0, prefix, key);
436 /********************************************************************************/
437 /********************************************************************************/
440 enchant_modify_string_chars (gchar *str,
442 gchar (*function)(gchar))
446 g_return_val_if_fail (str != NULL, NULL);
453 for (it = str; it != end; ++it)
454 *it = function (*it);
460 enchant_ascii_strup (gchar *str,
463 return enchant_modify_string_chars(str, len, g_ascii_toupper);
467 enchant_ascii_strdown (gchar *str,
470 return enchant_modify_string_chars(str, len, g_ascii_tolower);
473 /* returns TRUE if tag is valid
474 * for requires alphanumeric ASCII or underscore
477 enchant_is_valid_dictionary_tag(const char * const tag)
480 for (it = tag; *it; ++it)
482 if(!g_ascii_isalnum(*it) && *it != '_')
486 return it != tag; /*empty tag invalid*/
490 enchant_normalize_dictionary_tag (const char * const dict_tag)
492 char * new_tag = g_strdup (dict_tag);
495 new_tag = g_strstrip (new_tag);
497 /* strip off en_GB@euro */
498 if ((needle = strchr (new_tag, '@')) != NULL)
501 /* strip off en_GB.UTF-8 */
502 if ((needle = strchr (new_tag, '.')) != NULL)
505 /* turn en-GB into en_GB */
506 if ((needle = strchr (new_tag, '-')) != NULL)
509 /* everything before first '_' is converted to lower case */
510 if ((needle = strchr (new_tag, '_')) != NULL) {
511 enchant_ascii_strdown(new_tag, needle - new_tag);
513 /* everything after first '_' is converted to upper case */
514 enchant_ascii_strup(needle, -1);
517 enchant_ascii_strdown(new_tag, -1);
524 enchant_iso_639_from_tag (const char * const dict_tag)
526 char * new_tag = g_strdup (dict_tag);
529 if ((needle = strchr (new_tag, '_')) != NULL)
536 enchant_session_destroy (EnchantSession * session)
538 g_hash_table_destroy (session->session_include);
539 g_hash_table_destroy (session->session_exclude);
540 enchant_pwl_free (session->personal);
541 enchant_pwl_free (session->exclude);
542 g_free (session->personal_filename);
543 g_free (session->exclude_filename);
544 g_free (session->language_tag);
547 g_free (session->error);
552 static EnchantSession *
553 enchant_session_new_with_pwl (EnchantProvider * provider,
554 const char * const pwl,
555 const char * const excl,
556 const char * const lang,
557 gboolean fail_if_no_pwl)
559 EnchantSession * session;
560 EnchantPWL *personal = NULL;
561 EnchantPWL *exclude = NULL;
564 personal = enchant_pwl_init_with_file (pwl);
566 if (personal == NULL) {
570 personal = enchant_pwl_init ();
574 exclude = enchant_pwl_init_with_file (excl);
576 exclude = enchant_pwl_init ();
578 session = g_new0 (EnchantSession, 1);
579 session->session_include = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
580 session->session_exclude = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
581 session->personal = personal;
582 session->exclude = exclude;
583 session->provider = provider;
584 session->language_tag = g_strdup (lang);
585 session->personal_filename = g_strdup (pwl);
586 session->exclude_filename = g_strdup (excl);
591 static EnchantSession *
592 _enchant_session_new (EnchantProvider *provider, const char * const user_config_dir,
593 const char * const lang, gboolean fail_if_no_pwl)
595 char *filename, *dic, *excl;
596 EnchantSession * session;
598 if (!user_config_dir || !lang)
601 filename = g_strdup_printf ("%s.dic", lang);
602 dic = g_build_filename (user_config_dir, filename, NULL);
605 filename = g_strdup_printf ("%s.exc", lang);
606 excl = g_build_filename (user_config_dir, filename, NULL);
609 session = enchant_session_new_with_pwl (provider, dic, excl, lang, fail_if_no_pwl);
617 static EnchantSession *
618 enchant_session_new (EnchantProvider *provider, const char * const lang)
620 EnchantSession * session = NULL;
621 GSList *user_config_dirs, *iter;
623 user_config_dirs = enchant_get_user_config_dirs ();
624 for (iter = user_config_dirs; iter != NULL && session == NULL; iter = iter->next)
626 session =_enchant_session_new (provider, iter->data, lang, TRUE);
629 if (session == NULL && user_config_dirs != NULL)
631 _enchant_ensure_dir_exists (user_config_dirs->data);
633 session =_enchant_session_new (provider, user_config_dirs->data, lang, FALSE);
636 g_slist_foreach (user_config_dirs, (GFunc)g_free, NULL);
637 g_slist_free (user_config_dirs);
644 enchant_session_add (EnchantSession * session, const char * const word, size_t len)
646 char* key = g_strndup (word, len);
647 g_hash_table_remove (session->session_exclude, key);
648 g_hash_table_insert (session->session_include, key, GINT_TO_POINTER(TRUE));
652 enchant_session_remove (EnchantSession * session, const char * const word, size_t len)
654 char* key = g_strndup (word, len);
655 g_hash_table_remove (session->session_include, key);
656 g_hash_table_insert (session->session_exclude, key, GINT_TO_POINTER(TRUE));
660 enchant_session_add_personal (EnchantSession * session, const char * const word, size_t len)
662 enchant_pwl_add(session->personal, word, len);
666 enchant_session_remove_personal (EnchantSession * session, const char * const word, size_t len)
668 enchant_pwl_remove(session->personal, word, len);
672 enchant_session_add_exclude (EnchantSession * session, const char * const word, size_t len)
674 enchant_pwl_add(session->exclude, word, len);
678 enchant_session_remove_exclude (EnchantSession * session, const char * const word, size_t len)
680 enchant_pwl_remove(session->exclude, word, len);
683 /* a word is excluded if it is in the exclude dictionary or in the session exclude list
684 * AND the word has not been added to the session include list
687 enchant_session_exclude (EnchantSession * session, const char * const word, size_t len)
689 gboolean result = FALSE;
691 char * utf = g_strndup (word, len);
693 if (!g_hash_table_lookup (session->session_include, utf) &&
694 (g_hash_table_lookup (session->session_exclude, utf)||
695 enchant_pwl_check (session->exclude, word, len) == 0 ))
703 enchant_session_contains (EnchantSession * session, const char * const word, size_t len)
705 gboolean result = FALSE;
707 char * utf = g_strndup (word, len);
709 if (g_hash_table_lookup (session->session_include, utf) ||
710 (enchant_pwl_check (session->personal, word, len) == 0 &&
711 !enchant_pwl_check (session->exclude, word, len) == 0))
720 enchant_session_clear_error (EnchantSession * session)
724 g_free (session->error);
725 session->error = NULL;
729 /********************************************************************************/
730 /********************************************************************************/
733 enchant_provider_free_string_list (EnchantProvider * provider, char ** string_list)
735 if (provider && provider->free_string_list)
736 (*provider->free_string_list) (provider, string_list);
740 * enchant_dict_set_error
741 * @dict: A non-null dictionary
742 * @err: A non-null error message
744 * Sets the current runtime error to @err. This API is private to the
747 ENCHANT_MODULE_EXPORT(void)
748 enchant_dict_set_error (EnchantDict * dict, const char * const err)
750 EnchantSession * session;
752 g_return_if_fail (dict);
753 g_return_if_fail (err);
754 g_return_if_fail (g_utf8_validate(err, -1, NULL));
756 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
758 enchant_session_clear_error (session);
759 session->error = g_strdup (err);
763 * enchant_dict_get_error
764 * @dict: A non-null dictionary
766 * Returns a const char string or NULL describing the last exception in UTF8 encoding.
767 * WARNING: error is transient. It will likely be cleared as soon as
768 * the next dictionary operation is called
770 * Returns: an error message
772 ENCHANT_MODULE_EXPORT(char *)
773 enchant_dict_get_error (EnchantDict * dict)
775 EnchantSession * session;
777 g_return_val_if_fail (dict, NULL);
779 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
780 return session->error;
785 * @dict: A non-null #EnchantDict
786 * @word: The non-null word you wish to check, in UTF-8 encoding
787 * @len: The byte length of @word, or -1 for strlen (@word)
789 * Will return an "incorrect" value if any of those pre-conditions
792 * Returns: 0 if the word is correctly spelled, positive if not, negative if error
794 ENCHANT_MODULE_EXPORT (int)
795 enchant_dict_check (EnchantDict * dict, const char *const word, ssize_t len)
797 EnchantSession * session;
799 g_return_val_if_fail (dict, -1);
800 g_return_val_if_fail (word, -1);
805 g_return_val_if_fail (len, -1);
806 g_return_val_if_fail (g_utf8_validate(word, len, NULL),-1);
808 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
809 enchant_session_clear_error (session);
811 /* first, see if it's to be excluded*/
812 if (enchant_session_exclude (session, word, len))
815 /* then, see if it's in our pwl or session*/
816 if (enchant_session_contains(session, word, len))
820 return (*dict->check) (dict, word, len);
821 else if (session->is_pwl)
827 /* @suggs must have at least n_suggs + n_new_suggs space allocated
828 * @n_suggs is the number if items currently appearing in @suggs
830 * returns the number of items in @suggs after merge is complete
833 enchant_dict_merge_suggestions(EnchantDict * dict,
836 const char * const* const new_suggs,
839 EnchantSession * session;
842 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
844 for(i = 0; i < n_new_suggs; i++)
846 int is_duplicate = 0;
847 char * normalized_new_sugg;
849 normalized_new_sugg = g_utf8_normalize (new_suggs[i], -1, G_NORMALIZE_NFD);
851 for(j = 0; j < n_suggs; j++)
853 char* normalized_sugg;
854 normalized_sugg = g_utf8_normalize (suggs[j], -1, G_NORMALIZE_NFD);
856 if(strcmp(normalized_sugg,normalized_new_sugg)==0)
859 g_free(normalized_sugg);
862 g_free(normalized_sugg);
864 g_free(normalized_new_sugg);
868 suggs[n_suggs] = g_strdup (new_suggs[i]);
877 enchant_dict_get_good_suggestions(EnchantDict * dict,
878 const char * const* const suggs,
880 size_t* out_n_filtered_suggs)
882 EnchantSession * session;
883 size_t i, n_filtered_suggs;
884 char ** filtered_suggs;
886 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
888 filtered_suggs = g_new0 (char *, n_suggs + 1);
889 n_filtered_suggs = 0;
891 for(i = 0; i < n_suggs; i++)
893 size_t sugg_len = strlen(suggs[i]);
895 if (sugg_len == 0) continue;
897 if(g_utf8_validate(suggs[i], sugg_len, NULL) &&
898 !enchant_session_exclude(session, suggs[i], sugg_len) )
900 filtered_suggs[n_filtered_suggs] = g_strdup (suggs[i]);
905 if(out_n_filtered_suggs)
906 *out_n_filtered_suggs = n_filtered_suggs;
908 return filtered_suggs;
912 * enchant_dict_suggest
913 * @dict: A non-null #EnchantDict
914 * @word: The non-null word you wish to find suggestions for, in UTF-8 encoding
915 * @len: The byte length of @word, or -1 for strlen (@word)
916 * @out_n_suggs: The location to store the # of suggestions returned, or %null
918 * Will return an %null value if any of those pre-conditions
921 * Returns: A %null terminated list of UTF-8 encoded suggestions, or %null
923 ENCHANT_MODULE_EXPORT (char **)
924 enchant_dict_suggest (EnchantDict * dict, const char *const word,
925 ssize_t len, size_t * out_n_suggs)
927 EnchantSession * session;
928 size_t n_suggs = 0, n_dict_suggs = 0, n_pwl_suggs = 0, n_suggsT = 0;
929 char **suggs, **dict_suggs = NULL, **pwl_suggs = NULL, **suggsT;
931 g_return_val_if_fail (dict, NULL);
932 g_return_val_if_fail (word, NULL);
937 g_return_val_if_fail (len, NULL);
938 g_return_val_if_fail (g_utf8_validate(word, len, NULL), NULL);
940 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
941 enchant_session_clear_error (session);
942 /* Check for suggestions from provider dictionary */
945 dict_suggs = (*dict->suggest) (dict, word, len,
949 suggsT = enchant_dict_get_good_suggestions(dict, dict_suggs, n_dict_suggs, &n_suggsT);
950 enchant_provider_free_string_list (session->provider, dict_suggs);
952 n_dict_suggs = n_suggsT;
956 /* Check for suggestions from personal dictionary */
957 if(session->personal)
959 pwl_suggs = enchant_pwl_suggest(session->personal, word, len, dict_suggs, &n_pwl_suggs);
962 suggsT = enchant_dict_get_good_suggestions(dict, pwl_suggs, n_pwl_suggs, &n_suggsT);
963 enchant_pwl_free_string_list (session->personal, pwl_suggs);
965 n_pwl_suggs = n_suggsT;
968 /* Clone suggestions if there are any */
969 n_suggs = n_pwl_suggs + n_dict_suggs;
972 suggs = g_new0 (char *, n_suggs + 1);
974 /* Copy over suggestions from dict, if no dupes */
975 n_suggs = enchant_dict_merge_suggestions(dict,
977 dict_suggs, n_dict_suggs);
979 /* Copy over suggestions from pwl, if no dupes */
980 n_suggs = enchant_dict_merge_suggestions(dict,
982 pwl_suggs, n_pwl_suggs);
994 g_strfreev(dict_suggs);
995 g_strfreev(pwl_suggs);
998 *out_n_suggs = n_suggs;
1005 * @dict: A non-null #EnchantDict
1006 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1007 * @len: The byte length of @word, or -1 for strlen (@word)
1009 * Remarks: if the word exists in the exclude dictionary, it will be removed from the
1010 * exclude dictionary
1012 ENCHANT_MODULE_EXPORT (void)
1013 enchant_dict_add (EnchantDict * dict, const char *const word,
1016 EnchantSession * session;
1018 g_return_if_fail (dict);
1019 g_return_if_fail (word);
1022 len = strlen (word);
1024 g_return_if_fail (len);
1025 g_return_if_fail (g_utf8_validate(word, len, NULL));
1027 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1028 enchant_session_clear_error (session);
1029 enchant_session_add_personal (session, word, len);
1030 enchant_session_remove_exclude (session, word, len);
1032 if (dict->add_to_personal)
1033 (*dict->add_to_personal) (dict, word, len);
1037 * enchant_dict_add_to_pwl
1038 * @dict: A non-null #EnchantDict
1039 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1040 * @len: The byte length of @word, or -1 for strlen (@word)
1042 * DEPRECATED. Please use enchant_dict_add() instead.
1044 ENCHANT_MODULE_EXPORT (void)
1045 enchant_dict_add_to_pwl (EnchantDict * dict, const char *const word,
1048 enchant_dict_add(dict,word,len);
1052 * enchant_dict_add_to_personal
1053 * @dict: A non-null #EnchantDict
1054 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1055 * @len: The byte length of @word, or -1 for strlen (@word)
1057 * DEPRECATED. Please use enchant_dict_add() instead.
1059 ENCHANT_MODULE_EXPORT (void)
1060 enchant_dict_add_to_personal (EnchantDict * dict, const char *const word,
1063 enchant_dict_add(dict, word, len);
1067 * enchant_dict_add_to_session
1068 * @dict: A non-null #EnchantDict
1069 * @word: The non-null word you wish to add to this spell-checking session, in UTF-8 encoding
1070 * @len: The byte length of @word, or -1 for strlen (@word)
1073 ENCHANT_MODULE_EXPORT (void)
1074 enchant_dict_add_to_session (EnchantDict * dict, const char *const word,
1077 EnchantSession * session;
1079 g_return_if_fail (dict);
1080 g_return_if_fail (word);
1083 len = strlen (word);
1085 g_return_if_fail (len);
1086 g_return_if_fail (g_utf8_validate(word, len, NULL));
1088 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1089 enchant_session_clear_error (session);
1091 enchant_session_add (session, word, len);
1092 if (dict->add_to_session)
1093 (*dict->add_to_session) (dict, word, len);
1097 * enchant_dict_is_added
1098 * @dict: A non-null #EnchantDict
1099 * @word: The word you wish to see if it has been added (to your session or dict) in UTF8 encoding
1100 * @len: the byte length of @word, or -1 for strlen (@word)
1102 ENCHANT_MODULE_EXPORT (int)
1103 enchant_dict_is_added (EnchantDict * dict, const char *const word,
1106 EnchantSession * session;
1108 g_return_val_if_fail (dict, 0);
1109 g_return_val_if_fail (word, 0);
1112 len = strlen (word);
1114 g_return_val_if_fail (len, 0);
1115 g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0);
1117 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1118 enchant_session_clear_error (session);
1120 return enchant_session_contains (session, word, len);
1124 * enchant_dict_is_in_session
1125 * @dict: A non-null #EnchantDict
1126 * @word: The word you wish to see if it's in your session in UTF8 encoding
1127 * @len: the byte length of @word, or -1 for strlen (@word)
1129 * DEPRECATED. Please use enchant_dict_is_added() instead.
1131 ENCHANT_MODULE_EXPORT (int)
1132 enchant_dict_is_in_session (EnchantDict * dict, const char *const word,
1135 return enchant_dict_is_added(dict, word, len);
1139 * enchant_dict_remove
1140 * @dict: A non-null #EnchantDict
1141 * @word: The non-null word you wish to add to your exclude dictionary and
1142 * remove from the personal dictionary, in UTF-8 encoding
1143 * @len: The byte length of @word, or -1 for strlen (@word)
1146 ENCHANT_MODULE_EXPORT (void)
1147 enchant_dict_remove (EnchantDict * dict, const char *const word,
1150 EnchantSession * session;
1152 g_return_if_fail (dict);
1153 g_return_if_fail (word);
1156 len = strlen (word);
1158 g_return_if_fail (len);
1159 g_return_if_fail (g_utf8_validate(word, len, NULL));
1161 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1162 enchant_session_clear_error (session);
1164 enchant_session_remove_personal (session, word, len);
1165 enchant_session_add_exclude(session, word, len);
1167 if (dict->add_to_exclude)
1168 (*dict->add_to_exclude) (dict, word, len);
1172 * enchant_dict_remove_from_session
1173 * @dict: A non-null #EnchantDict
1174 * @word: The non-null word you wish to exclude from this spell-checking session, in UTF-8 encoding
1175 * @len: The byte length of @word, or -1 for strlen (@word)
1178 ENCHANT_MODULE_EXPORT (void)
1179 enchant_dict_remove_from_session (EnchantDict * dict, const char *const word,
1182 EnchantSession * session;
1184 g_return_if_fail (dict);
1185 g_return_if_fail (word);
1188 len = strlen (word);
1190 g_return_if_fail (len);
1191 g_return_if_fail (g_utf8_validate(word, len, NULL));
1193 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1194 enchant_session_clear_error (session);
1196 enchant_session_remove (session, word, len);
1200 * enchant_dict_is_removed
1201 * @dict: A non-null #EnchantDict
1202 * @word: The word you wish to see if it has been removed (from your session or dict) in UTF8 encoding
1203 * @len: the byte length of @word, or -1 for strlen (@word)
1205 ENCHANT_MODULE_EXPORT (int)
1206 enchant_dict_is_removed (EnchantDict * dict, const char *const word,
1209 EnchantSession * session;
1211 g_return_val_if_fail (dict, 0);
1212 g_return_val_if_fail (word, 0);
1215 len = strlen (word);
1217 g_return_val_if_fail (len, 0);
1218 g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0);
1220 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1221 enchant_session_clear_error (session);
1223 return enchant_session_exclude (session, word, len);
1227 * enchant_dict_store_replacement
1228 * @dict: A non-null #EnchantDict
1229 * @mis: The non-null word you wish to add a correction for, in UTF-8 encoding
1230 * @mis_len: The byte length of @mis, or -1 for strlen (@mis)
1231 * @cor: The non-null correction word, in UTF-8 encoding
1232 * @cor_len: The byte length of @cor, or -1 for strlen (@cor)
1234 * Notes that you replaced @mis with @cor, so it's possibly more likely
1235 * that future occurrences of @mis will be replaced with @cor. So it might
1236 * bump @cor up in the suggestion list.
1238 ENCHANT_MODULE_EXPORT (void)
1239 enchant_dict_store_replacement (EnchantDict * dict,
1240 const char *const mis, ssize_t mis_len,
1241 const char *const cor, ssize_t cor_len)
1243 EnchantSession * session;
1245 g_return_if_fail (dict);
1246 g_return_if_fail (mis);
1247 g_return_if_fail (cor);
1250 mis_len = strlen (mis);
1253 cor_len = strlen (cor);
1255 g_return_if_fail (mis_len);
1256 g_return_if_fail (cor_len);
1258 g_return_if_fail (g_utf8_validate(mis, mis_len, NULL));
1259 g_return_if_fail (g_utf8_validate(cor, cor_len, NULL));
1261 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1262 enchant_session_clear_error (session);
1264 /* if it's not implemented, it's not worth emulating */
1265 if (dict->store_replacement)
1266 (*dict->store_replacement) (dict, mis, mis_len, cor, cor_len);
1270 * enchant_dict_free_string_list
1271 * @dict: A non-null #EnchantDict
1272 * @string_list: A non-null string list returned from enchant_dict_suggest
1274 * Releases the string list
1276 ENCHANT_MODULE_EXPORT (void)
1277 enchant_dict_free_string_list (EnchantDict * dict, char **string_list)
1279 EnchantSession * session;
1281 g_return_if_fail (dict);
1282 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1283 enchant_session_clear_error (session);
1284 g_strfreev(string_list);
1288 * enchant_dict_free_suggestions
1289 * @dict: A non-null #EnchantDict
1290 * @suggestions: The non-null suggestion list returned by
1291 * 'enchant_dict_suggest'
1293 * Releases the suggestions
1294 * This function is DEPRECATED. Please use enchant_dict_free_string_list() instead.
1296 ENCHANT_MODULE_EXPORT (void)
1297 enchant_dict_free_suggestions (EnchantDict * dict, char **suggestions)
1299 enchant_dict_free_string_list (dict, suggestions);
1303 * enchant_dict_describe
1304 * @broker: A non-null #EnchantDict
1305 * @dict: A non-null #EnchantDictDescribeFn
1306 * @user_data: Optional user-data
1308 * Describes an individual dictionary
1310 ENCHANT_MODULE_EXPORT (void)
1311 enchant_dict_describe (EnchantDict * dict,
1312 EnchantDictDescribeFn fn,
1315 EnchantSession * session;
1316 EnchantProvider * provider;
1319 const char * tag, * name, * desc, * file;
1321 g_return_if_fail (dict);
1322 g_return_if_fail (fn);
1324 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1325 enchant_session_clear_error (session);
1326 provider = session->provider;
1330 module = (GModule *) provider->enchant_private_data;
1331 file = g_module_name (module);
1332 name = (*provider->identify) (provider);
1333 desc = (*provider->describe) (provider);
1337 file = session->personal_filename;
1338 name = "Personal Wordlist";
1339 desc = "Personal Wordlist";
1342 tag = session->language_tag;
1343 (*fn) (tag, name, desc, file, user_data);
1346 /***********************************************************************************/
1347 /***********************************************************************************/
1350 enchant_broker_clear_error (EnchantBroker * broker)
1354 g_free (broker->error);
1355 broker->error = NULL;
1360 enchant_broker_set_error (EnchantBroker * broker, const char * const err)
1362 enchant_broker_clear_error (broker);
1363 broker->error = g_strdup (err);
1367 enchant_provider_is_valid(EnchantProvider * provider)
1369 if(provider == NULL)
1371 g_warning ("EnchantProvider cannot be NULL\n");
1375 if(provider->identify == NULL)
1377 g_warning ("EnchantProvider's identify method cannot be NULL\n");
1380 else if(!g_utf8_validate((*provider->identify)(provider), -1, NULL))
1382 g_warning ("EnchantProvider's identify method does not return valid utf8.\n");
1386 if(provider->describe == NULL)
1388 g_warning ("EnchantProvider's describe method cannot be NULL\n");
1391 else if(!g_utf8_validate((*provider->describe)(provider), -1, NULL))
1393 g_warning ("EnchantProvider's describe method does not return valid utf8.\n");
1401 enchant_load_providers_in_dir (EnchantBroker * broker, const char *dir_name)
1403 GModule *module = NULL;
1405 G_CONST_RETURN char *dir_entry;
1406 size_t entry_len, g_module_suffix_len;
1410 EnchantProvider *provider;
1411 EnchantProviderInitFunc init_func;
1412 EnchantPreConfigureFunc conf_func;
1414 dir = g_dir_open (dir_name, 0, NULL);
1418 g_module_suffix_len = strlen (G_MODULE_SUFFIX);
1420 while ((dir_entry = g_dir_read_name (dir)) != NULL)
1424 entry_len = strlen (dir_entry);
1425 if ((entry_len > g_module_suffix_len) &&
1426 !strcmp(dir_entry+(entry_len-g_module_suffix_len), G_MODULE_SUFFIX))
1429 /* Suppress error popups for failing to load plugins */
1430 UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
1432 filename = g_build_filename (dir_name, dir_entry, NULL);
1434 module = g_module_open (filename, (GModuleFlags) 0);
1438 (module, "init_enchant_provider", (gpointer *) (&init_func))
1441 provider = init_func ();
1442 if (!enchant_provider_is_valid(provider))
1444 g_warning ("Error loading plugin: %s's init_enchant_provider returned invalid provider.\n", dir_entry);
1447 if(provider->dispose)
1448 provider->dispose(provider);
1452 g_module_close (module);
1457 g_module_close (module);
1462 g_warning ("Error loading plugin: %s\n", g_module_error());
1467 /* Restore the original error mode */
1468 SetErrorMode(old_error_mode);
1473 /* optional entry point to allow modules to look for associated files
1476 (module, "configure_enchant_provider", (gpointer *) (&conf_func))
1479 conf_func (provider, dir_name);
1480 if (!enchant_provider_is_valid(provider))
1482 g_warning ("Error loading plugin: %s's configure_enchant_provider modified provider and it is now invalid.\n", dir_entry);
1483 if(provider->dispose)
1484 provider->dispose(provider);
1487 g_module_close (module);
1493 provider->enchant_private_data = (void *) module;
1494 provider->owner = broker;
1495 broker->provider_list = g_slist_append (broker->provider_list, (gpointer)provider);
1503 enchant_load_providers (EnchantBroker * broker)
1505 GSList *module_dirs, *iter;
1507 module_dirs = enchant_get_module_dirs();
1509 for (iter = module_dirs; iter; iter = iter->next)
1511 enchant_load_providers_in_dir (broker, iter->data);
1514 g_slist_foreach (module_dirs, (GFunc)g_free, NULL);
1515 g_slist_free (module_dirs);
1519 enchant_load_ordering_from_file (EnchantBroker * broker, const char * file)
1522 char * tag, * ordering;
1528 f = enchant_fopen (file, "r");
1532 while (NULL != fgets (line, sizeof(line), f)) {
1533 for (i = 0, len = strlen(line); i < len && line[i] != ':'; i++)
1538 tag = g_strndup (line, i);
1539 ordering = g_strndup (line+(i+1), len - i);
1541 enchant_broker_set_ordering (broker, tag, ordering);
1552 enchant_load_provider_ordering (EnchantBroker * broker)
1554 GSList *conf_dirs, *iter;
1556 broker->provider_ordering = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1558 /* we want the user's dirs to show up last, so they override system dirs */
1559 conf_dirs = g_slist_reverse (enchant_get_conf_dirs ());
1560 for (iter = conf_dirs; iter; iter = iter->next)
1562 char *ordering_file;
1563 ordering_file = g_build_filename (iter->data, "enchant.ordering", NULL);
1564 enchant_load_ordering_from_file (broker, ordering_file);
1565 g_free (ordering_file);
1568 g_slist_foreach (conf_dirs, (GFunc)g_free, NULL);
1569 g_slist_free (conf_dirs);
1573 enchant_get_ordered_providers (EnchantBroker * broker,
1574 const char * const tag)
1576 EnchantProvider *provider;
1577 GSList * list = NULL, * iter = NULL;
1579 char * ordering = NULL, ** tokens, *token;
1582 ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)tag);
1584 ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)"*");
1588 /* return an unordered copy of the list */
1589 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1590 list = g_slist_append (list, iter->data);
1594 tokens = g_strsplit (ordering, ",", 0);
1597 for (i = 0; tokens[i]; i++)
1599 token = g_strstrip(tokens[i]);
1601 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1603 provider = (EnchantProvider*)iter->data;
1605 if (provider && !strcmp (token, (*provider->identify)(provider)))
1606 list = g_slist_append (list, (gpointer)provider);
1610 g_strfreev (tokens);
1613 /* providers not in the list need to be appended at the end */
1614 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1616 if (!g_slist_find (list, iter->data))
1617 list = g_slist_append (list, iter->data);
1624 enchant_dict_destroyed (gpointer data)
1627 EnchantProvider *owner;
1628 EnchantSession *session;
1629 EnchantDictPrivateData *enchant_dict_private_data;
1631 g_return_if_fail (data);
1633 dict = (EnchantDict *) data;
1634 enchant_dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data;
1635 session = enchant_dict_private_data->session;
1636 owner = session->provider;
1638 if (owner && owner->dispose_dict)
1639 (*owner->dispose_dict) (owner, dict);
1640 else if(session->is_pwl)
1643 g_free(enchant_dict_private_data);
1645 enchant_session_destroy (session);
1649 enchant_provider_free (gpointer data, gpointer user_data)
1651 EnchantProvider *provider;
1654 g_return_if_fail (data);
1656 provider = (EnchantProvider *) data;
1657 module = (GModule *) provider->enchant_private_data;
1659 if (provider->dispose)
1660 (*provider->dispose) (provider);
1662 /* close module only after invoking dispose */
1663 g_module_close (module);
1667 * enchant_broker_init
1669 * Returns: A new broker object capable of requesting
1672 ENCHANT_MODULE_EXPORT (EnchantBroker *)
1673 enchant_broker_init (void)
1675 EnchantBroker *broker = NULL;
1677 g_return_val_if_fail (g_module_supported (), NULL);
1679 #ifdef ENABLE_BINRELOC
1681 static gboolean binreloc_initialized = FALSE;
1683 if (!binreloc_initialized)
1685 (void)gbr_init_lib (NULL);
1686 binreloc_initialized = TRUE;
1691 broker = g_new0 (EnchantBroker, 1);
1693 broker->dict_map = g_hash_table_new_full (g_str_hash, g_str_equal,
1694 g_free, enchant_dict_destroyed);
1695 broker->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1696 enchant_load_providers (broker);
1697 enchant_load_provider_ordering (broker);
1703 * enchant_broker_free
1704 * @broker: A non-null #EnchantBroker
1706 * Destroys the broker object. Must only be called once per broker init
1708 ENCHANT_MODULE_EXPORT (void)
1709 enchant_broker_free (EnchantBroker * broker)
1713 g_return_if_fail (broker);
1715 n_remaining = g_hash_table_size (broker->dict_map);
1718 g_warning ("%u dictionaries weren't free'd.\n", n_remaining);
1721 /* will destroy any remaining dictionaries for us */
1722 g_hash_table_destroy (broker->dict_map);
1723 g_hash_table_destroy (broker->provider_ordering);
1724 g_hash_table_destroy (broker->params);
1726 g_slist_foreach (broker->provider_list, enchant_provider_free, NULL);
1727 g_slist_free (broker->provider_list);
1729 enchant_broker_clear_error (broker);
1735 * enchant_broker_request_pwl_dict
1737 * PWL is a personal wordlist file, 1 entry per line
1739 * @pwl: A non-null pathname in the GLib file name encoding (UTF-8 on Windows)
1740 * to the personal wordlist file
1742 * Returns: An EnchantDict. This dictionary is reference counted.
1744 ENCHANT_MODULE_EXPORT (EnchantDict *)
1745 enchant_broker_request_pwl_dict (EnchantBroker * broker, const char *const pwl)
1747 EnchantSession *session;
1748 EnchantDictPrivateData *enchant_dict_private_data;
1749 EnchantDict *dict = NULL;
1751 g_return_val_if_fail (broker, NULL);
1752 g_return_val_if_fail (pwl && strlen(pwl), NULL);
1754 enchant_broker_clear_error (broker);
1756 dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) pwl);
1758 ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++;
1762 /* since the broker pwl file is a read/write file (there is no readonly dictionary associated)
1763 * there is no need for complementary exclude file to add a word to. The word just needs to be
1764 * removed from the broker pwl file
1766 session = enchant_session_new_with_pwl (NULL, pwl, NULL, "Personal Wordlist", TRUE);
1769 broker->error = g_strdup_printf ("Couldn't open personal wordlist '%s'", pwl);
1773 session->is_pwl = 1;
1775 dict = g_new0 (EnchantDict, 1);
1776 enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1);
1777 enchant_dict_private_data->reference_count = 1;
1778 enchant_dict_private_data->session = session;
1779 dict->enchant_private_data = (void *)enchant_dict_private_data;
1782 g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (pwl), dict);
1787 static EnchantDict *
1788 _enchant_broker_request_dict (EnchantBroker * broker, const char *const tag)
1794 dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) tag);
1796 ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++;
1800 list = enchant_get_ordered_providers (broker, tag);
1801 for (listIter = list; listIter != NULL; listIter = g_slist_next (listIter))
1803 EnchantProvider * provider;
1805 provider = (EnchantProvider *) listIter->data;
1807 if (provider->request_dict)
1809 dict = (*provider->request_dict) (provider, tag);
1813 EnchantSession *session;
1814 EnchantDictPrivateData *enchant_dict_private_data;
1816 session = enchant_session_new (provider, tag);
1817 enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1);
1818 enchant_dict_private_data->reference_count = 1;
1819 enchant_dict_private_data->session = session;
1820 dict->enchant_private_data = (void *)enchant_dict_private_data;
1821 g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (tag), dict);
1827 g_slist_free (list);
1833 * enchant_broker_request_dict
1834 * @broker: A non-null #EnchantBroker
1835 * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...)
1837 * Returns: An #EnchantDict, or %null if no suitable dictionary could be found. This dictionary is reference counted.
1839 ENCHANT_MODULE_EXPORT (EnchantDict *)
1840 enchant_broker_request_dict (EnchantBroker * broker, const char *const tag)
1842 EnchantDict *dict = NULL;
1843 char * normalized_tag;
1845 g_return_val_if_fail (broker, NULL);
1846 g_return_val_if_fail (tag && strlen(tag), NULL);
1848 enchant_broker_clear_error (broker);
1850 normalized_tag = enchant_normalize_dictionary_tag (tag);
1851 if(!enchant_is_valid_dictionary_tag(normalized_tag))
1853 enchant_broker_set_error (broker, "invalid tag character found");
1855 else if ((dict = _enchant_broker_request_dict (broker, normalized_tag)) == NULL)
1857 char * iso_639_only_tag;
1859 iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag);
1861 dict = _enchant_broker_request_dict (broker, iso_639_only_tag);
1863 g_free (iso_639_only_tag);
1866 g_free (normalized_tag);
1872 * enchant_broker_describe
1873 * @broker: A non-null #EnchantBroker
1874 * @fn: A non-null #EnchantBrokerDescribeFn
1875 * @user_data: Optional user-data
1877 * Enumerates the Enchant providers and tells
1878 * you some rudimentary information about them.
1880 ENCHANT_MODULE_EXPORT (void)
1881 enchant_broker_describe (EnchantBroker * broker,
1882 EnchantBrokerDescribeFn fn,
1886 EnchantProvider *provider;
1889 const char * name, * desc, * file;
1891 g_return_if_fail (broker);
1892 g_return_if_fail (fn);
1894 enchant_broker_clear_error (broker);
1896 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
1898 provider = (EnchantProvider *) list->data;
1899 module = (GModule *) provider->enchant_private_data;
1901 name = (*provider->identify) (provider);
1902 desc = (*provider->describe) (provider);
1903 file = g_module_name (module);
1905 (*fn) (name, desc, file, user_data);
1910 * enchant_broker_list_dicts
1911 * @broker: A non-null #EnchantBroker
1912 * @fn: A non-null #EnchantDictDescribeFn
1913 * @user_data: Optional user-data
1915 * Enumerates the dictionaries available from
1916 * all Enchant providers.
1918 ENCHANT_MODULE_EXPORT (void)
1919 enchant_broker_list_dicts (EnchantBroker * broker,
1920 EnchantDictDescribeFn fn,
1926 g_return_if_fail (broker);
1927 g_return_if_fail (fn);
1929 tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1931 enchant_broker_clear_error (broker);
1933 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
1935 EnchantProvider *provider;
1938 provider = (EnchantProvider *) list->data;
1939 module = (GModule *) provider->enchant_private_data;
1941 if (provider->list_dicts)
1943 const char * tag, * name, * desc, * file;
1947 dicts = (*provider->list_dicts) (provider, &n_dicts);
1948 name = (*provider->identify) (provider);
1949 desc = (*provider->describe) (provider);
1950 file = g_module_name (module);
1952 for (i = 0; i < n_dicts; i++)
1955 if(enchant_is_valid_dictionary_tag(tag) &&
1956 !g_hash_table_lookup (tags, tag))
1958 g_hash_table_insert (tags, g_strdup (tag), GINT_TO_POINTER(TRUE));
1959 (*fn) (tag, name, desc, file, user_data);
1963 enchant_provider_free_string_list (provider, dicts);
1967 g_hash_table_destroy (tags);
1971 * enchant_broker_free_dict
1972 * @broker: A non-null #EnchantBroker
1973 * @dict: A non-null #EnchantDict
1975 * Releases the dictionary when you are done using it. Must only be called once per dictionary request
1977 ENCHANT_MODULE_EXPORT (void)
1978 enchant_broker_free_dict (EnchantBroker * broker, EnchantDict * dict)
1980 EnchantSession * session;
1981 EnchantDictPrivateData * dict_private_data;
1983 g_return_if_fail (broker);
1984 g_return_if_fail (dict);
1986 enchant_broker_clear_error (broker);
1988 dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data;
1989 dict_private_data->reference_count--;
1990 if(dict_private_data->reference_count == 0)
1992 session = dict_private_data->session;
1994 if (session->provider)
1995 g_hash_table_remove (broker->dict_map, session->language_tag);
1997 g_hash_table_remove (broker->dict_map, session->personal_filename);
2002 _enchant_provider_dictionary_exists (EnchantProvider * provider,
2003 const char * const tag)
2007 if (provider->dictionary_exists)
2009 exists = (*provider->dictionary_exists) (provider, tag);
2011 else if (provider->list_dicts)
2016 dicts = (*provider->list_dicts) (provider, &n_dicts);
2018 for (i = 0; (i < n_dicts) && !exists; i++)
2020 if (!strcmp(dicts[i], tag))
2024 enchant_provider_free_string_list (provider, dicts);
2026 else if (provider->request_dict)
2030 dict = (*provider->request_dict) (provider, tag);
2033 if (provider->dispose_dict)
2034 (*provider->dispose_dict) (provider, dict);
2043 _enchant_broker_dict_exists (EnchantBroker * broker,
2044 const char * const tag)
2048 /* don't query the providers if it is an empty string */
2049 if (tag == NULL || *tag == '\0') {
2053 /* don't query the providers if we can just do a quick map lookup */
2054 if (g_hash_table_lookup (broker->dict_map, (gpointer) tag) != NULL) {
2058 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
2060 EnchantProvider * provider;
2062 provider = (EnchantProvider *) list->data;
2064 if (_enchant_provider_dictionary_exists (provider, tag))
2074 * enchant_broker_dict_exists
2075 * @broker: A non-null #EnchantBroker
2076 * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...)
2078 * Return existance of the requested dictionary (1 == true, 0 == false)
2080 ENCHANT_MODULE_EXPORT (int)
2081 enchant_broker_dict_exists (EnchantBroker * broker,
2082 const char * const tag)
2084 char * normalized_tag;
2087 g_return_val_if_fail (broker, 0);
2088 g_return_val_if_fail (tag && strlen(tag), 0);
2090 enchant_broker_clear_error (broker);
2092 normalized_tag = enchant_normalize_dictionary_tag (tag);
2094 if(!enchant_is_valid_dictionary_tag(normalized_tag))
2096 enchant_broker_set_error (broker, "invalid tag character found");
2098 else if ((exists = _enchant_broker_dict_exists (broker, normalized_tag)) == 0)
2100 char * iso_639_only_tag;
2102 iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag);
2104 if (strcmp (normalized_tag, iso_639_only_tag) != 0)
2106 exists = _enchant_broker_dict_exists (broker, iso_639_only_tag);
2109 g_free (iso_639_only_tag);
2112 g_free (normalized_tag);
2117 * enchant_broker_set_ordering
2118 * @broker: A non-null #EnchantBroker
2119 * @tag: A non-null language tag (en_US)
2120 * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell)
2122 * Declares a preference of dictionaries to use for the language
2123 * described/referred to by @tag. The ordering is a comma delimited
2124 * list of provider names. As a special exception, the "*" tag can
2125 * be used as a language tag to declare a default ordering for any
2126 * language that does not explictly declare an ordering.
2128 ENCHANT_MODULE_EXPORT (void)
2129 enchant_broker_set_ordering (EnchantBroker * broker,
2130 const char * const tag,
2131 const char * const ordering)
2134 char * ordering_dupl;
2136 g_return_if_fail (broker);
2137 g_return_if_fail (tag && strlen(tag));
2138 g_return_if_fail (ordering && strlen(ordering));
2140 enchant_broker_clear_error (broker);
2142 tag_dupl = enchant_normalize_dictionary_tag (tag);
2144 ordering_dupl = g_strdup (ordering);
2145 ordering_dupl = g_strstrip (ordering_dupl);
2147 if (tag_dupl && strlen(tag_dupl) &&
2148 ordering_dupl && strlen(ordering_dupl))
2150 /* we will free ordering_dupl && tag_dupl when the hash is destroyed */
2151 g_hash_table_insert (broker->provider_ordering, (gpointer)tag_dupl,
2152 (gpointer)(ordering_dupl));
2157 g_free (ordering_dupl);
2162 * enchant_provider_set_error
2163 * @provider: A non-null provider
2164 * @err: A non-null error message
2166 * Sets the current runtime error to @err. This API is private to
2169 ENCHANT_MODULE_EXPORT(void)
2170 enchant_provider_set_error (EnchantProvider * provider, const char * const err)
2172 EnchantBroker * broker;
2174 g_return_if_fail (provider);
2175 g_return_if_fail (err);
2176 g_return_if_fail (g_utf8_validate(err, -1, NULL));
2178 broker = provider->owner;
2179 g_return_if_fail (broker);
2181 enchant_broker_set_error (broker, err);
2185 * enchant_broker_get_error
2186 * @broker: A non-null broker
2188 * Returns a const char string or NULL describing the last exception in UTF8 encoding.
2189 * WARNING: error is transient and is likely cleared as soon as the
2190 * next broker operation happens
2192 ENCHANT_MODULE_EXPORT(char *)
2193 enchant_broker_get_error (EnchantBroker * broker)
2195 g_return_val_if_fail (broker, NULL);
2197 return broker->error;
2200 /* private. returned string should be free'd with g_free */
2201 ENCHANT_MODULE_EXPORT(char *)
2202 enchant_get_user_language(void)
2204 char * locale = NULL;
2206 #if defined(G_OS_WIN32)
2208 locale = g_win32_getlocale ();
2212 locale = g_strdup (g_getenv ("LANG"));
2214 #if defined(HAVE_LC_MESSAGES)
2216 locale = g_strdup (setlocale (LC_MESSAGES, NULL));
2220 locale = g_strdup (setlocale (LC_ALL, NULL));
2222 if(!locale || strcmp(locale, "C") == 0) {
2224 locale = g_strdup("en");
2232 * enchant_get_prefix_dir
2234 * Returns a string giving the location of the base directory
2235 * of the enchant installation. This corresponds roughly to
2236 * the --prefix option given to ./configure when enchant is
2237 * compiled, except it is determined at runtime based on the location
2238 * of the enchant library.
2240 * Returns: the prefix dir if it can be determined, or %null otherwise. Must be free'd.
2242 * This API is private to the providers.
2245 ENCHANT_MODULE_EXPORT (char *)
2246 enchant_get_prefix_dir(void)
2248 char * prefix = NULL;
2252 /* Dynamically locate library and return containing directory */
2253 WCHAR dll_path[MAX_PATH];
2255 if(GetModuleFileNameW(s_hModule,dll_path,MAX_PATH))
2257 gchar* utf8_dll_path = g_utf16_to_utf8 (dll_path, -1, NULL, NULL, NULL);
2258 prefix = g_path_get_dirname(utf8_dll_path);
2259 g_free(utf8_dll_path);
2260 /* Strip off "bin" subfolder if present */
2261 if (strlen(prefix) >=6 &&
2262 G_IS_DIR_SEPARATOR(prefix[strlen(prefix)-4]) &&
2263 g_ascii_strcasecmp(prefix+strlen(prefix)-3, "bin") == 0)
2264 prefix[strlen(prefix)-4] = '\0';
2269 #if defined(ENABLE_BINRELOC)
2271 /* Use standard binreloc PREFIX macro */
2272 prefix = gbr_find_prefix(NULL);
2276 #if defined(ENCHANT_PREFIX_DIR)
2278 prefix = g_strdup (ENCHANT_PREFIX_DIR);
2285 ENCHANT_MODULE_EXPORT(char *)
2286 enchant_broker_get_param (EnchantBroker * broker, const char * const param_name)
2288 g_return_val_if_fail (broker, NULL);
2289 g_return_val_if_fail (param_name && *param_name, NULL);
2291 return g_hash_table_lookup (broker->params, param_name);
2294 ENCHANT_MODULE_EXPORT(void)
2295 enchant_broker_set_param (EnchantBroker * broker, const char * const param_name, const char * const param_value)
2297 g_return_if_fail (broker);
2298 g_return_if_fail (param_name && *param_name);
2300 if (param_value == NULL || *param_value == '\0')
2301 g_hash_table_remove (broker->params, param_name);
2303 g_hash_table_insert (broker->params, g_strdup (param_name), g_strdup (param_value));
2306 ENCHANT_MODULE_EXPORT (GSList *)
2307 enchant_get_dirs_from_param (EnchantBroker * broker, const char * const param_name)
2309 const char *param_value;
2311 GSList *dirs = NULL;
2313 param_value = enchant_broker_get_param (broker, param_name);
2314 if (param_value == NULL)
2318 tokens = g_strsplit (param_value, ";", 0);
2320 tokens = g_strsplit (param_value, ":", 0);
2322 if (tokens != NULL) {
2324 for (i = 0; tokens[i]; i++)
2326 char *token = g_strstrip(tokens[i]);
2327 dirs = g_slist_append (dirs, g_strdup (token));
2330 g_strfreev (tokens);
2336 ENCHANT_MODULE_EXPORT(char *)
2337 enchant_get_version (void) {
2338 return ENCHANT_VERSION_STRING;