1 /* enchant
2  * Copyright (C) 2003,2004 Dom Lachowicz
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  * In addition, as a special exception, Dom Lachowicz
20  * gives permission to link the code of this program with
21  * non-LGPL Spelling Provider libraries (eg: a MSFT Office
22  * spell checker backend) and distribute linked combinations including
23  * the two.  You must obey the GNU Lesser General Public License in all
24  * respects for all of the code used other than said providers.  If you modify
25  * this file, you may extend this exception to your version of the
26  * file, but you are not obligated to do so.  If you do not wish to
27  * do so, delete this exception statement from your version.
28  */
29 
30 /*
31  * This is the GNU Aspell Enchant Backend.
32  * GNU Aspell is by Kevin Atkinson.  See http://aspell.net/
33  */
34 
35 #include "config.h"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <glib.h>
42 #include <aspell.h>
43 
44 #include "enchant-provider.h"
45 #include "unused-parameter.h"
46 
47 
48 EnchantProvider *init_enchant_provider (void);
49 
50 static int
aspell_dict_check(EnchantDict * me,const char * const word,size_t len)51 aspell_dict_check (EnchantDict * me, const char *const word, size_t len)
52 {
53 	AspellSpeller *manager = (AspellSpeller *) me->user_data;
54 
55 	char *normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC);
56 	int val = aspell_speller_check (manager, normalizedWord, strlen(normalizedWord));
57 	g_free(normalizedWord);
58 
59 	if (val == 0)
60 		return 1;
61 	else if (val > 0)
62 		return 0;
63 	else {
64 		enchant_dict_set_error (me, aspell_speller_error_message (manager));
65 		return -1;
66 	}
67 }
68 
69 static char **
aspell_dict_suggest(EnchantDict * me,const char * const word,size_t len,size_t * out_n_suggs)70 aspell_dict_suggest (EnchantDict * me, const char *const word,
71 		     size_t len, size_t * out_n_suggs)
72 {
73 	AspellSpeller *manager = (AspellSpeller *) me->user_data;
74 
75 	char *normalizedWord = g_utf8_normalize (word, len, G_NORMALIZE_NFC);
76 	const AspellWordList *word_list = aspell_speller_suggest (manager, normalizedWord, strlen(normalizedWord));
77 	g_free(normalizedWord);
78 
79 	char **sugg_arr = NULL;
80 	if (word_list)
81 		{
82 			AspellStringEnumeration *suggestions = aspell_word_list_elements (word_list);
83 			if (suggestions)
84 				{
85 					size_t n_suggestions = aspell_word_list_size (word_list);
86 					*out_n_suggs = n_suggestions;
87 
88 					if (n_suggestions)
89 						{
90 							sugg_arr = g_new0 (char *, n_suggestions + 1);
91 
92 							for (size_t i = 0; i < n_suggestions; i++)
93 								{
94 									const char *sugg = aspell_string_enumeration_next (suggestions);
95 									if (sugg)
96 										sugg_arr[i] = g_strdup (sugg);
97 								}
98 						}
99 					delete_aspell_string_enumeration (suggestions);
100 				}
101 		}
102 
103 	return sugg_arr;
104 }
105 
106 static void
aspell_dict_add_to_personal(EnchantDict * me,const char * const word,size_t len)107 aspell_dict_add_to_personal (EnchantDict * me,
108 			     const char *const word, size_t len)
109 {
110 	AspellSpeller *manager = (AspellSpeller *) me->user_data;
111 	aspell_speller_add_to_personal (manager, word, len);
112 	aspell_speller_save_all_word_lists (manager);
113 }
114 
115 static void
aspell_dict_add_to_session(EnchantDict * me,const char * const word,size_t len)116 aspell_dict_add_to_session (EnchantDict * me,
117 			    const char *const word, size_t len)
118 {
119 	AspellSpeller *manager = (AspellSpeller *) me->user_data;
120 	aspell_speller_add_to_session (manager, word, len);
121 }
122 
123 static void
aspell_dict_store_replacement(EnchantDict * me,const char * const mis,size_t mis_len,const char * const cor,size_t cor_len)124 aspell_dict_store_replacement (EnchantDict * me,
125 			       const char *const mis, size_t mis_len,
126 			       const char *const cor, size_t cor_len)
127 {
128 	AspellSpeller *manager = (AspellSpeller *) me->user_data;
129 	aspell_speller_store_replacement (manager, mis, mis_len,
130 					  cor, cor_len);
131 	aspell_speller_save_all_word_lists (manager);
132 }
133 
134 static EnchantDict *
aspell_provider_request_dict(EnchantProvider * me _GL_UNUSED_PARAMETER,const char * const tag)135 aspell_provider_request_dict (EnchantProvider * me _GL_UNUSED_PARAMETER, const char *const tag)
136 {
137 	AspellConfig *spell_config = new_aspell_config ();
138 	aspell_config_replace (spell_config, "language-tag", tag);
139 	aspell_config_replace (spell_config, "encoding", "utf-8");
140 
141 	AspellCanHaveError *spell_error = new_aspell_speller (spell_config);
142 	delete_aspell_config (spell_config);
143 
144 	if (aspell_error_number (spell_error) != 0)
145 		{
146 			delete_aspell_can_have_error(spell_error);
147 			return NULL;
148 		}
149 
150 	AspellSpeller *manager = to_aspell_speller (spell_error);
151 
152 	EnchantDict *dict = g_new0 (EnchantDict, 1);
153 	dict->user_data = (void *) manager;
154 	dict->check = aspell_dict_check;
155 	dict->suggest = aspell_dict_suggest;
156 	dict->add_to_personal = aspell_dict_add_to_personal;
157 	dict->add_to_session = aspell_dict_add_to_session;
158 	dict->store_replacement = aspell_dict_store_replacement;
159 
160 	return dict;
161 }
162 
163 static void
aspell_provider_dispose_dict(EnchantProvider * me _GL_UNUSED_PARAMETER,EnchantDict * dict)164 aspell_provider_dispose_dict (EnchantProvider * me _GL_UNUSED_PARAMETER, EnchantDict * dict)
165 {
166 	AspellSpeller *manager = (AspellSpeller *) dict->user_data;
167 	delete_aspell_speller (manager);
168 
169 	g_free (dict);
170 }
171 
172 static char **
aspell_provider_list_dicts(EnchantProvider * me _GL_UNUSED_PARAMETER,size_t * out_n_dicts)173 aspell_provider_list_dicts (EnchantProvider * me _GL_UNUSED_PARAMETER,
174 			    size_t * out_n_dicts)
175 {
176 	AspellConfig * spell_config = new_aspell_config ();
177 	AspellDictInfoList * dlist = get_aspell_dict_info_list (spell_config);
178 
179 	*out_n_dicts = 0;
180 	AspellDictInfoEnumeration * dels = aspell_dict_info_list_elements (dlist);
181 
182 	/* Note: aspell_dict_info_list_size() always returns zero: https://github.com/GNUAspell/aspell/issues/155 */
183 	const AspellDictInfo * entry;
184 	while ( (entry = aspell_dict_info_enumeration_next(dels)) != 0)
185 		(*out_n_dicts)++;
186 	delete_aspell_dict_info_enumeration (dels);
187 
188 	char ** out_list = NULL;
189 
190 	if (*out_n_dicts) {
191 		out_list = g_new0 (char *, *out_n_dicts + 1);
192 		dels = aspell_dict_info_list_elements (dlist);
193 
194 		for (size_t i = 0; i < *out_n_dicts; i++) {
195 			entry = aspell_dict_info_enumeration_next (dels);
196 			/* FIXME: should this be entry->code or entry->name ? */
197 			out_list[i] = g_strdup (entry->code);
198 		}
199 
200 		delete_aspell_dict_info_enumeration (dels);
201 	}
202 
203 	delete_aspell_config (spell_config);
204 
205 	return out_list;
206 }
207 
208 static void
aspell_provider_dispose(EnchantProvider * me)209 aspell_provider_dispose (EnchantProvider * me)
210 {
211 	g_free (me);
212 }
213 
214 static const char *
aspell_provider_identify(EnchantProvider * me _GL_UNUSED_PARAMETER)215 aspell_provider_identify (EnchantProvider * me _GL_UNUSED_PARAMETER)
216 {
217 	return "aspell";
218 }
219 
220 static const char *
aspell_provider_describe(EnchantProvider * me _GL_UNUSED_PARAMETER)221 aspell_provider_describe (EnchantProvider * me _GL_UNUSED_PARAMETER)
222 {
223 	return "Aspell Provider";
224 }
225 
226 EnchantProvider *
init_enchant_provider(void)227 init_enchant_provider (void)
228 {
229 	EnchantProvider *provider = g_new0 (EnchantProvider, 1);
230 	provider->dispose = aspell_provider_dispose;
231 	provider->request_dict = aspell_provider_request_dict;
232 	provider->dispose_dict = aspell_provider_dispose_dict;
233 	provider->identify = aspell_provider_identify;
234 	provider->describe = aspell_provider_describe;
235 	provider->list_dicts = aspell_provider_list_dicts;
236 
237 	return provider;
238 }
239