Tizen 2.1 base
[external/enchant.git] / src / aspell / aspell_provider.c
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
35 #include <glib.h>
36
37 #ifdef HAVE_PSPELL_H
38 #include <pspell/pspell.h>
39 #else
40 #include <aspell.h>
41
42 /* to allow this to build on places that lack pspell.h */
43
44 #define PspellManager AspellSpeller
45 #define PspellWordList AspellWordList
46 #define PspellStringEmulation AspellStringEnumeration
47 #define PspellCanHaveError   AspellCanHaveError 
48 #define PspellConfig AspellConfig
49
50 #define new_pspell_config new_aspell_config
51 #define delete_pspell_config delete_aspell_config
52 #define pspell_config_replace aspell_config_replace
53
54 #define new_pspell_manager new_aspell_speller
55 #define to_pspell_manager to_aspell_speller
56 #define delete_pspell_manager delete_aspell_speller
57 #define pspell_manager_error_number aspell_speller_error_number
58 #define pspell_manager_error_message aspell_speller_error_message
59 #define pspell_manager_save_all_word_lists aspell_speller_save_all_word_lists
60 #define pspell_manager_check aspell_speller_check
61 #define pspell_manager_add_to_personal aspell_speller_add_to_personal
62 #define pspell_manager_add_to_session aspell_speller_add_to_session
63 #define pspell_manager_suggest aspell_speller_suggest
64 #define pspell_manager_store_replacement aspell_speller_store_replacement
65
66 #define pspell_word_list_elements aspell_word_list_elements
67 #define pspell_word_list_size aspell_word_list_size
68
69 #define pspell_string_emulation_next    aspell_string_enumeration_next
70 #define delete_pspell_string_emulation  delete_aspell_string_enumeration
71
72 #define pspell_error_message    aspell_error_message
73 #define pspell_error_number     aspell_error_number
74
75 #endif
76
77 #include "enchant.h"
78 #include "enchant-provider.h"
79
80 ENCHANT_PLUGIN_DECLARE("Aspell")
81
82 #ifdef __cplusplus
83 extern "C" {
84 #endif
85
86 ENCHANT_MODULE_EXPORT(void)
87      configure_enchant_provider(EnchantProvider * me, const char *dir_name);
88
89 ENCHANT_MODULE_EXPORT (EnchantProvider *) 
90      init_enchant_provider (void);
91
92 #ifdef __cplusplus
93 }
94 #endif
95
96 static int
97 aspell_dict_check (EnchantDict * me, const char *const word, size_t len)
98 {
99         PspellManager *manager;
100         int val;
101         char *normalizedWord;
102
103         manager = (PspellManager *) me->user_data;
104
105         normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC);
106         val = pspell_manager_check (manager, normalizedWord, strlen(normalizedWord));
107         g_free(normalizedWord);
108
109         if (val == 0)
110                 return 1;
111         else if (val > 0)
112                 return 0;
113         else {
114                 enchant_dict_set_error (me, pspell_manager_error_message (manager));
115                 return -1;
116         }
117 }
118
119 static char **
120 aspell_dict_suggest (EnchantDict * me, const char *const word,
121                      size_t len, size_t * out_n_suggs)
122 {
123         PspellManager *manager;
124         
125         const PspellWordList *word_list;
126         PspellStringEmulation *suggestions;
127         char *normalizedWord;
128         
129         char **sugg_arr = NULL;
130         size_t n_suggestions, i;
131         const char *sugg;
132         
133         manager = (PspellManager *) me->user_data;
134         
135         normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC);
136         word_list = pspell_manager_suggest (manager, normalizedWord, strlen(normalizedWord));
137         g_free(normalizedWord);
138
139         if (word_list)
140                 {
141                         suggestions = pspell_word_list_elements (word_list);
142                         if (suggestions)
143                                 {
144                                         n_suggestions = pspell_word_list_size (word_list);
145                                         *out_n_suggs = n_suggestions;
146                                         
147                                         if (n_suggestions)
148                                                 {
149                                                         sugg_arr = g_new0 (char *, n_suggestions + 1);
150                                                         
151                                                         for (i = 0; i < n_suggestions; i++)
152                                                                 {
153                                                                         sugg = pspell_string_emulation_next (suggestions);
154                                                                         if (sugg)
155                                                                                 sugg_arr[i] = g_strdup (sugg);
156                                                                 }
157                                                 }
158                                         delete_pspell_string_emulation (suggestions);
159                                 }
160                 }
161         
162         return sugg_arr;
163 }
164
165 static void
166 aspell_dict_add_to_personal (EnchantDict * me,
167                              const char *const word, size_t len)
168 {
169         PspellManager *manager;
170         
171         manager = (PspellManager *) me->user_data;
172         pspell_manager_add_to_personal (manager, word, len);
173         pspell_manager_save_all_word_lists (manager);
174 }
175
176 static void
177 aspell_dict_add_to_session (EnchantDict * me,
178                             const char *const word, size_t len)
179 {
180         PspellManager *manager;
181         
182         manager = (PspellManager *) me->user_data;
183         pspell_manager_add_to_session (manager, word, len);
184 }
185
186 static void
187 aspell_dict_store_replacement (EnchantDict * me,
188                                const char *const mis, size_t mis_len,
189                                const char *const cor, size_t cor_len)
190 {
191         PspellManager *manager;
192         
193         manager = (PspellManager *) me->user_data;
194         pspell_manager_store_replacement (manager, mis, mis_len,
195                                           cor, cor_len);
196         pspell_manager_save_all_word_lists (manager);
197 }
198
199 static EnchantDict *
200 aspell_provider_request_dict (EnchantProvider * me, const char *const tag)
201 {
202         EnchantDict *dict;
203         PspellManager *manager;
204         PspellConfig *spell_config;
205         PspellCanHaveError *spell_error;
206         
207         spell_config = new_pspell_config ();
208         pspell_config_replace (spell_config, "language-tag", tag);
209         pspell_config_replace (spell_config, "encoding", "utf-8");
210         
211         spell_error = new_pspell_manager (spell_config);
212         delete_pspell_config (spell_config);
213         
214         if (pspell_error_number (spell_error) != 0)
215                 {
216                         return NULL;
217                 }
218         
219         manager = to_pspell_manager (spell_error);
220         
221         dict = g_new0 (EnchantDict, 1);
222         dict->user_data = (void *) manager;
223         dict->check = aspell_dict_check;
224         dict->suggest = aspell_dict_suggest;
225         dict->add_to_personal = aspell_dict_add_to_personal;
226         dict->add_to_session = aspell_dict_add_to_session;
227         dict->store_replacement = aspell_dict_store_replacement;
228         
229         return dict;
230 }
231
232 static void
233 aspell_provider_dispose_dict (EnchantProvider * me, EnchantDict * dict)
234 {
235         PspellManager *manager;
236         
237         manager = (PspellManager *) dict->user_data;
238         delete_pspell_manager (manager);
239         
240         g_free (dict);
241 }
242
243 #if ASPELL_0_50_0
244 static char ** 
245 aspell_provider_list_dicts (EnchantProvider * me, 
246                             size_t * out_n_dicts)
247 {
248         PspellConfig * spell_config;
249         AspellDictInfoList * dlist;
250         AspellDictInfoEnumeration * dels;
251         const AspellDictInfo * entry;
252         char ** out_list = NULL;
253         
254         spell_config = new_pspell_config ();
255
256         dlist = get_aspell_dict_info_list (spell_config);
257
258         *out_n_dicts = 0;
259         dels = aspell_dict_info_list_elements (dlist);
260
261         /* TODO: Use aspell_dict_info_list_size() once it is implemented and returns non-zero. */
262         while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
263                 (*out_n_dicts)++;
264
265         if (*out_n_dicts) {
266                 size_t i;               
267
268                 out_list = g_new0 (char *, *out_n_dicts + 1);
269                 dels = aspell_dict_info_list_elements (dlist);
270                 
271                 for (i = 0; i < *out_n_dicts; i++) {
272                         entry = aspell_dict_info_enumeration_next (dels);                       
273                         /* XXX: should this be entry->code or entry->name ? */
274                         out_list[i] = g_strdup (entry->code);
275                 }
276                 
277                 delete_aspell_dict_info_enumeration (dels);
278         }
279         
280         delete_pspell_config (spell_config);
281         
282         return out_list;
283 }
284 #endif
285
286 static void
287 aspell_provider_free_string_list (EnchantProvider * me, char **str_list)
288 {
289         g_strfreev (str_list);
290 }
291
292 static void
293 aspell_provider_dispose (EnchantProvider * me)
294 {
295         g_free (me);
296 }
297
298 static const char *
299 aspell_provider_identify (EnchantProvider * me)
300 {
301         return "aspell";
302 }
303
304 static const char *
305 aspell_provider_describe (EnchantProvider * me)
306 {
307         return "Aspell Provider";
308 }
309
310 #ifdef __cplusplus
311 extern "C" {
312 #endif
313
314 EnchantProvider *
315 init_enchant_provider (void)
316 {
317         EnchantProvider *provider;
318         
319         provider = g_new0 (EnchantProvider, 1);
320         provider->dispose = aspell_provider_dispose;
321         provider->request_dict = aspell_provider_request_dict;
322         provider->dispose_dict = aspell_provider_dispose_dict;
323         provider->identify = aspell_provider_identify;
324         provider->describe = aspell_provider_describe;
325
326 #if ASPELL_0_50_0
327         provider->list_dicts = aspell_provider_list_dicts;
328 #else
329 #  ifdef __GNUC__
330 #    warning "You're using an ancient version of Aspell. Some things won't work properly."
331 #  endif
332 #endif
333         provider->free_string_list = aspell_provider_free_string_list;
334
335         return provider;
336 }
337
338
339 #if defined(_WIN32)
340
341 static WCHAR* GetDirectoryOfThisLibrary(void)
342 {
343         WCHAR dll_path[MAX_PATH];
344     gchar* utf8_dll_path;
345     gchar* utf8_prefix;
346     gunichar2* utf16_prefix;
347
348     if(!GetModuleFileNameW(s_hModule,dll_path,MAX_PATH))
349         { /* unable to determine filename of this library */
350             return NULL;
351         }
352         utf8_dll_path = g_utf16_to_utf8 (dll_path, -1, NULL, NULL, NULL);
353         utf8_prefix = g_path_get_dirname(utf8_dll_path);
354         g_free(utf8_dll_path);
355
356     utf16_prefix = g_utf8_to_utf16 (utf8_prefix, -1, NULL, NULL, NULL);
357         g_free(utf8_prefix);
358
359     return utf16_prefix;
360 }
361
362 static HMODULE LoadLibraryFromPath(const WCHAR* path, const WCHAR* libraryName)
363 {
364     HMODULE h;
365         WCHAR* wszFullLibraryPath;
366     size_t fullLibraryPathLen;
367
368     fullLibraryPathLen = wcslen(path) +  1 /* '\\' */+ wcslen(libraryName);
369     wszFullLibraryPath = g_new0(WCHAR, fullLibraryPathLen + 1);
370
371     wcscpy(wszFullLibraryPath, path);
372     wcscat(wszFullLibraryPath, L"\\");
373     wcscat(wszFullLibraryPath, libraryName);
374
375     h = LoadLibraryW(wszFullLibraryPath);
376
377     g_free(wszFullLibraryPath);
378     return h;
379 }
380
381 static WCHAR* GetRegistryValue(HKEY baseKey, const WCHAR * uKeyName, const WCHAR * uKey)
382 {
383         HKEY hKey;
384         unsigned long lType;    
385         DWORD dwSize;
386         WCHAR* wszValue = NULL;
387
388         if(RegOpenKeyExW(baseKey, uKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
389                 {
390                         /* Determine size of string */
391                         if(RegQueryValueExW( hKey, uKey, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS)
392                                 {
393                                         wszValue = g_new0(WCHAR, dwSize + 1);
394                                         RegQueryValueExW(hKey, uKey, NULL, &lType, (LPBYTE) wszValue, &dwSize);
395                                 }
396                 }
397
398         return wszValue;
399 }
400 #endif
401
402 #if defined(_WIN32)
403 gboolean load_library(EnchantProvider * me, const char *dir_name, const WCHAR *aspell_module_name)
404 {
405     HMODULE aspell_module = NULL;
406     char* szModule;
407
408     /* first try load from registry path */
409         szModule = enchant_get_registry_value ("Aspell", "Module");
410     if(szModule)
411     {
412         WCHAR* wszModule;
413
414             wszModule = g_utf8_to_utf16 (szModule, -1, NULL, NULL, NULL);
415         aspell_module = LoadLibraryW(wszModule);
416         g_free(wszModule);
417     }
418
419     if (aspell_module == NULL)
420         {
421             /* next try load from aspell registry path */
422             WCHAR* wszDirectory = GetRegistryValue (HKEY_LOCAL_MACHINE, L"Software\\Aspell", L"Path");
423             if(wszDirectory)
424                 {
425                     aspell_module = LoadLibraryFromPath(wszDirectory, aspell_module_name);
426                     g_free(wszDirectory);
427                 }
428         }
429
430     if (aspell_module == NULL)
431         {
432             /* then try from same directory as provider */
433             WCHAR* wszDirectory = GetDirectoryOfThisLibrary();
434             if(wszDirectory)
435                 {
436                     aspell_module = LoadLibraryFromPath(wszDirectory, aspell_module_name);
437                     g_free(wszDirectory);
438                 }
439         }
440
441     if (aspell_module == NULL) 
442         {
443             /* then try default lookup */
444             aspell_module = LoadLibraryW(aspell_module_name);
445         }
446
447     if (aspell_module == NULL) 
448         {
449                 return FALSE;
450         }
451     return TRUE;
452 }
453 #endif
454
455 void configure_enchant_provider(EnchantProvider * me, const char *dir_name)
456 {
457 #if defined(_WIN32)
458         if(!load_library(me, dir_name, L"aspell-15.dll") &&
459        !load_library(me, dir_name, L"libaspell-15.dll"))
460     {
461             /* we can't seem to load aspell. Avoid late binding problems later */
462             g_warning("Unable to load library aspell-15.dll.");
463             me->request_dict = NULL;
464             me->dispose_dict = NULL;
465             me->list_dicts = NULL;
466     }
467 #endif
468 }
469
470
471 #ifdef __cplusplus
472 }
473 #endif