1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* enchant
3 * Copyright (C) 2003, 2004 Dom Lachowicz
4 *
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.
9 *
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.
14 *
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.
19 *
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.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 #include <glib.h>
37 #include <gmodule.h>
38 #include <glib/gstdio.h>
39 #include <locale.h>
40
41 #include "enchant.h"
42 #include "enchant-provider.h"
43 #include "pwl.h"
44
45 #ifdef XP_TARGET_COCOA
46 #import "enchant_cocoa.h"
47 #endif
48
49 #ifdef XP_TARGET_COCOA
50 #define ENCHANT_USER_PATH_EXTENSION "Library", "Application Support", "Enchant"
51 #elif defined(_WIN32)
52 #define ENCHANT_USER_PATH_EXTENSION "enchant"
53 #else
54 #define ENCHANT_USER_PATH_EXTENSION ".enchant"
55 #endif
56
57 #ifdef ENABLE_BINRELOC
58 #include "prefix.h"
59 #endif
60
61 ENCHANT_PLUGIN_DECLARE("Enchant")
62
63 static char *
64 enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key);
65
66 /********************************************************************************/
67 /********************************************************************************/
68
69 struct str_enchant_broker
70 {
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 */
74 GHashTable *params;
75
76 gchar * error;
77 };
78
79 typedef struct str_enchant_session
80 {
81 GHashTable *session_include;
82 GHashTable *session_exclude;
83 EnchantPWL *personal;
84 EnchantPWL *exclude;
85
86 char * personal_filename;
87 char * exclude_filename;
88 char * language_tag;
89
90 char * error;
91
92 gboolean is_pwl;
93
94 EnchantProvider * provider;
95 } EnchantSession;
96
97 typedef struct str_enchant_dict_private_data
98 {
99 unsigned int reference_count;
100 EnchantSession* session;
101 } EnchantDictPrivateData;
102
103 typedef EnchantProvider *(*EnchantProviderInitFunc) (void);
104 typedef void (*EnchantPreConfigureFunc) (EnchantProvider * provider, const char * module_dir);
105
106 /********************************************************************************/
107 /********************************************************************************/
108
109 #ifdef _WIN32
110 #define path_cmp g_utf8_collate
111 #else
112 #define path_cmp strcmp
113 #endif
114
enchant_slist_prepend_unique_path(GSList * slist,gchar * data)115 static GSList* enchant_slist_prepend_unique_path (GSList *slist, gchar* data)
116 {
117 if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp))
118 {
119 return g_slist_prepend (slist, data);
120 }
121 else
122 {
123 g_free (data);
124 return slist;
125 }
126 }
127
enchant_slist_append_unique_path(GSList * slist,gchar * data)128 static GSList* enchant_slist_append_unique_path (GSList *slist, gchar* data)
129 {
130 if (NULL == g_slist_find_custom (slist, data, (GCompareFunc)path_cmp))
131 {
132 return g_slist_append (slist, data);
133 }
134 else
135 {
136 g_free (data);
137 return slist;
138 }
139 }
140
141 static GSList *
_enchant_get_user_home_dirs(void)142 _enchant_get_user_home_dirs (void)
143 {
144 GSList *dirs = NULL;
145 const char* home_dir;
146 char *tmp;
147
148 tmp = enchant_get_registry_value_ex (1, "Config", "Home_Dir");
149 if (tmp)
150 dirs = enchant_slist_append_unique_path (dirs, tmp);
151
152 home_dir = g_get_home_dir ();
153 if (home_dir)
154 dirs = enchant_slist_append_unique_path (dirs, g_strdup (home_dir));
155
156 return dirs;
157 }
158
159 static void
_enchant_ensure_dir_exists(const char * dir)160 _enchant_ensure_dir_exists (const char* dir)
161 {
162 if (dir && !g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
163 {
164 (void)g_remove (dir);
165 g_mkdir_with_parents (dir, 0700);
166 }
167 }
168
169 static GSList *
enchant_get_user_dirs(void)170 enchant_get_user_dirs (void)
171 {
172 GSList *user_dirs = NULL;
173
174 {
175 const char * user_config_dir;
176
177 user_config_dir = g_get_user_config_dir();
178
179 if (user_config_dir)
180 user_dirs = enchant_slist_append_unique_path (user_dirs, g_build_filename (user_config_dir,
181 "enchant",
182 NULL));
183 }
184
185 {
186 GSList *home_dirs = NULL, *dir;
187 home_dirs = _enchant_get_user_home_dirs ();
188
189 for (dir = home_dirs; dir; dir = dir->next)
190 {
191 user_dirs = enchant_slist_append_unique_path (user_dirs,
192 g_build_filename (dir->data,
193 ENCHANT_USER_PATH_EXTENSION,
194 NULL));
195 }
196
197 g_slist_foreach (home_dirs, (GFunc)g_free, NULL);
198 g_slist_free (home_dirs);
199 }
200
201 return user_dirs;
202 }
203
204 /* place to look for system level providers */
205 static GSList *
enchant_get_module_dirs(void)206 enchant_get_module_dirs (void)
207 {
208 GSList *module_dirs = NULL;
209
210 char * module_dir = NULL;
211 char * prefix = NULL;
212
213 {
214 char* user_module_dir;
215
216 user_module_dir = enchant_get_registry_value_ex (1, "Config", "Module_Dir");
217 if (user_module_dir)
218 module_dirs = enchant_slist_append_unique_path (module_dirs, user_module_dir);
219 }
220
221 #ifdef XP_TARGET_COCOA
222 module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup ([[EnchantResourceProvider instance] moduleFolder]));
223 #endif
224
225 {
226 GSList *user_dirs, *iter;
227
228 user_dirs = enchant_get_user_dirs();
229
230 for (iter = user_dirs; iter; iter = iter->next)
231 module_dirs = enchant_slist_append_unique_path (module_dirs, iter->data);
232
233 g_slist_free (user_dirs);
234 }
235
236 /* Look for explicitly set registry values */
237 module_dir = enchant_get_registry_value_ex (0, "Config", "Module_Dir");
238 if (module_dir)
239 module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir);
240
241 #if defined(ENCHANT_GLOBAL_MODULE_DIR)
242 module_dirs = enchant_slist_append_unique_path (module_dirs, g_strdup (ENCHANT_GLOBAL_MODULE_DIR));
243 #else
244 /* Dynamically locate library and search for modules relative to it. */
245 prefix = enchant_get_prefix_dir();
246 if(prefix)
247 {
248 module_dir = g_build_filename(prefix,"lib","enchant",NULL);
249 g_free(prefix);
250 module_dirs = enchant_slist_append_unique_path (module_dirs, module_dir);
251 }
252 #endif
253
254 return module_dirs;
255 }
256
257 static GSList *
enchant_get_conf_dirs(void)258 enchant_get_conf_dirs (void)
259 {
260 GSList *conf_dirs = NULL, *user_conf_dirs, *iter;
261 char * ordering_dir = NULL, * prefix = NULL;
262
263 user_conf_dirs = enchant_get_user_config_dirs();
264
265 for (iter = user_conf_dirs; iter != NULL; iter = iter->next)
266 {
267 conf_dirs = enchant_slist_append_unique_path (conf_dirs, iter->data);
268 }
269
270 g_slist_free (user_conf_dirs);
271
272 #ifdef XP_TARGET_COCOA
273 conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup ([[EnchantResourceProvider instance] configFolder]));
274 #endif
275
276 /* Look for explicitly set registry values */
277 ordering_dir = enchant_get_registry_value_ex (0, "Config", "Data_Dir");
278 if (ordering_dir)
279 conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir);
280
281 /* Dynamically locate library and search for files relative to it. */
282 prefix = enchant_get_prefix_dir();
283 if(prefix)
284 {
285 ordering_dir = g_build_filename(prefix,"share","enchant",NULL);
286 g_free(prefix);
287 conf_dirs = enchant_slist_append_unique_path (conf_dirs, ordering_dir);
288 }
289
290 #if defined(ENCHANT_GLOBAL_ORDERING)
291 conf_dirs = enchant_slist_append_unique_path (conf_dirs, g_strdup (ENCHANT_GLOBAL_ORDERING));
292 #endif
293
294 return conf_dirs;
295 }
296
297 ENCHANT_MODULE_EXPORT(FILE *)
enchant_fopen(const gchar * filename,const gchar * mode)298 enchant_fopen (const gchar *filename, const gchar *mode)
299 {
300 #ifdef G_OS_WIN32
301 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
302 wchar_t *wmode;
303 FILE *retval;
304 int save_errno;
305
306 if (wfilename == NULL)
307 {
308 errno = EINVAL;
309 return NULL;
310 }
311
312 wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
313
314 if (wmode == NULL)
315 {
316 g_free (wfilename);
317 errno = EINVAL;
318 return NULL;
319 }
320
321 retval = _wfopen (wfilename, wmode);
322 save_errno = errno;
323
324 g_free (wfilename);
325 g_free (wmode);
326
327 errno = save_errno;
328 return retval;
329 #else
330 return fopen (filename, mode);
331 #endif
332 }
333
334 /**
335 * enchant_get_user_config_dir
336 *
337 * Returns: the user's enchant directory, or %null. Returned value
338 * must be free'd.
339 *
340 * The enchant directory is the place where enchant finds user
341 * dictionaries and settings related to enchant
342 *
343 * This API is private to the providers.
344 */
345 ENCHANT_MODULE_EXPORT (GSList *)
enchant_get_user_config_dirs(void)346 enchant_get_user_config_dirs (void)
347 {
348 GSList *dirs;
349 char* user_config;
350
351 dirs = enchant_get_user_dirs();
352
353 user_config = enchant_get_registry_value_ex (1, "Config", "Data_Dir");
354 if (user_config)
355 dirs = enchant_slist_prepend_unique_path (dirs, user_config);
356
357 return dirs;
358 }
359
360 /*
361 * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd.
362 */
363 static char *
enchant_get_registry_value_ex(int current_user,const char * const prefix,const char * const key)364 enchant_get_registry_value_ex (int current_user, const char * const prefix, const char * const key)
365 {
366 #ifndef _WIN32
367 /* TODO: GConf? KConfig? */
368 return NULL;
369 #else
370 HKEY hKey;
371 HKEY baseKey;
372 unsigned long lType;
373 DWORD dwSize;
374 char* keyName;
375 WCHAR* wszValue = NULL;
376 char* szValue = NULL;
377 gunichar2 * uKeyName;
378 gunichar2 * uKey;
379
380 if (current_user)
381 baseKey = HKEY_CURRENT_USER;
382 else
383 baseKey = HKEY_LOCAL_MACHINE;
384
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);
388
389 if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
390 {
391 /* Determine size of string */
392 if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS)
393 {
394 wszValue = g_new0(WCHAR, dwSize + 1);
395 RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize);
396 }
397 RegCloseKey(hKey);
398 }
399
400 if(wszValue && *wszValue)
401 szValue = g_utf16_to_utf8 (wszValue, -1, NULL, NULL, NULL);
402
403 g_free(keyName);
404 g_free(uKeyName);
405 g_free(uKey);
406 g_free(wszValue);
407
408 return szValue;
409 #endif
410 }
411
412 /**
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
416 *
417 * Returns: the value if it exists and is not an empty string ("") or %null otherwise. Must be free'd.
418 *
419 * This API is private to the providers.
420 */
421 ENCHANT_MODULE_EXPORT (char *)
enchant_get_registry_value(const char * const prefix,const char * const key)422 enchant_get_registry_value (const char * const prefix, const char * const key)
423 {
424 char *val;
425
426 g_return_val_if_fail (prefix, NULL);
427 g_return_val_if_fail (key, NULL);
428
429 val = enchant_get_registry_value_ex(1, prefix, key);
430 if(val == NULL) {
431 val = enchant_get_registry_value_ex (0, prefix, key);
432 }
433 return val;
434 }
435
436 /********************************************************************************/
437 /********************************************************************************/
438
439 static gchar*
enchant_modify_string_chars(gchar * str,gssize len,gchar (* function)(gchar))440 enchant_modify_string_chars (gchar *str,
441 gssize len,
442 gchar (*function)(gchar))
443 {
444 gchar* it, *end;
445
446 g_return_val_if_fail (str != NULL, NULL);
447
448 if (len < 0)
449 len = strlen (str);
450
451 end = str + len;
452
453 for (it = str; it != end; ++it)
454 *it = function (*it);
455
456 return str;
457 }
458
459 static gchar*
enchant_ascii_strup(gchar * str,gssize len)460 enchant_ascii_strup (gchar *str,
461 gssize len)
462 {
463 return enchant_modify_string_chars(str, len, g_ascii_toupper);
464 }
465
466 static gchar*
enchant_ascii_strdown(gchar * str,gssize len)467 enchant_ascii_strdown (gchar *str,
468 gssize len)
469 {
470 return enchant_modify_string_chars(str, len, g_ascii_tolower);
471 }
472
473 /* returns TRUE if tag is valid
474 * for requires alphanumeric ASCII or underscore
475 */
476 static int
enchant_is_valid_dictionary_tag(const char * const tag)477 enchant_is_valid_dictionary_tag(const char * const tag)
478 {
479 const char * it;
480 for (it = tag; *it; ++it)
481 {
482 if(!g_ascii_isalnum(*it) && *it != '_')
483 return 0;
484 }
485
486 return it != tag; /*empty tag invalid*/
487 }
488
489 static char *
enchant_normalize_dictionary_tag(const char * const dict_tag)490 enchant_normalize_dictionary_tag (const char * const dict_tag)
491 {
492 char * new_tag = g_strdup (dict_tag);
493 char * needle;
494
495 new_tag = g_strstrip (new_tag);
496
497 /* strip off en_GB@euro */
498 if ((needle = strchr (new_tag, '@')) != NULL)
499 *needle = '\0';
500
501 /* strip off en_GB.UTF-8 */
502 if ((needle = strchr (new_tag, '.')) != NULL)
503 *needle = '\0';
504
505 /* turn en-GB into en_GB */
506 if ((needle = strchr (new_tag, '-')) != NULL)
507 *needle = '_';
508
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);
512 ++needle;
513 /* everything after first '_' is converted to upper case */
514 enchant_ascii_strup(needle, -1);
515 }
516 else {
517 enchant_ascii_strdown(new_tag, -1);
518 }
519
520 return new_tag;
521 }
522
523 static char *
enchant_iso_639_from_tag(const char * const dict_tag)524 enchant_iso_639_from_tag (const char * const dict_tag)
525 {
526 char * new_tag = g_strdup (dict_tag);
527 char * needle;
528
529 if ((needle = strchr (new_tag, '_')) != NULL)
530 *needle = '\0';
531
532 return new_tag;
533 }
534
535 static void
enchant_session_destroy(EnchantSession * session)536 enchant_session_destroy (EnchantSession * session)
537 {
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);
545
546 if (session->error)
547 g_free (session->error);
548
549 g_free (session);
550 }
551
552 static EnchantSession *
enchant_session_new_with_pwl(EnchantProvider * provider,const char * const pwl,const char * const excl,const char * const lang,gboolean fail_if_no_pwl)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)
558 {
559 EnchantSession * session;
560 EnchantPWL *personal = NULL;
561 EnchantPWL *exclude = NULL;
562
563 if (pwl)
564 personal = enchant_pwl_init_with_file (pwl);
565
566 if (personal == NULL) {
567 if (fail_if_no_pwl)
568 return NULL;
569 else
570 personal = enchant_pwl_init ();
571 }
572
573 if (excl)
574 exclude = enchant_pwl_init_with_file (excl);
575 if (exclude == NULL)
576 exclude = enchant_pwl_init ();
577
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);
587
588 return session;
589 }
590
591 static EnchantSession *
_enchant_session_new(EnchantProvider * provider,const char * const user_config_dir,const char * const lang,gboolean fail_if_no_pwl)592 _enchant_session_new (EnchantProvider *provider, const char * const user_config_dir,
593 const char * const lang, gboolean fail_if_no_pwl)
594 {
595 char *filename, *dic, *excl;
596 EnchantSession * session;
597
598 if (!user_config_dir || !lang)
599 return NULL;
600
601 filename = g_strdup_printf ("%s.dic", lang);
602 dic = g_build_filename (user_config_dir, filename, NULL);
603 g_free (filename);
604
605 filename = g_strdup_printf ("%s.exc", lang);
606 excl = g_build_filename (user_config_dir, filename, NULL);
607 g_free (filename);
608
609 session = enchant_session_new_with_pwl (provider, dic, excl, lang, fail_if_no_pwl);
610
611 g_free (dic);
612 g_free (excl);
613
614 return session;
615 }
616
617 static EnchantSession *
enchant_session_new(EnchantProvider * provider,const char * const lang)618 enchant_session_new (EnchantProvider *provider, const char * const lang)
619 {
620 EnchantSession * session = NULL;
621 GSList *user_config_dirs, *iter;
622
623 user_config_dirs = enchant_get_user_config_dirs ();
624 for (iter = user_config_dirs; iter != NULL && session == NULL; iter = iter->next)
625 {
626 session =_enchant_session_new (provider, iter->data, lang, TRUE);
627 }
628
629 if (session == NULL && user_config_dirs != NULL)
630 {
631 _enchant_ensure_dir_exists (user_config_dirs->data);
632
633 session =_enchant_session_new (provider, user_config_dirs->data, lang, FALSE);
634 }
635
636 g_slist_foreach (user_config_dirs, (GFunc)g_free, NULL);
637 g_slist_free (user_config_dirs);
638
639
640 return session;
641 }
642
643 static void
enchant_session_add(EnchantSession * session,const char * const word,size_t len)644 enchant_session_add (EnchantSession * session, const char * const word, size_t len)
645 {
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));
649 }
650
651 static void
enchant_session_remove(EnchantSession * session,const char * const word,size_t len)652 enchant_session_remove (EnchantSession * session, const char * const word, size_t len)
653 {
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));
657 }
658
659 static void
enchant_session_add_personal(EnchantSession * session,const char * const word,size_t len)660 enchant_session_add_personal (EnchantSession * session, const char * const word, size_t len)
661 {
662 enchant_pwl_add(session->personal, word, len);
663 }
664
665 static void
enchant_session_remove_personal(EnchantSession * session,const char * const word,size_t len)666 enchant_session_remove_personal (EnchantSession * session, const char * const word, size_t len)
667 {
668 enchant_pwl_remove(session->personal, word, len);
669 }
670
671 static void
enchant_session_add_exclude(EnchantSession * session,const char * const word,size_t len)672 enchant_session_add_exclude (EnchantSession * session, const char * const word, size_t len)
673 {
674 enchant_pwl_add(session->exclude, word, len);
675 }
676
677 static void
enchant_session_remove_exclude(EnchantSession * session,const char * const word,size_t len)678 enchant_session_remove_exclude (EnchantSession * session, const char * const word, size_t len)
679 {
680 enchant_pwl_remove(session->exclude, word, len);
681 }
682
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
685 */
686 static gboolean
enchant_session_exclude(EnchantSession * session,const char * const word,size_t len)687 enchant_session_exclude (EnchantSession * session, const char * const word, size_t len)
688 {
689 gboolean result = FALSE;
690
691 char * utf = g_strndup (word, len);
692
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 ))
696 result = TRUE;
697 g_free (utf);
698
699 return result;
700 }
701
702 static gboolean
enchant_session_contains(EnchantSession * session,const char * const word,size_t len)703 enchant_session_contains (EnchantSession * session, const char * const word, size_t len)
704 {
705 gboolean result = FALSE;
706
707 char * utf = g_strndup (word, len);
708
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))
712 result = TRUE;
713
714 g_free (utf);
715
716 return result;
717 }
718
719 static void
enchant_session_clear_error(EnchantSession * session)720 enchant_session_clear_error (EnchantSession * session)
721 {
722 if (session->error)
723 {
724 g_free (session->error);
725 session->error = NULL;
726 }
727 }
728
729 /********************************************************************************/
730 /********************************************************************************/
731
732 static void
enchant_provider_free_string_list(EnchantProvider * provider,char ** string_list)733 enchant_provider_free_string_list (EnchantProvider * provider, char ** string_list)
734 {
735 if (provider && provider->free_string_list)
736 (*provider->free_string_list) (provider, string_list);
737 }
738
739 /**
740 * enchant_dict_set_error
741 * @dict: A non-null dictionary
742 * @err: A non-null error message
743 *
744 * Sets the current runtime error to @err. This API is private to the
745 * providers.
746 */
747 ENCHANT_MODULE_EXPORT(void)
enchant_dict_set_error(EnchantDict * dict,const char * const err)748 enchant_dict_set_error (EnchantDict * dict, const char * const err)
749 {
750 EnchantSession * session;
751
752 g_return_if_fail (dict);
753 g_return_if_fail (err);
754 g_return_if_fail (g_utf8_validate(err, -1, NULL));
755
756 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
757
758 enchant_session_clear_error (session);
759 session->error = g_strdup (err);
760 }
761
762 /**
763 * enchant_dict_get_error
764 * @dict: A non-null dictionary
765 *
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
769 *
770 * Returns: an error message
771 */
772 ENCHANT_MODULE_EXPORT(char *)
enchant_dict_get_error(EnchantDict * dict)773 enchant_dict_get_error (EnchantDict * dict)
774 {
775 EnchantSession * session;
776
777 g_return_val_if_fail (dict, NULL);
778
779 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
780 return session->error;
781 }
782
783 /**
784 * enchant_dict_check
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)
788 *
789 * Will return an "incorrect" value if any of those pre-conditions
790 * are not met.
791 *
792 * Returns: 0 if the word is correctly spelled, positive if not, negative if error
793 */
794 ENCHANT_MODULE_EXPORT (int)
enchant_dict_check(EnchantDict * dict,const char * const word,ssize_t len)795 enchant_dict_check (EnchantDict * dict, const char *const word, ssize_t len)
796 {
797 EnchantSession * session;
798
799 g_return_val_if_fail (dict, -1);
800 g_return_val_if_fail (word, -1);
801
802 if (len < 0)
803 len = strlen (word);
804
805 g_return_val_if_fail (len, -1);
806 g_return_val_if_fail (g_utf8_validate(word, len, NULL),-1);
807
808 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
809 enchant_session_clear_error (session);
810
811 /* first, see if it's to be excluded*/
812 if (enchant_session_exclude (session, word, len))
813 return 1;
814
815 /* then, see if it's in our pwl or session*/
816 if (enchant_session_contains(session, word, len))
817 return 0;
818
819 if (dict->check)
820 return (*dict->check) (dict, word, len);
821 else if (session->is_pwl)
822 return 1;
823
824 return -1;
825 }
826
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
829 *
830 * returns the number of items in @suggs after merge is complete
831 */
832 static int
enchant_dict_merge_suggestions(EnchantDict * dict,const char ** suggs,size_t n_suggs,const char * const * const new_suggs,size_t n_new_suggs)833 enchant_dict_merge_suggestions(EnchantDict * dict,
834 const char ** suggs,
835 size_t n_suggs,
836 const char * const* const new_suggs,
837 size_t n_new_suggs)
838 {
839 EnchantSession * session;
840 size_t i, j;
841
842 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
843
844 for(i = 0; i < n_new_suggs; i++)
845 {
846 int is_duplicate = 0;
847 char * normalized_new_sugg;
848
849 normalized_new_sugg = g_utf8_normalize (new_suggs[i], -1, G_NORMALIZE_NFD);
850
851 for(j = 0; j < n_suggs; j++)
852 {
853 char* normalized_sugg;
854 normalized_sugg = g_utf8_normalize (suggs[j], -1, G_NORMALIZE_NFD);
855
856 if(strcmp(normalized_sugg,normalized_new_sugg)==0)
857 {
858 is_duplicate = 1;
859 g_free(normalized_sugg);
860 break;
861 }
862 g_free(normalized_sugg);
863 }
864 g_free(normalized_new_sugg);
865
866 if(!is_duplicate)
867 {
868 suggs[n_suggs] = g_strdup (new_suggs[i]);
869 ++n_suggs;
870 }
871 }
872
873 return n_suggs;
874 }
875
876 static char **
enchant_dict_get_good_suggestions(EnchantDict * dict,const char * const * const suggs,size_t n_suggs,size_t * out_n_filtered_suggs)877 enchant_dict_get_good_suggestions(EnchantDict * dict,
878 const char * const* const suggs,
879 size_t n_suggs,
880 size_t* out_n_filtered_suggs)
881 {
882 EnchantSession * session;
883 size_t i, n_filtered_suggs;
884 char ** filtered_suggs;
885
886 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
887
888 filtered_suggs = g_new0 (char *, n_suggs + 1);
889 n_filtered_suggs = 0;
890
891 for(i = 0; i < n_suggs; i++)
892 {
893 size_t sugg_len = strlen(suggs[i]);
894
895 if(g_utf8_validate(suggs[i], sugg_len, NULL) &&
896 !enchant_session_exclude(session, suggs[i], sugg_len) )
897 {
898 filtered_suggs[n_filtered_suggs] = g_strdup (suggs[i]);
899 ++n_filtered_suggs;
900 }
901 }
902
903 if(out_n_filtered_suggs)
904 *out_n_filtered_suggs = n_filtered_suggs;
905
906 return filtered_suggs;
907 }
908
909 /**
910 * enchant_dict_suggest
911 * @dict: A non-null #EnchantDict
912 * @word: The non-null word you wish to find suggestions for, in UTF-8 encoding
913 * @len: The byte length of @word, or -1 for strlen (@word)
914 * @out_n_suggs: The location to store the # of suggestions returned, or %null
915 *
916 * Will return an %null value if any of those pre-conditions
917 * are not met.
918 *
919 * Returns: A %null terminated list of UTF-8 encoded suggestions, or %null
920 */
921 ENCHANT_MODULE_EXPORT (char **)
enchant_dict_suggest(EnchantDict * dict,const char * const word,ssize_t len,size_t * out_n_suggs)922 enchant_dict_suggest (EnchantDict * dict, const char *const word,
923 ssize_t len, size_t * out_n_suggs)
924 {
925 EnchantSession * session;
926 size_t n_suggs = 0, n_dict_suggs = 0, n_pwl_suggs = 0, n_suggsT = 0;
927 char **suggs, **dict_suggs = NULL, **pwl_suggs = NULL, **suggsT;
928
929 g_return_val_if_fail (dict, NULL);
930 g_return_val_if_fail (word, NULL);
931
932 if (len < 0)
933 len = strlen (word);
934
935 g_return_val_if_fail (len, NULL);
936 g_return_val_if_fail (g_utf8_validate(word, len, NULL), NULL);
937
938 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
939 enchant_session_clear_error (session);
940 /* Check for suggestions from provider dictionary */
941 if (dict->suggest)
942 {
943 dict_suggs = (*dict->suggest) (dict, word, len,
944 &n_dict_suggs);
945 if(dict_suggs)
946 {
947 suggsT = enchant_dict_get_good_suggestions(dict, dict_suggs, n_dict_suggs, &n_suggsT);
948 enchant_provider_free_string_list (session->provider, dict_suggs);
949 dict_suggs = suggsT;
950 n_dict_suggs = n_suggsT;
951 }
952 }
953
954 /* Check for suggestions from personal dictionary */
955 if(session->personal)
956 {
957 pwl_suggs = enchant_pwl_suggest(session->personal, word, len, dict_suggs, &n_pwl_suggs);
958 if(pwl_suggs)
959 {
960 suggsT = enchant_dict_get_good_suggestions(dict, pwl_suggs, n_pwl_suggs, &n_suggsT);
961 enchant_pwl_free_string_list (session->personal, pwl_suggs);
962 pwl_suggs = suggsT;
963 n_pwl_suggs = n_suggsT;
964 }
965 }
966 /* Clone suggestions if there are any */
967 n_suggs = n_pwl_suggs + n_dict_suggs;
968 if (n_suggs > 0)
969 {
970 suggs = g_new0 (char *, n_suggs + 1);
971
972 /* Copy over suggestions from dict, if no dupes */
973 n_suggs = enchant_dict_merge_suggestions(dict,
974 suggs, 0,
975 dict_suggs, n_dict_suggs);
976
977 /* Copy over suggestions from pwl, if no dupes */
978 n_suggs = enchant_dict_merge_suggestions(dict,
979 suggs, n_suggs,
980 pwl_suggs, n_pwl_suggs);
981 if(n_suggs == 0)
982 {
983 g_free(suggs);
984 suggs = NULL;
985 }
986 }
987 else
988 {
989 suggs = NULL;
990 }
991
992 g_strfreev(dict_suggs);
993 g_strfreev(pwl_suggs);
994
995 if (out_n_suggs)
996 *out_n_suggs = n_suggs;
997
998 return suggs;
999 }
1000
1001 /**
1002 * enchant_dict_add
1003 * @dict: A non-null #EnchantDict
1004 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1005 * @len: The byte length of @word, or -1 for strlen (@word)
1006 *
1007 * Remarks: if the word exists in the exclude dictionary, it will be removed from the
1008 * exclude dictionary
1009 */
1010 ENCHANT_MODULE_EXPORT (void)
enchant_dict_add(EnchantDict * dict,const char * const word,ssize_t len)1011 enchant_dict_add (EnchantDict * dict, const char *const word,
1012 ssize_t len)
1013 {
1014 EnchantSession * session;
1015
1016 g_return_if_fail (dict);
1017 g_return_if_fail (word);
1018
1019 if (len < 0)
1020 len = strlen (word);
1021
1022 g_return_if_fail (len);
1023 g_return_if_fail (g_utf8_validate(word, len, NULL));
1024
1025 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1026 enchant_session_clear_error (session);
1027 enchant_session_add_personal (session, word, len);
1028 enchant_session_remove_exclude (session, word, len);
1029
1030 if (dict->add_to_personal)
1031 (*dict->add_to_personal) (dict, word, len);
1032 }
1033
1034 /**
1035 * enchant_dict_add_to_pwl
1036 * @dict: A non-null #EnchantDict
1037 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1038 * @len: The byte length of @word, or -1 for strlen (@word)
1039 *
1040 * DEPRECATED. Please use enchant_dict_add() instead.
1041 */
1042 ENCHANT_MODULE_EXPORT (void)
enchant_dict_add_to_pwl(EnchantDict * dict,const char * const word,ssize_t len)1043 enchant_dict_add_to_pwl (EnchantDict * dict, const char *const word,
1044 ssize_t len)
1045 {
1046 enchant_dict_add(dict,word,len);
1047 }
1048
1049 /**
1050 * enchant_dict_add_to_personal
1051 * @dict: A non-null #EnchantDict
1052 * @word: The non-null word you wish to add to your personal dictionary, in UTF-8 encoding
1053 * @len: The byte length of @word, or -1 for strlen (@word)
1054 *
1055 * DEPRECATED. Please use enchant_dict_add() instead.
1056 */
1057 ENCHANT_MODULE_EXPORT (void)
enchant_dict_add_to_personal(EnchantDict * dict,const char * const word,ssize_t len)1058 enchant_dict_add_to_personal (EnchantDict * dict, const char *const word,
1059 ssize_t len)
1060 {
1061 enchant_dict_add(dict, word, len);
1062 }
1063
1064 /**
1065 * enchant_dict_add_to_session
1066 * @dict: A non-null #EnchantDict
1067 * @word: The non-null word you wish to add to this spell-checking session, in UTF-8 encoding
1068 * @len: The byte length of @word, or -1 for strlen (@word)
1069 *
1070 */
1071 ENCHANT_MODULE_EXPORT (void)
enchant_dict_add_to_session(EnchantDict * dict,const char * const word,ssize_t len)1072 enchant_dict_add_to_session (EnchantDict * dict, const char *const word,
1073 ssize_t len)
1074 {
1075 EnchantSession * session;
1076
1077 g_return_if_fail (dict);
1078 g_return_if_fail (word);
1079
1080 if (len < 0)
1081 len = strlen (word);
1082
1083 g_return_if_fail (len);
1084 g_return_if_fail (g_utf8_validate(word, len, NULL));
1085
1086 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1087 enchant_session_clear_error (session);
1088
1089 enchant_session_add (session, word, len);
1090 if (dict->add_to_session)
1091 (*dict->add_to_session) (dict, word, len);
1092 }
1093
1094 /**
1095 * enchant_dict_is_added
1096 * @dict: A non-null #EnchantDict
1097 * @word: The word you wish to see if it has been added (to your session or dict) in UTF8 encoding
1098 * @len: the byte length of @word, or -1 for strlen (@word)
1099 */
1100 ENCHANT_MODULE_EXPORT (int)
enchant_dict_is_added(EnchantDict * dict,const char * const word,ssize_t len)1101 enchant_dict_is_added (EnchantDict * dict, const char *const word,
1102 ssize_t len)
1103 {
1104 EnchantSession * session;
1105
1106 g_return_val_if_fail (dict, 0);
1107 g_return_val_if_fail (word, 0);
1108
1109 if (len < 0)
1110 len = strlen (word);
1111
1112 g_return_val_if_fail (len, 0);
1113 g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0);
1114
1115 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1116 enchant_session_clear_error (session);
1117
1118 return enchant_session_contains (session, word, len);
1119 }
1120
1121 /**
1122 * enchant_dict_is_in_session
1123 * @dict: A non-null #EnchantDict
1124 * @word: The word you wish to see if it's in your session in UTF8 encoding
1125 * @len: the byte length of @word, or -1 for strlen (@word)
1126 *
1127 * DEPRECATED. Please use enchant_dict_is_added() instead.
1128 */
1129 ENCHANT_MODULE_EXPORT (int)
enchant_dict_is_in_session(EnchantDict * dict,const char * const word,ssize_t len)1130 enchant_dict_is_in_session (EnchantDict * dict, const char *const word,
1131 ssize_t len)
1132 {
1133 return enchant_dict_is_added(dict, word, len);
1134 }
1135
1136 /**
1137 * enchant_dict_remove
1138 * @dict: A non-null #EnchantDict
1139 * @word: The non-null word you wish to add to your exclude dictionary and
1140 * remove from the personal dictionary, in UTF-8 encoding
1141 * @len: The byte length of @word, or -1 for strlen (@word)
1142 *
1143 */
1144 ENCHANT_MODULE_EXPORT (void)
enchant_dict_remove(EnchantDict * dict,const char * const word,ssize_t len)1145 enchant_dict_remove (EnchantDict * dict, const char *const word,
1146 ssize_t len)
1147 {
1148 EnchantSession * session;
1149
1150 g_return_if_fail (dict);
1151 g_return_if_fail (word);
1152
1153 if (len < 0)
1154 len = strlen (word);
1155
1156 g_return_if_fail (len);
1157 g_return_if_fail (g_utf8_validate(word, len, NULL));
1158
1159 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1160 enchant_session_clear_error (session);
1161
1162 enchant_session_remove_personal (session, word, len);
1163 enchant_session_add_exclude(session, word, len);
1164
1165 if (dict->add_to_exclude)
1166 (*dict->add_to_exclude) (dict, word, len);
1167 }
1168
1169 /**
1170 * enchant_dict_remove_from_session
1171 * @dict: A non-null #EnchantDict
1172 * @word: The non-null word you wish to exclude from this spell-checking session, in UTF-8 encoding
1173 * @len: The byte length of @word, or -1 for strlen (@word)
1174 *
1175 */
1176 ENCHANT_MODULE_EXPORT (void)
enchant_dict_remove_from_session(EnchantDict * dict,const char * const word,ssize_t len)1177 enchant_dict_remove_from_session (EnchantDict * dict, const char *const word,
1178 ssize_t len)
1179 {
1180 EnchantSession * session;
1181
1182 g_return_if_fail (dict);
1183 g_return_if_fail (word);
1184
1185 if (len < 0)
1186 len = strlen (word);
1187
1188 g_return_if_fail (len);
1189 g_return_if_fail (g_utf8_validate(word, len, NULL));
1190
1191 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1192 enchant_session_clear_error (session);
1193
1194 enchant_session_remove (session, word, len);
1195 }
1196
1197 /**
1198 * enchant_dict_is_removed
1199 * @dict: A non-null #EnchantDict
1200 * @word: The word you wish to see if it has been removed (from your session or dict) in UTF8 encoding
1201 * @len: the byte length of @word, or -1 for strlen (@word)
1202 */
1203 ENCHANT_MODULE_EXPORT (int)
enchant_dict_is_removed(EnchantDict * dict,const char * const word,ssize_t len)1204 enchant_dict_is_removed (EnchantDict * dict, const char *const word,
1205 ssize_t len)
1206 {
1207 EnchantSession * session;
1208
1209 g_return_val_if_fail (dict, 0);
1210 g_return_val_if_fail (word, 0);
1211
1212 if (len < 0)
1213 len = strlen (word);
1214
1215 g_return_val_if_fail (len, 0);
1216 g_return_val_if_fail (g_utf8_validate(word, len, NULL), 0);
1217
1218 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1219 enchant_session_clear_error (session);
1220
1221 return enchant_session_exclude (session, word, len);
1222 }
1223
1224 /**
1225 * enchant_dict_store_replacement
1226 * @dict: A non-null #EnchantDict
1227 * @mis: The non-null word you wish to add a correction for, in UTF-8 encoding
1228 * @mis_len: The byte length of @mis, or -1 for strlen (@mis)
1229 * @cor: The non-null correction word, in UTF-8 encoding
1230 * @cor_len: The byte length of @cor, or -1 for strlen (@cor)
1231 *
1232 * Notes that you replaced @mis with @cor, so it's possibly more likely
1233 * that future occurrences of @mis will be replaced with @cor. So it might
1234 * bump @cor up in the suggestion list.
1235 */
1236 ENCHANT_MODULE_EXPORT (void)
enchant_dict_store_replacement(EnchantDict * dict,const char * const mis,ssize_t mis_len,const char * const cor,ssize_t cor_len)1237 enchant_dict_store_replacement (EnchantDict * dict,
1238 const char *const mis, ssize_t mis_len,
1239 const char *const cor, ssize_t cor_len)
1240 {
1241 EnchantSession * session;
1242
1243 g_return_if_fail (dict);
1244 g_return_if_fail (mis);
1245 g_return_if_fail (cor);
1246
1247 if (mis_len < 0)
1248 mis_len = strlen (mis);
1249
1250 if (cor_len < 0)
1251 cor_len = strlen (cor);
1252
1253 g_return_if_fail (mis_len);
1254 g_return_if_fail (cor_len);
1255
1256 g_return_if_fail (g_utf8_validate(mis, mis_len, NULL));
1257 g_return_if_fail (g_utf8_validate(cor, cor_len, NULL));
1258
1259 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1260 enchant_session_clear_error (session);
1261
1262 /* if it's not implemented, it's not worth emulating */
1263 if (dict->store_replacement)
1264 (*dict->store_replacement) (dict, mis, mis_len, cor, cor_len);
1265 }
1266
1267 /**
1268 * enchant_dict_free_string_list
1269 * @dict: A non-null #EnchantDict
1270 * @string_list: A non-null string list returned from enchant_dict_suggest
1271 *
1272 * Releases the string list
1273 */
1274 ENCHANT_MODULE_EXPORT (void)
enchant_dict_free_string_list(EnchantDict * dict,char ** string_list)1275 enchant_dict_free_string_list (EnchantDict * dict, char **string_list)
1276 {
1277 EnchantSession * session;
1278
1279 g_return_if_fail (dict);
1280 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1281 enchant_session_clear_error (session);
1282 g_strfreev(string_list);
1283 }
1284
1285 /**
1286 * enchant_dict_free_suggestions
1287 * @dict: A non-null #EnchantDict
1288 * @suggestions: The non-null suggestion list returned by
1289 * 'enchant_dict_suggest'
1290 *
1291 * Releases the suggestions
1292 * This function is DEPRECATED. Please use enchant_dict_free_string_list() instead.
1293 */
1294 ENCHANT_MODULE_EXPORT (void)
enchant_dict_free_suggestions(EnchantDict * dict,char ** suggestions)1295 enchant_dict_free_suggestions (EnchantDict * dict, char **suggestions)
1296 {
1297 enchant_dict_free_string_list (dict, suggestions);
1298 }
1299
1300 /**
1301 * enchant_dict_describe
1302 * @broker: A non-null #EnchantDict
1303 * @dict: A non-null #EnchantDictDescribeFn
1304 * @user_data: Optional user-data
1305 *
1306 * Describes an individual dictionary
1307 */
1308 ENCHANT_MODULE_EXPORT (void)
enchant_dict_describe(EnchantDict * dict,EnchantDictDescribeFn fn,void * user_data)1309 enchant_dict_describe (EnchantDict * dict,
1310 EnchantDictDescribeFn fn,
1311 void * user_data)
1312 {
1313 EnchantSession * session;
1314 EnchantProvider * provider;
1315 GModule *module;
1316
1317 const char * tag, * name, * desc, * file;
1318
1319 g_return_if_fail (dict);
1320 g_return_if_fail (fn);
1321
1322 session = ((EnchantDictPrivateData*)dict->enchant_private_data)->session;
1323 enchant_session_clear_error (session);
1324 provider = session->provider;
1325
1326 if (provider)
1327 {
1328 module = (GModule *) provider->enchant_private_data;
1329 file = g_module_name (module);
1330 name = (*provider->identify) (provider);
1331 desc = (*provider->describe) (provider);
1332 }
1333 else
1334 {
1335 file = session->personal_filename;
1336 name = "Personal Wordlist";
1337 desc = "Personal Wordlist";
1338 }
1339
1340 tag = session->language_tag;
1341 (*fn) (tag, name, desc, file, user_data);
1342 }
1343
1344 /***********************************************************************************/
1345 /***********************************************************************************/
1346
1347 static void
enchant_broker_clear_error(EnchantBroker * broker)1348 enchant_broker_clear_error (EnchantBroker * broker)
1349 {
1350 if (broker->error)
1351 {
1352 g_free (broker->error);
1353 broker->error = NULL;
1354 }
1355 }
1356
1357 static void
enchant_broker_set_error(EnchantBroker * broker,const char * const err)1358 enchant_broker_set_error (EnchantBroker * broker, const char * const err)
1359 {
1360 enchant_broker_clear_error (broker);
1361 broker->error = g_strdup (err);
1362 }
1363
1364 static int
enchant_provider_is_valid(EnchantProvider * provider)1365 enchant_provider_is_valid(EnchantProvider * provider)
1366 {
1367 if(provider == NULL)
1368 {
1369 g_warning ("EnchantProvider cannot be NULL\n");
1370 return 0;
1371 }
1372
1373 if(provider->identify == NULL)
1374 {
1375 g_warning ("EnchantProvider's identify method cannot be NULL\n");
1376 return 0;
1377 }
1378 else if(!g_utf8_validate((*provider->identify)(provider), -1, NULL))
1379 {
1380 g_warning ("EnchantProvider's identify method does not return valid utf8.\n");
1381 return 0;
1382 }
1383
1384 if(provider->describe == NULL)
1385 {
1386 g_warning ("EnchantProvider's describe method cannot be NULL\n");
1387 return 0;
1388 }
1389 else if(!g_utf8_validate((*provider->describe)(provider), -1, NULL))
1390 {
1391 g_warning ("EnchantProvider's describe method does not return valid utf8.\n");
1392 return 0;
1393 }
1394
1395 return 1;
1396 }
1397
1398 static void
enchant_load_providers_in_dir(EnchantBroker * broker,const char * dir_name)1399 enchant_load_providers_in_dir (EnchantBroker * broker, const char *dir_name)
1400 {
1401 GModule *module = NULL;
1402 GDir *dir;
1403 G_CONST_RETURN char *dir_entry;
1404 size_t entry_len, g_module_suffix_len;
1405
1406 char * filename;
1407
1408 EnchantProvider *provider;
1409 EnchantProviderInitFunc init_func;
1410 EnchantPreConfigureFunc conf_func;
1411
1412 dir = g_dir_open (dir_name, 0, NULL);
1413 if (!dir)
1414 return;
1415
1416 g_module_suffix_len = strlen (G_MODULE_SUFFIX);
1417
1418 while ((dir_entry = g_dir_read_name (dir)) != NULL)
1419 {
1420 provider = 0;
1421
1422 entry_len = strlen (dir_entry);
1423 if ((entry_len > g_module_suffix_len) &&
1424 !strcmp(dir_entry+(entry_len-g_module_suffix_len), G_MODULE_SUFFIX))
1425 {
1426 #ifdef _WIN32
1427 /* Suppress error popups for failing to load plugins */
1428 UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
1429 #endif
1430 filename = g_build_filename (dir_name, dir_entry, NULL);
1431
1432 module = g_module_open (filename, (GModuleFlags) 0);
1433 if (module)
1434 {
1435 if (g_module_symbol
1436 (module, "init_enchant_provider", (gpointer *) (&init_func))
1437 && init_func)
1438 {
1439 provider = init_func ();
1440 if (!enchant_provider_is_valid(provider))
1441 {
1442 g_warning ("Error loading plugin: %s's init_enchant_provider returned invalid provider.\n", dir_entry);
1443 if(provider)
1444 {
1445 if(provider->dispose)
1446 provider->dispose(provider);
1447
1448 provider = NULL;
1449 }
1450 g_module_close (module);
1451 }
1452 }
1453 else
1454 {
1455 g_module_close (module);
1456 }
1457 }
1458 else
1459 {
1460 g_warning ("Error loading plugin: %s\n", g_module_error());
1461 }
1462
1463 g_free (filename);
1464 #ifdef _WIN32
1465 /* Restore the original error mode */
1466 SetErrorMode(old_error_mode);
1467 #endif
1468 }
1469 if (provider)
1470 {
1471 /* optional entry point to allow modules to look for associated files
1472 */
1473 if (g_module_symbol
1474 (module, "configure_enchant_provider", (gpointer *) (&conf_func))
1475 && conf_func)
1476 {
1477 conf_func (provider, dir_name);
1478 if (!enchant_provider_is_valid(provider))
1479 {
1480 g_warning ("Error loading plugin: %s's configure_enchant_provider modified provider and it is now invalid.\n", dir_entry);
1481 if(provider->dispose)
1482 provider->dispose(provider);
1483
1484 provider = NULL;
1485 g_module_close (module);
1486 }
1487 }
1488 }
1489 if (provider)
1490 {
1491 provider->enchant_private_data = (void *) module;
1492 provider->owner = broker;
1493 broker->provider_list = g_slist_append (broker->provider_list, (gpointer)provider);
1494 }
1495 }
1496
1497 g_dir_close (dir);
1498 }
1499
1500 static void
enchant_load_providers(EnchantBroker * broker)1501 enchant_load_providers (EnchantBroker * broker)
1502 {
1503 GSList *module_dirs, *iter;
1504
1505 module_dirs = enchant_get_module_dirs();
1506
1507 for (iter = module_dirs; iter; iter = iter->next)
1508 {
1509 enchant_load_providers_in_dir (broker, iter->data);
1510 }
1511
1512 g_slist_foreach (module_dirs, (GFunc)g_free, NULL);
1513 g_slist_free (module_dirs);
1514 }
1515
1516 static void
enchant_load_ordering_from_file(EnchantBroker * broker,const char * file)1517 enchant_load_ordering_from_file (EnchantBroker * broker, const char * file)
1518 {
1519 char line [1024];
1520 char * tag, * ordering;
1521
1522 size_t i, len;
1523
1524 FILE * f;
1525
1526 f = enchant_fopen (file, "r");
1527 if (!f)
1528 return;
1529
1530 while (NULL != fgets (line, sizeof(line), f)) {
1531 for (i = 0, len = strlen(line); i < len && line[i] != ':'; i++)
1532 ;
1533
1534 if (i < len)
1535 {
1536 tag = g_strndup (line, i);
1537 ordering = g_strndup (line+(i+1), len - i);
1538
1539 enchant_broker_set_ordering (broker, tag, ordering);
1540
1541 g_free (tag);
1542 g_free (ordering);
1543 }
1544 }
1545
1546 fclose (f);
1547 }
1548
1549 static void
enchant_load_provider_ordering(EnchantBroker * broker)1550 enchant_load_provider_ordering (EnchantBroker * broker)
1551 {
1552 GSList *conf_dirs, *iter;
1553
1554 broker->provider_ordering = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1555
1556 /* we want the user's dirs to show up last, so they override system dirs */
1557 conf_dirs = g_slist_reverse (enchant_get_conf_dirs ());
1558 for (iter = conf_dirs; iter; iter = iter->next)
1559 {
1560 char *ordering_file;
1561 ordering_file = g_build_filename (iter->data, "enchant1.ordering", NULL);
1562 enchant_load_ordering_from_file (broker, ordering_file);
1563 g_free (ordering_file);
1564 }
1565
1566 g_slist_foreach (conf_dirs, (GFunc)g_free, NULL);
1567 g_slist_free (conf_dirs);
1568 }
1569
1570 static GSList *
enchant_get_ordered_providers(EnchantBroker * broker,const char * const tag)1571 enchant_get_ordered_providers (EnchantBroker * broker,
1572 const char * const tag)
1573 {
1574 EnchantProvider *provider;
1575 GSList * list = NULL, * iter = NULL;
1576
1577 char * ordering = NULL, ** tokens, *token;
1578 size_t i;
1579
1580 ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)tag);
1581 if (!ordering)
1582 ordering = (char *)g_hash_table_lookup (broker->provider_ordering, (gpointer)"*");
1583
1584 if (!ordering)
1585 {
1586 /* return an unordered copy of the list */
1587 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1588 list = g_slist_append (list, iter->data);
1589 return list;
1590 }
1591
1592 tokens = g_strsplit (ordering, ",", 0);
1593 if (tokens)
1594 {
1595 for (i = 0; tokens[i]; i++)
1596 {
1597 token = g_strstrip(tokens[i]);
1598
1599 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1600 {
1601 provider = (EnchantProvider*)iter->data;
1602
1603 if (provider && !strcmp (token, (*provider->identify)(provider)))
1604 list = g_slist_append (list, (gpointer)provider);
1605 }
1606 }
1607
1608 g_strfreev (tokens);
1609 }
1610
1611 /* providers not in the list need to be appended at the end */
1612 for (iter = broker->provider_list; iter != NULL; iter = g_slist_next (iter))
1613 {
1614 if (!g_slist_find (list, iter->data))
1615 list = g_slist_append (list, iter->data);
1616 }
1617
1618 return list;
1619 }
1620
1621 static void
enchant_dict_destroyed(gpointer data)1622 enchant_dict_destroyed (gpointer data)
1623 {
1624 EnchantDict *dict;
1625 EnchantProvider *owner;
1626 EnchantSession *session;
1627 EnchantDictPrivateData *enchant_dict_private_data;
1628
1629 g_return_if_fail (data);
1630
1631 dict = (EnchantDict *) data;
1632 enchant_dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data;
1633 session = enchant_dict_private_data->session;
1634 owner = session->provider;
1635
1636 if (owner && owner->dispose_dict)
1637 (*owner->dispose_dict) (owner, dict);
1638 else if(session->is_pwl)
1639 g_free (dict);
1640
1641 g_free(enchant_dict_private_data);
1642
1643 enchant_session_destroy (session);
1644 }
1645
1646 static void
enchant_provider_free(gpointer data,gpointer user_data)1647 enchant_provider_free (gpointer data, gpointer user_data)
1648 {
1649 EnchantProvider *provider;
1650 GModule *module;
1651
1652 g_return_if_fail (data);
1653
1654 provider = (EnchantProvider *) data;
1655 module = (GModule *) provider->enchant_private_data;
1656
1657 if (provider->dispose)
1658 (*provider->dispose) (provider);
1659
1660 /* close module only after invoking dispose */
1661 g_module_close (module);
1662 }
1663
1664 /**
1665 * enchant_broker_init
1666 *
1667 * Returns: A new broker object capable of requesting
1668 * dictionaries from
1669 */
1670 ENCHANT_MODULE_EXPORT (EnchantBroker *)
enchant_broker_init(void)1671 enchant_broker_init (void)
1672 {
1673 EnchantBroker *broker = NULL;
1674
1675 g_return_val_if_fail (g_module_supported (), NULL);
1676
1677 #ifdef ENABLE_BINRELOC
1678 {
1679 static gboolean binreloc_initialized = FALSE;
1680
1681 if (!binreloc_initialized)
1682 {
1683 (void)gbr_init_lib (NULL);
1684 binreloc_initialized = TRUE;
1685 }
1686 }
1687 #endif
1688
1689 broker = g_new0 (EnchantBroker, 1);
1690
1691 broker->dict_map = g_hash_table_new_full (g_str_hash, g_str_equal,
1692 g_free, enchant_dict_destroyed);
1693 broker->params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1694 enchant_load_providers (broker);
1695 enchant_load_provider_ordering (broker);
1696
1697 return broker;
1698 }
1699
1700 /**
1701 * enchant_broker_free
1702 * @broker: A non-null #EnchantBroker
1703 *
1704 * Destroys the broker object. Must only be called once per broker init
1705 */
1706 ENCHANT_MODULE_EXPORT (void)
enchant_broker_free(EnchantBroker * broker)1707 enchant_broker_free (EnchantBroker * broker)
1708 {
1709 guint n_remaining;
1710
1711 g_return_if_fail (broker);
1712
1713 n_remaining = g_hash_table_size (broker->dict_map);
1714 if (n_remaining)
1715 {
1716 g_warning ("%u dictionaries weren't free'd.\n", n_remaining);
1717 }
1718
1719 /* will destroy any remaining dictionaries for us */
1720 g_hash_table_destroy (broker->dict_map);
1721 g_hash_table_destroy (broker->provider_ordering);
1722 g_hash_table_destroy (broker->params);
1723
1724 g_slist_foreach (broker->provider_list, enchant_provider_free, NULL);
1725 g_slist_free (broker->provider_list);
1726
1727 enchant_broker_clear_error (broker);
1728
1729 g_free (broker);
1730 }
1731
1732 /**
1733 * enchant_broker_request_pwl_dict
1734 *
1735 * PWL is a personal wordlist file, 1 entry per line
1736 *
1737 * @pwl: A non-null pathname in the GLib file name encoding (UTF-8 on Windows)
1738 * to the personal wordlist file
1739 *
1740 * Returns: An EnchantDict. This dictionary is reference counted.
1741 */
1742 ENCHANT_MODULE_EXPORT (EnchantDict *)
enchant_broker_request_pwl_dict(EnchantBroker * broker,const char * const pwl)1743 enchant_broker_request_pwl_dict (EnchantBroker * broker, const char *const pwl)
1744 {
1745 EnchantSession *session;
1746 EnchantDictPrivateData *enchant_dict_private_data;
1747 EnchantDict *dict = NULL;
1748
1749 g_return_val_if_fail (broker, NULL);
1750 g_return_val_if_fail (pwl && strlen(pwl), NULL);
1751
1752 enchant_broker_clear_error (broker);
1753
1754 dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) pwl);
1755 if (dict) {
1756 ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++;
1757 return dict;
1758 }
1759
1760 /* since the broker pwl file is a read/write file (there is no readonly dictionary associated)
1761 * there is no need for complementary exclude file to add a word to. The word just needs to be
1762 * removed from the broker pwl file
1763 */
1764 session = enchant_session_new_with_pwl (NULL, pwl, NULL, "Personal Wordlist", TRUE);
1765 if (!session)
1766 {
1767 broker->error = g_strdup_printf ("Couldn't open personal wordlist '%s'", pwl);
1768 return NULL;
1769 }
1770
1771 session->is_pwl = 1;
1772
1773 dict = g_new0 (EnchantDict, 1);
1774 enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1);
1775 enchant_dict_private_data->reference_count = 1;
1776 enchant_dict_private_data->session = session;
1777 dict->enchant_private_data = (void *)enchant_dict_private_data;
1778
1779
1780 g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (pwl), dict);
1781
1782 return dict;
1783 }
1784
1785 static EnchantDict *
_enchant_broker_request_dict(EnchantBroker * broker,const char * const tag)1786 _enchant_broker_request_dict (EnchantBroker * broker, const char *const tag)
1787 {
1788 EnchantDict * dict;
1789 GSList * list;
1790 GSList * listIter;
1791
1792 dict = (EnchantDict*)g_hash_table_lookup (broker->dict_map, (gpointer) tag);
1793 if (dict) {
1794 ((EnchantDictPrivateData*)dict->enchant_private_data)->reference_count++;
1795 return dict;
1796 }
1797
1798 list = enchant_get_ordered_providers (broker, tag);
1799 for (listIter = list; listIter != NULL; listIter = g_slist_next (listIter))
1800 {
1801 EnchantProvider * provider;
1802
1803 provider = (EnchantProvider *) listIter->data;
1804
1805 if (provider->request_dict)
1806 {
1807 dict = (*provider->request_dict) (provider, tag);
1808
1809 if (dict)
1810 {
1811 EnchantSession *session;
1812 EnchantDictPrivateData *enchant_dict_private_data;
1813
1814 session = enchant_session_new (provider, tag);
1815 enchant_dict_private_data = g_new0 (EnchantDictPrivateData, 1);
1816 enchant_dict_private_data->reference_count = 1;
1817 enchant_dict_private_data->session = session;
1818 dict->enchant_private_data = (void *)enchant_dict_private_data;
1819 g_hash_table_insert (broker->dict_map, (gpointer)g_strdup (tag), dict);
1820 break;
1821 }
1822 }
1823 }
1824
1825 g_slist_free (list);
1826
1827 return dict;
1828 }
1829
1830 /**
1831 * enchant_broker_request_dict
1832 * @broker: A non-null #EnchantBroker
1833 * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...)
1834 *
1835 * Returns: An #EnchantDict, or %null if no suitable dictionary could be found. This dictionary is reference counted.
1836 */
1837 ENCHANT_MODULE_EXPORT (EnchantDict *)
enchant_broker_request_dict(EnchantBroker * broker,const char * const tag)1838 enchant_broker_request_dict (EnchantBroker * broker, const char *const tag)
1839 {
1840 EnchantDict *dict = NULL;
1841 char * normalized_tag;
1842
1843 g_return_val_if_fail (broker, NULL);
1844 g_return_val_if_fail (tag && strlen(tag), NULL);
1845
1846 enchant_broker_clear_error (broker);
1847
1848 normalized_tag = enchant_normalize_dictionary_tag (tag);
1849 if(!enchant_is_valid_dictionary_tag(normalized_tag))
1850 {
1851 enchant_broker_set_error (broker, "invalid tag character found");
1852 }
1853 else if ((dict = _enchant_broker_request_dict (broker, normalized_tag)) == NULL)
1854 {
1855 char * iso_639_only_tag;
1856
1857 iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag);
1858
1859 dict = _enchant_broker_request_dict (broker, iso_639_only_tag);
1860
1861 g_free (iso_639_only_tag);
1862 }
1863
1864 g_free (normalized_tag);
1865
1866 return dict;
1867 }
1868
1869 /**
1870 * enchant_broker_describe
1871 * @broker: A non-null #EnchantBroker
1872 * @fn: A non-null #EnchantBrokerDescribeFn
1873 * @user_data: Optional user-data
1874 *
1875 * Enumerates the Enchant providers and tells
1876 * you some rudimentary information about them.
1877 */
1878 ENCHANT_MODULE_EXPORT (void)
enchant_broker_describe(EnchantBroker * broker,EnchantBrokerDescribeFn fn,void * user_data)1879 enchant_broker_describe (EnchantBroker * broker,
1880 EnchantBrokerDescribeFn fn,
1881 void * user_data)
1882 {
1883 GSList *list;
1884 EnchantProvider *provider;
1885 GModule *module;
1886
1887 const char * name, * desc, * file;
1888
1889 g_return_if_fail (broker);
1890 g_return_if_fail (fn);
1891
1892 enchant_broker_clear_error (broker);
1893
1894 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
1895 {
1896 provider = (EnchantProvider *) list->data;
1897 module = (GModule *) provider->enchant_private_data;
1898
1899 name = (*provider->identify) (provider);
1900 desc = (*provider->describe) (provider);
1901 file = g_module_name (module);
1902
1903 (*fn) (name, desc, file, user_data);
1904 }
1905 }
1906
1907 /**
1908 * enchant_broker_list_dicts
1909 * @broker: A non-null #EnchantBroker
1910 * @fn: A non-null #EnchantDictDescribeFn
1911 * @user_data: Optional user-data
1912 *
1913 * Enumerates the dictionaries available from
1914 * all Enchant providers.
1915 */
1916 ENCHANT_MODULE_EXPORT (void)
enchant_broker_list_dicts(EnchantBroker * broker,EnchantDictDescribeFn fn,void * user_data)1917 enchant_broker_list_dicts (EnchantBroker * broker,
1918 EnchantDictDescribeFn fn,
1919 void * user_data)
1920 {
1921 GSList *list;
1922 GHashTable *tags;
1923
1924 g_return_if_fail (broker);
1925 g_return_if_fail (fn);
1926
1927 tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1928
1929 enchant_broker_clear_error (broker);
1930
1931 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
1932 {
1933 EnchantProvider *provider;
1934 GModule *module;
1935
1936 provider = (EnchantProvider *) list->data;
1937 module = (GModule *) provider->enchant_private_data;
1938
1939 if (provider->list_dicts)
1940 {
1941 const char * tag, * name, * desc, * file;
1942 size_t n_dicts, i;
1943 char ** dicts;
1944
1945 dicts = (*provider->list_dicts) (provider, &n_dicts);
1946 name = (*provider->identify) (provider);
1947 desc = (*provider->describe) (provider);
1948 file = g_module_name (module);
1949
1950 for (i = 0; i < n_dicts; i++)
1951 {
1952 tag = dicts[i];
1953 if(enchant_is_valid_dictionary_tag(tag) &&
1954 !g_hash_table_lookup (tags, tag))
1955 {
1956 g_hash_table_insert (tags, g_strdup (tag), GINT_TO_POINTER(TRUE));
1957 (*fn) (tag, name, desc, file, user_data);
1958 }
1959 }
1960
1961 enchant_provider_free_string_list (provider, dicts);
1962 }
1963 }
1964
1965 g_hash_table_destroy (tags);
1966 }
1967
1968 /**
1969 * enchant_broker_free_dict
1970 * @broker: A non-null #EnchantBroker
1971 * @dict: A non-null #EnchantDict
1972 *
1973 * Releases the dictionary when you are done using it. Must only be called once per dictionary request
1974 */
1975 ENCHANT_MODULE_EXPORT (void)
enchant_broker_free_dict(EnchantBroker * broker,EnchantDict * dict)1976 enchant_broker_free_dict (EnchantBroker * broker, EnchantDict * dict)
1977 {
1978 EnchantSession * session;
1979 EnchantDictPrivateData * dict_private_data;
1980
1981 g_return_if_fail (broker);
1982 g_return_if_fail (dict);
1983
1984 enchant_broker_clear_error (broker);
1985
1986 dict_private_data = (EnchantDictPrivateData*)dict->enchant_private_data;
1987 dict_private_data->reference_count--;
1988 if(dict_private_data->reference_count == 0)
1989 {
1990 session = dict_private_data->session;
1991
1992 if (session->provider)
1993 g_hash_table_remove (broker->dict_map, session->language_tag);
1994 else
1995 g_hash_table_remove (broker->dict_map, session->personal_filename);
1996 }
1997 }
1998
1999 static int
_enchant_provider_dictionary_exists(EnchantProvider * provider,const char * const tag)2000 _enchant_provider_dictionary_exists (EnchantProvider * provider,
2001 const char * const tag)
2002 {
2003 int exists = 0;
2004
2005 if (provider->dictionary_exists)
2006 {
2007 exists = (*provider->dictionary_exists) (provider, tag);
2008 }
2009 else if (provider->list_dicts)
2010 {
2011 size_t n_dicts, i;
2012 char ** dicts;
2013
2014 dicts = (*provider->list_dicts) (provider, &n_dicts);
2015
2016 for (i = 0; (i < n_dicts) && !exists; i++)
2017 {
2018 if (!strcmp(dicts[i], tag))
2019 exists = 1;
2020 }
2021
2022 enchant_provider_free_string_list (provider, dicts);
2023 }
2024 else if (provider->request_dict)
2025 {
2026 EnchantDict *dict;
2027
2028 dict = (*provider->request_dict) (provider, tag);
2029 if (dict)
2030 {
2031 if (provider->dispose_dict)
2032 (*provider->dispose_dict) (provider, dict);
2033 exists = 1;
2034 }
2035 }
2036
2037 return exists;
2038 }
2039
2040 static int
_enchant_broker_dict_exists(EnchantBroker * broker,const char * const tag)2041 _enchant_broker_dict_exists (EnchantBroker * broker,
2042 const char * const tag)
2043 {
2044 GSList * list;
2045
2046 /* don't query the providers if it is an empty string */
2047 if (tag == NULL || *tag == '\0') {
2048 return 0;
2049 }
2050
2051 /* don't query the providers if we can just do a quick map lookup */
2052 if (g_hash_table_lookup (broker->dict_map, (gpointer) tag) != NULL) {
2053 return 1;
2054 }
2055
2056 for (list = broker->provider_list; list != NULL; list = g_slist_next (list))
2057 {
2058 EnchantProvider * provider;
2059
2060 provider = (EnchantProvider *) list->data;
2061
2062 if (_enchant_provider_dictionary_exists (provider, tag))
2063 {
2064 return 1;
2065 }
2066 }
2067
2068 return 0;
2069 }
2070
2071 /**
2072 * enchant_broker_dict_exists
2073 * @broker: A non-null #EnchantBroker
2074 * @tag: The non-null language tag you wish to request a dictionary for ("en_US", "de_DE", ...)
2075 *
2076 * Return existance of the requested dictionary (1 == true, 0 == false)
2077 */
2078 ENCHANT_MODULE_EXPORT (int)
enchant_broker_dict_exists(EnchantBroker * broker,const char * const tag)2079 enchant_broker_dict_exists (EnchantBroker * broker,
2080 const char * const tag)
2081 {
2082 char * normalized_tag;
2083 int exists = 0;
2084
2085 g_return_val_if_fail (broker, 0);
2086 g_return_val_if_fail (tag && strlen(tag), 0);
2087
2088 enchant_broker_clear_error (broker);
2089
2090 normalized_tag = enchant_normalize_dictionary_tag (tag);
2091
2092 if(!enchant_is_valid_dictionary_tag(normalized_tag))
2093 {
2094 enchant_broker_set_error (broker, "invalid tag character found");
2095 }
2096 else if ((exists = _enchant_broker_dict_exists (broker, normalized_tag)) == 0)
2097 {
2098 char * iso_639_only_tag;
2099
2100 iso_639_only_tag = enchant_iso_639_from_tag (normalized_tag);
2101
2102 if (strcmp (normalized_tag, iso_639_only_tag) != 0)
2103 {
2104 exists = _enchant_broker_dict_exists (broker, iso_639_only_tag);
2105 }
2106
2107 g_free (iso_639_only_tag);
2108 }
2109
2110 g_free (normalized_tag);
2111 return exists;
2112 }
2113
2114 /**
2115 * enchant_broker_set_ordering
2116 * @broker: A non-null #EnchantBroker
2117 * @tag: A non-null language tag (en_US)
2118 * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell)
2119 *
2120 * Declares a preference of dictionaries to use for the language
2121 * described/referred to by @tag. The ordering is a comma delimited
2122 * list of provider names. As a special exception, the "*" tag can
2123 * be used as a language tag to declare a default ordering for any
2124 * language that does not explictly declare an ordering.
2125 */
2126 ENCHANT_MODULE_EXPORT (void)
enchant_broker_set_ordering(EnchantBroker * broker,const char * const tag,const char * const ordering)2127 enchant_broker_set_ordering (EnchantBroker * broker,
2128 const char * const tag,
2129 const char * const ordering)
2130 {
2131 char * tag_dupl;
2132 char * ordering_dupl;
2133
2134 g_return_if_fail (broker);
2135 g_return_if_fail (tag && strlen(tag));
2136 g_return_if_fail (ordering && strlen(ordering));
2137
2138 enchant_broker_clear_error (broker);
2139
2140 tag_dupl = enchant_normalize_dictionary_tag (tag);
2141
2142 ordering_dupl = g_strdup (ordering);
2143 ordering_dupl = g_strstrip (ordering_dupl);
2144
2145 if (tag_dupl && strlen(tag_dupl) &&
2146 ordering_dupl && strlen(ordering_dupl))
2147 {
2148 /* we will free ordering_dupl && tag_dupl when the hash is destroyed */
2149 g_hash_table_insert (broker->provider_ordering, (gpointer)tag_dupl,
2150 (gpointer)(ordering_dupl));
2151 }
2152 else
2153 {
2154 g_free (tag_dupl);
2155 g_free (ordering_dupl);
2156 }
2157 }
2158
2159 /**
2160 * enchant_provider_set_error
2161 * @provider: A non-null provider
2162 * @err: A non-null error message
2163 *
2164 * Sets the current runtime error to @err. This API is private to
2165 * the providers.
2166 */
2167 ENCHANT_MODULE_EXPORT(void)
enchant_provider_set_error(EnchantProvider * provider,const char * const err)2168 enchant_provider_set_error (EnchantProvider * provider, const char * const err)
2169 {
2170 EnchantBroker * broker;
2171
2172 g_return_if_fail (provider);
2173 g_return_if_fail (err);
2174 g_return_if_fail (g_utf8_validate(err, -1, NULL));
2175
2176 broker = provider->owner;
2177 g_return_if_fail (broker);
2178
2179 enchant_broker_set_error (broker, err);
2180 }
2181
2182 /**
2183 * enchant_broker_get_error
2184 * @broker: A non-null broker
2185 *
2186 * Returns a const char string or NULL describing the last exception in UTF8 encoding.
2187 * WARNING: error is transient and is likely cleared as soon as the
2188 * next broker operation happens
2189 */
2190 ENCHANT_MODULE_EXPORT(char *)
enchant_broker_get_error(EnchantBroker * broker)2191 enchant_broker_get_error (EnchantBroker * broker)
2192 {
2193 g_return_val_if_fail (broker, NULL);
2194
2195 return broker->error;
2196 }
2197
2198 /* private. returned string should be free'd with g_free */
2199 ENCHANT_MODULE_EXPORT(char *)
enchant_get_user_language(void)2200 enchant_get_user_language(void)
2201 {
2202 char * locale = NULL;
2203
2204 #if defined(G_OS_WIN32)
2205 if(!locale)
2206 locale = g_win32_getlocale ();
2207 #endif
2208
2209 if(!locale)
2210 locale = g_strdup (g_getenv ("LANG"));
2211
2212 #if defined(HAVE_LC_MESSAGES)
2213 if(!locale)
2214 locale = g_strdup (setlocale (LC_MESSAGES, NULL));
2215 #endif
2216
2217 if(!locale)
2218 locale = g_strdup (setlocale (LC_ALL, NULL));
2219
2220 if(!locale || strcmp(locale, "C") == 0) {
2221 g_free(locale);
2222 locale = g_strdup("en");
2223 }
2224
2225 return locale;
2226 }
2227
2228
2229 /**
2230 * enchant_get_prefix_dir
2231 *
2232 * Returns a string giving the location of the base directory
2233 * of the enchant installation. This corresponds roughly to
2234 * the --prefix option given to ./configure when enchant is
2235 * compiled, except it is determined at runtime based on the location
2236 * of the enchant library.
2237 *
2238 * Returns: the prefix dir if it can be determined, or %null otherwise. Must be free'd.
2239 *
2240 * This API is private to the providers.
2241 *
2242 */
2243 ENCHANT_MODULE_EXPORT (char *)
enchant_get_prefix_dir(void)2244 enchant_get_prefix_dir(void)
2245 {
2246 char * prefix = NULL;
2247
2248 #ifdef _WIN32
2249 if (!prefix) {
2250 /* Dynamically locate library and return containing directory */
2251 WCHAR dll_path[MAX_PATH];
2252
2253 if(GetModuleFileNameW(s_hModule,dll_path,MAX_PATH))
2254 {
2255 gchar* utf8_dll_path = g_utf16_to_utf8 (dll_path, -1, NULL, NULL, NULL);
2256 prefix = g_path_get_dirname(utf8_dll_path);
2257 g_free(utf8_dll_path);
2258 /* Strip off "bin" subfolder if present */
2259 if (strlen(prefix) >=6 &&
2260 G_IS_DIR_SEPARATOR(prefix[strlen(prefix)-4]) &&
2261 g_ascii_strcasecmp(prefix+strlen(prefix)-3, "bin") == 0)
2262 prefix[strlen(prefix)-4] = '\0';
2263 }
2264 }
2265 #endif
2266
2267 #if defined(ENABLE_BINRELOC)
2268 if (!prefix) {
2269 /* Use standard binreloc PREFIX macro */
2270 prefix = gbr_find_prefix(NULL);
2271 }
2272 #endif
2273
2274 #if defined(ENCHANT_PREFIX_DIR)
2275 if (!prefix) {
2276 prefix = g_strdup (ENCHANT_PREFIX_DIR);
2277 }
2278 #endif
2279
2280 return prefix;
2281 }
2282
2283 ENCHANT_MODULE_EXPORT(char *)
enchant_broker_get_param(EnchantBroker * broker,const char * const param_name)2284 enchant_broker_get_param (EnchantBroker * broker, const char * const param_name)
2285 {
2286 g_return_val_if_fail (broker, NULL);
2287 g_return_val_if_fail (param_name && *param_name, NULL);
2288
2289 return g_hash_table_lookup (broker->params, param_name);
2290 }
2291
2292 ENCHANT_MODULE_EXPORT(void)
enchant_broker_set_param(EnchantBroker * broker,const char * const param_name,const char * const param_value)2293 enchant_broker_set_param (EnchantBroker * broker, const char * const param_name, const char * const param_value)
2294 {
2295 g_return_if_fail (broker);
2296 g_return_if_fail (param_name && *param_name);
2297
2298 if (param_value == NULL || *param_value == '\0')
2299 g_hash_table_remove (broker->params, param_name);
2300 else
2301 g_hash_table_insert (broker->params, g_strdup (param_name), g_strdup (param_value));
2302 }
2303
2304 ENCHANT_MODULE_EXPORT (GSList *)
enchant_get_dirs_from_param(EnchantBroker * broker,const char * const param_name)2305 enchant_get_dirs_from_param (EnchantBroker * broker, const char * const param_name)
2306 {
2307 const char *param_value;
2308 char **tokens;
2309 GSList *dirs = NULL;
2310
2311 param_value = enchant_broker_get_param (broker, param_name);
2312 if (param_value == NULL)
2313 return NULL;
2314
2315 #ifdef _WIN32
2316 tokens = g_strsplit (param_value, ";", 0);
2317 #else
2318 tokens = g_strsplit (param_value, ":", 0);
2319 #endif
2320 if (tokens != NULL) {
2321 int i;
2322 for (i = 0; tokens[i]; i++)
2323 {
2324 char *token = g_strstrip(tokens[i]);
2325 dirs = g_slist_append (dirs, g_strdup (token));
2326 }
2327
2328 g_strfreev (tokens);
2329 }
2330
2331 return dirs;
2332 }
2333
2334 ENCHANT_MODULE_EXPORT(char *)
enchant_get_version(void)2335 enchant_get_version (void) {
2336 return ENCHANT_VERSION_STRING;
2337 }
2338