1 /***************************************************************************
2  *   Copyright (C) 2012~2012 by Yichao Yu                                  *
3  *   yyc1992@gmail.com                                                     *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program 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         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
19  ***************************************************************************/
20 
21 #include "fcitx/fcitx.h"
22 #include "config.h"
23 
24 #include "fcitx/ime.h"
25 #include "fcitx/instance.h"
26 #include "fcitx/context.h"
27 #include "fcitx/module.h"
28 #include "fcitx/frontend.h"
29 #include "fcitx-config/xdg.h"
30 #include "fcitx-utils/log.h"
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <time.h>
34 
35 #include "spell-internal.h"
36 #include "spell-enchant.h"
37 #include <dlfcn.h>
38 
39 static void *_enchant_handle = NULL;
40 static void *(*_enchant_broker_init)() = NULL;
41 static char **(*_enchant_dict_suggest)(void *dict, const char *str,
42                                        ssize_t len, size_t *out_n) = NULL;
43 static void (*_enchant_dict_free_string_list)(void *dict,
44                                               char **str_list) = NULL;
45 static void (*_enchant_broker_free_dict)(void *broker, void *dict) = NULL;
46 static void (*_enchant_broker_free)(void *broker) = NULL;
47 static void *(*_enchant_broker_request_dict)(void *broker,
48                                              const char *const tag) = NULL;
49 static void (*_enchant_broker_set_ordering)(void *broker,
50                                             const char *const tag,
51                                             const char *const ordering) = NULL;
52 static void (*_enchant_dict_add)(void *dict, const char *const word,
53                                              ssize_t len) = NULL;
54 
55 static boolean
SpellEnchantLoadLib()56 SpellEnchantLoadLib()
57 {
58     if (_enchant_handle)
59         return true;
60     _enchant_handle = dlopen(ENCHANT_LIBRARY_FILENAME, RTLD_NOW | RTLD_NODELETE | RTLD_GLOBAL);
61     if (!_enchant_handle)
62         goto fail;
63 #define ENCHANT_LOAD_SYMBOL(sym) do {            \
64         _##sym = dlsym(_enchant_handle, #sym);\
65             if (!_##sym)                         \
66                 goto fail;                       \
67     } while(0)
68     ENCHANT_LOAD_SYMBOL(enchant_broker_init);
69     ENCHANT_LOAD_SYMBOL(enchant_dict_suggest);
70     ENCHANT_LOAD_SYMBOL(enchant_dict_free_string_list);
71     ENCHANT_LOAD_SYMBOL(enchant_broker_free_dict);
72     ENCHANT_LOAD_SYMBOL(enchant_broker_free);
73     ENCHANT_LOAD_SYMBOL(enchant_broker_request_dict);
74     ENCHANT_LOAD_SYMBOL(enchant_broker_set_ordering);
75     ENCHANT_LOAD_SYMBOL(enchant_dict_add);
76     return true;
77 fail:
78     if (_enchant_handle) {
79         dlclose(_enchant_handle);
80         _enchant_handle = NULL;
81     }
82     return false;
83 }
84 
85 boolean
SpellEnchantInit(FcitxSpell * spell)86 SpellEnchantInit(FcitxSpell *spell)
87 {
88     if (spell->broker)
89         return true;
90     if (!SpellEnchantLoadLib())
91         return false;
92     spell->broker = _enchant_broker_init();
93     spell->cur_enchant_provider = EP_Default;
94     if (!spell->broker)
95         return false;
96     if (spell->dictLang)
97         SpellEnchantLoadDict(spell, spell->dictLang);
98     return true;
99 }
100 
101 SpellHint*
SpellEnchantHintWords(FcitxSpell * spell,unsigned int len_limit)102 SpellEnchantHintWords(FcitxSpell *spell, unsigned int len_limit)
103 {
104     SpellHint *res = NULL;
105     if (!SpellEnchantInit(spell))
106         return NULL;
107     if (!spell->enchant_dict || spell->enchant_saved_lang)
108         return NULL;
109     char **suggestions = NULL;
110     size_t number = 0;
111     if (!*spell->current_str)
112         return NULL;
113     suggestions = _enchant_dict_suggest(spell->enchant_dict, spell->current_str,
114                                         strlen(spell->current_str), &number);
115     if (!suggestions)
116         return NULL;
117     number = number > len_limit ? len_limit : number;
118     res = SpellHintList(number, suggestions, NULL);
119     _enchant_dict_free_string_list(spell->enchant_dict, suggestions);
120     return res;
121 }
122 
123 boolean
SpellEnchantCheck(FcitxSpell * spell)124 SpellEnchantCheck(FcitxSpell *spell)
125 {
126     if (!SpellEnchantInit(spell))
127         return false;
128     if (spell->enchant_dict && !spell->enchant_saved_lang)
129         return true;
130     return false;
131 }
132 
133 void
SpellEnchantDestroy(FcitxSpell * spell)134 SpellEnchantDestroy(FcitxSpell *spell)
135 {
136     if (spell->broker) {
137         if (spell->enchant_dict)
138             _enchant_broker_free_dict(spell->broker, spell->enchant_dict);
139         _enchant_broker_free(spell->broker);
140     }
141     if (spell->enchant_saved_lang)
142         free(spell->enchant_saved_lang);
143     /* if (spell->enchantLanguages) */
144     /*     fcitx_utils_free_string_list(spell->enchantLanguages); */
145 }
146 
147 boolean
SpellEnchantLoadDict(FcitxSpell * spell,const char * lang)148 SpellEnchantLoadDict(FcitxSpell *spell, const char *lang)
149 {
150     void *enchant_dict;
151     if (!SpellEnchantInit(spell))
152         return false;
153     if (!spell->broker)
154         return false;
155     if (spell->enchant_saved_lang &&
156         !strcmp(spell->enchant_saved_lang, lang)) {
157         free(spell->enchant_saved_lang);
158         spell->enchant_saved_lang = NULL;
159         return true;
160     }
161     enchant_dict = _enchant_broker_request_dict(spell->broker, lang);
162     if (enchant_dict) {
163         if (spell->enchant_saved_lang) {
164             free(spell->enchant_saved_lang);
165             spell->enchant_saved_lang = NULL;
166         }
167         if (spell->enchant_dict)
168             _enchant_broker_free_dict(spell->broker, spell->enchant_dict);
169         spell->enchant_dict = enchant_dict;
170         return true;
171     }
172     if (!spell->enchant_dict || !spell->dictLang)
173         return false;
174     if (spell->enchant_saved_lang)
175         return false;
176     spell->enchant_saved_lang = strdup(spell->dictLang);
177     return false;
178 }
179 
180 void
SpellEnchantApplyConfig(FcitxSpell * spell)181 SpellEnchantApplyConfig(FcitxSpell *spell)
182 {
183     if (!SpellEnchantInit(spell))
184         return;
185     if (!spell->broker) {
186         spell->broker = _enchant_broker_init();
187         spell->cur_enchant_provider = EP_Default;
188         if (!spell->broker)
189             return;
190     }
191     if (spell->cur_enchant_provider == spell->config.enchant_provider)
192         return;
193     if (spell->config.enchant_provider == EP_Default) {
194         if (spell->enchant_saved_lang) {
195             free(spell->enchant_saved_lang);
196             spell->enchant_saved_lang = NULL;
197         }
198         if (spell->enchant_dict) {
199             _enchant_broker_free_dict(spell->broker, spell->enchant_dict);
200             spell->enchant_dict = NULL;
201         }
202         _enchant_broker_free(spell->broker);
203         spell->broker = _enchant_broker_init();
204         spell->cur_enchant_provider = EP_Default;
205         if (!spell->broker)
206             return;
207     }
208     switch (spell->config.enchant_provider) {
209     case EP_Aspell:
210         _enchant_broker_set_ordering(spell->broker, "*",
211                                     "aspell,myspell,ispell");
212         break;
213     case EP_Myspell:
214         _enchant_broker_set_ordering(spell->broker, "*",
215                                     "myspell,aspell,ispell");
216         break;
217     default:
218         break;
219     }
220     spell->cur_enchant_provider = spell->config.enchant_provider;
221     if (!spell->enchant_dict && spell->dictLang && spell->dictLang[0]) {
222         spell->enchant_dict = _enchant_broker_request_dict(spell->broker,
223                                                            spell->dictLang);
224     }
225 }
226 
227 void
SpellEnchantAddPersonal(FcitxSpell * spell,const char * new_word)228 SpellEnchantAddPersonal(FcitxSpell *spell, const char *new_word)
229 {
230     if (!SpellEnchantInit(spell))
231         return;
232     if (spell->enchant_dict && !spell->enchant_saved_lang) {
233         _enchant_dict_add(spell->enchant_dict, new_word,
234                                       strlen(new_word));
235         return;
236     }
237 }
238