809990c23aebb00dc3f4be408ff580491742d0f1
[midnight-commander.git] / src / editor / spell.c
blob809990c23aebb00dc3f4be408ff580491742d0f1
1 /*
2 Editor spell checker
4 Copyright (C) 2012
5 The Free Software Foundation, Inc.
7 Written by:
8 Ilia Maslakov <il.smind@gmail.com>, 2012
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <gmodule.h>
31 #include <aspell.h>
33 #ifdef HAVE_CHARSET
34 #include "lib/charsets.h"
35 #endif
36 #include "lib/strutil.h"
38 #include "edit-impl.h"
39 #include "spell.h"
41 /*** global variables ****************************************************************************/
43 /*** file scope macro definitions ****************************************************************/
45 /*** file scope type declarations ****************************************************************/
47 typedef struct aspell_struct
49 AspellConfig *config;
50 AspellSpeller *speller;
51 } spell_t;
53 /*** file scope variables ************************************************************************/
55 static GModule *spell_module = NULL;
56 static spell_t *global_speller = NULL;
58 static struct AspellConfig *(*mc_new_aspell_config) (void);
59 static int (*mc_aspell_config_replace) (struct AspellConfig * ths, const char *key,
60 const char *value);
61 static struct AspellCanHaveError *(*mc_new_aspell_speller) (struct AspellConfig * config);
62 static unsigned int (*mc_aspell_error_number) (const struct AspellCanHaveError * ths);
63 static const char *(*mc_aspell_speller_error_message) (const struct AspellSpeller * ths);
64 const struct AspellError *(*mc_aspell_speller_error) (const struct AspellSpeller * ths);
66 static struct AspellSpeller *(*mc_to_aspell_speller) (struct AspellCanHaveError * obj);
67 static int (*mc_aspell_speller_check) (struct AspellSpeller * ths, const char *word, int word_size);
68 static const struct AspellWordList *(*mc_aspell_speller_suggest) (struct AspellSpeller * ths,
69 const char *word, int word_size);
70 static struct AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList
71 * ths);
72 static const char *(*mc_aspell_config_retrieve) (struct AspellConfig * ths, const char *key);
73 static void (*mc_delete_aspell_speller) (struct AspellSpeller * ths);
74 static void (*mc_delete_aspell_config) (struct AspellConfig * ths);
75 static void (*mc_delete_aspell_can_have_error) (struct AspellCanHaveError * ths);
76 static const char *(*mc_aspell_error_message) (const struct AspellCanHaveError * ths);
77 static void (*mc_delete_aspell_string_enumeration) (struct AspellStringEnumeration * ths);
78 static struct AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
79 (const struct AspellDictInfoList * ths);
80 static struct AspellDictInfoList *(*mc_get_aspell_dict_info_list) (struct AspellConfig * config);
81 static const struct AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
82 (struct AspellDictInfoEnumeration * ths);
83 static const char *(*mc_aspell_string_enumeration_next) (struct AspellStringEnumeration * ths);
84 static void (*mc_delete_aspell_dict_info_enumeration) (struct AspellDictInfoEnumeration * ths);
85 static unsigned int (*mc_aspell_word_list_size) (const struct AspellWordList * ths);
86 static const struct AspellError *(*mc_aspell_error) (const struct AspellCanHaveError * ths);
87 static int (*mc_aspell_speller_add_to_personal) (struct AspellSpeller * ths, const char *word,
88 int word_size);
89 static int (*mc_aspell_speller_save_all_word_lists) (struct AspellSpeller * ths);
91 static struct
93 const char *code;
94 const char *name;
95 } spell_codes_map[] =
97 /* *INDENT-OFF* */
98 {"br", "Breton"},
99 {"cs", "Czech"},
100 {"cy", "Welsh"},
101 {"da", "Danish"},
102 {"de", "German"},
103 {"el", "Greek"},
104 {"en", "English"},
105 {"en_GB", "British English"},
106 {"en_CA", "Canadian English"},
107 {"en_US", "American English"},
108 {"eo", "Esperanto"},
109 {"es", "Spanish"},
110 {"fo", "Faroese"},
111 {"fr", "French"},
112 {"it", "Italian"},
113 {"nl", "Dutch"},
114 {"no", "Norwegian"},
115 {"pl", "Polish"},
116 {"pt", "Portuguese"},
117 {"ro", "Romanian"},
118 {"ru", "Russian"},
119 {"sk", "Slovak"},
120 {"sv", "Swedish"},
121 {"uk", "Ukrainian"},
122 {NULL, NULL}
123 /* *INDENT-ON* */
126 /*** file scope functions ************************************************************************/
127 /* --------------------------------------------------------------------------------------------- */
129 * Found the language name by language code. For example: en_US -> American English.
131 * @param code Short name of the language (ru, en, pl, uk, etc...)
132 * @returns the language name
135 static const char *
136 spell_decode_lang (const char *code)
138 size_t i;
140 for (i = 0; spell_codes_map[i].code != NULL; i++)
142 if (strcmp (spell_codes_map[i].code, code) == 0)
143 return spell_codes_map[i].name;
146 return code;
149 /* --------------------------------------------------------------------------------------------- */
151 * Checks if aspell library and symbols are available.
153 * @returns FALSE or error
156 static gboolean
157 spell_available (void)
159 gchar *spell_module_fname;
160 gboolean ret = FALSE;
162 if (spell_module != NULL)
163 return TRUE;
165 spell_module_fname = g_module_build_path (NULL, "libaspell");
166 spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
168 g_free (spell_module_fname);
170 if (spell_module == NULL)
171 return FALSE;
173 if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
174 goto error_ret;
176 if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
177 (void *) &mc_aspell_dict_info_list_elements))
178 goto error_ret;
180 if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
181 (void *) &mc_aspell_dict_info_enumeration_next))
182 goto error_ret;
184 if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
185 goto error_ret;
187 if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
188 goto error_ret;
190 if (!g_module_symbol (spell_module, "aspell_speller_error_message",
191 (void *) &mc_aspell_speller_error_message))
192 goto error_ret;
194 if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
195 goto error_ret;
197 if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
198 goto error_ret;
200 if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
201 goto error_ret;
203 if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
204 goto error_ret;
206 if (!g_module_symbol
207 (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
208 goto error_ret;
210 if (!g_module_symbol
211 (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
212 goto error_ret;
214 if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
215 (void *) &mc_aspell_string_enumeration_next))
216 goto error_ret;
218 if (!g_module_symbol
219 (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
220 goto error_ret;
222 if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
223 goto error_ret;
225 if (!g_module_symbol
226 (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
227 goto error_ret;
229 if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
230 goto error_ret;
232 if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
233 (void *) &mc_delete_aspell_string_enumeration))
234 goto error_ret;
236 if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
237 (void *) &mc_get_aspell_dict_info_list))
238 goto error_ret;
240 if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
241 (void *) &mc_delete_aspell_can_have_error))
242 goto error_ret;
244 if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
245 (void *) &mc_delete_aspell_dict_info_enumeration))
246 goto error_ret;
248 if (!g_module_symbol
249 (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
250 goto error_ret;
252 if (!g_module_symbol
253 (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
254 goto error_ret;
256 if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
257 (void *) &mc_aspell_speller_add_to_personal))
258 goto error_ret;
260 if (!g_module_symbol (spell_module, "aspell_speller_save_all_word_lists",
261 (void *) &mc_aspell_speller_save_all_word_lists))
262 goto error_ret;
264 ret = TRUE;
266 error_ret:
267 if (!ret)
269 g_module_close (spell_module);
270 spell_module = NULL;
272 return ret;
275 /* --------------------------------------------------------------------------------------------- */
276 /*** public functions ****************************************************************************/
277 /* --------------------------------------------------------------------------------------------- */
279 * Initialization of Aspell support.
282 void
283 aspell_init (void)
285 AspellCanHaveError *error = NULL;
287 if (global_speller != NULL)
288 return;
290 global_speller = g_try_malloc (sizeof (spell_t));
291 if (global_speller == NULL)
292 return;
294 if (!spell_available ())
296 g_free (global_speller);
297 global_speller = NULL;
298 return;
301 global_speller->config = mc_new_aspell_config ();
302 global_speller->speller = NULL;
304 error = mc_new_aspell_speller (global_speller->config);
306 if (mc_aspell_error_number (error) == 0)
307 global_speller->speller = mc_to_aspell_speller (error);
308 else
310 edit_error_dialog (_("Error"), mc_aspell_error_message (error));
311 mc_delete_aspell_can_have_error (error);
312 aspell_clean ();
316 /* --------------------------------------------------------------------------------------------- */
318 * Deinitialization of Aspell support.
321 void
322 aspell_clean (void)
324 if (global_speller == NULL)
325 return;
327 if (global_speller->speller != NULL)
328 mc_delete_aspell_speller (global_speller->speller);
330 if (global_speller->config != NULL)
331 mc_delete_aspell_config (global_speller->config);
333 g_free (global_speller);
334 global_speller = NULL;
336 g_module_close (spell_module);
337 spell_module = NULL;
340 /* --------------------------------------------------------------------------------------------- */
342 * Get array of available languages.
344 * @param lang_list Array of languages. Must be cleared before use
345 * @returns language list length
348 unsigned int
349 aspell_get_lang_list (GArray * lang_list)
351 AspellDictInfoList *dlist;
352 AspellDictInfoEnumeration *elem;
353 const AspellDictInfo *entry;
354 unsigned int i = 0;
356 if (spell_module == NULL)
357 return 0;
359 /* the returned pointer should _not_ need to be deleted */
360 dlist = mc_get_aspell_dict_info_list (global_speller->config);
361 elem = mc_aspell_dict_info_list_elements (dlist);
363 while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
365 if (entry->name != NULL)
367 char *tmp;
369 tmp = g_strdup (entry->name);
370 g_array_append_val (lang_list, tmp);
371 i++;
375 mc_delete_aspell_dict_info_enumeration (elem);
377 return i;
380 /* --------------------------------------------------------------------------------------------- */
382 * Clear the array of languages.
384 * @param array Array of languages
387 void
388 aspell_array_clean (GArray * array)
390 if (array != NULL)
392 guint i = 0;
394 for (i = 0; i < array->len; ++i)
396 char *tmp;
398 tmp = g_array_index (array, char *, i);
399 g_free (tmp);
401 g_array_free (array, TRUE);
405 /* --------------------------------------------------------------------------------------------- */
407 * Get the current language name.
409 * @returns Language name
412 const char *
413 aspell_get_lang (void)
415 const char *code;
417 code = mc_aspell_config_retrieve (global_speller->config, "lang");
418 return spell_decode_lang (code);
421 /* --------------------------------------------------------------------------------------------- */
423 * Set the language.
425 * @param lang Language name
426 * @returns FALSE or error
429 gboolean
430 aspell_set_lang (const char *lang)
432 if (lang != NULL)
434 int res;
435 AspellCanHaveError *error;
436 const char *spell_codeset;
438 #ifdef HAVE_CHARSET
439 if (mc_global.source_codepage > 0)
440 spell_codeset = get_codepage_id (mc_global.source_codepage);
441 else
442 #endif
443 spell_codeset = str_detect_termencoding ();
445 res = mc_aspell_config_replace (global_speller->config, "lang", lang);
446 res = mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
448 /* the returned pointer should _not_ need to be deleted */
449 if (global_speller->speller != NULL)
450 mc_delete_aspell_speller (global_speller->speller);
452 global_speller->speller = NULL;
454 error = mc_new_aspell_speller (global_speller->config);
455 if (mc_aspell_error (error) != 0)
457 mc_delete_aspell_can_have_error (error);
458 return FALSE;
461 global_speller->speller = mc_to_aspell_speller (error);
463 return TRUE;
466 /* --------------------------------------------------------------------------------------------- */
468 * Check word.
470 * @param word Word for spell check
471 * @param word_size Word size (in bytes)
472 * @returns FALSE if word is not in the dictionary
475 gboolean
476 aspell_check (const char *word, const int word_size)
478 int res = 0;
480 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
481 res = mc_aspell_speller_check (global_speller->speller, word, word_size);
483 return (res == 1);
486 /* --------------------------------------------------------------------------------------------- */
488 * Examine dictionaries and suggest possible words that may repalce the incorrect word.
490 * @param suggest array of words to iterate through
491 * @param word Word for spell check
492 * @param word_size Word size (in bytes)
493 * @returns count of suggests for the word
496 unsigned int
497 aspell_suggest (GArray * suggest, const char *word, const int word_size)
499 unsigned int size = 0;
501 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
503 const AspellWordList *wordlist;
505 wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
506 if (wordlist != NULL)
508 AspellStringEnumeration *elements = NULL;
509 unsigned int i;
511 elements = mc_aspell_word_list_elements (wordlist);
512 size = mc_aspell_word_list_size (wordlist);
514 for (i = 0; i < size; i++)
516 const char *cur_sugg_word;
518 cur_sugg_word = g_strdup (mc_aspell_string_enumeration_next (elements));
519 if (cur_sugg_word != NULL)
520 g_array_append_val (suggest, cur_sugg_word);
523 mc_delete_aspell_string_enumeration (elements);
527 return size;
530 /* --------------------------------------------------------------------------------------------- */
532 * Add word to personal dictionary.
534 * @param word Word for spell check
535 * @param word_size Word size (in bytes)
536 * @returns FALSE or error
538 gboolean
539 aspell_add_to_dict (const char *word, int word_size)
541 mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
543 if (mc_aspell_speller_error (global_speller->speller) != 0)
545 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
546 return FALSE;
549 mc_aspell_speller_save_all_word_lists (global_speller->speller);
551 if (mc_aspell_speller_error (global_speller->speller) != 0)
553 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
554 return FALSE;
557 return TRUE;
560 /* --------------------------------------------------------------------------------------------- */