Ticket #2914 (disable annoying aspell warnings about spelling language)
[midnight-commander.git] / src / editor / spell.c
blob4ca18b9c65b43f34ce36acca657bb636ad36ca33
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 "src/setup.h"
40 #include "edit-impl.h"
41 #include "spell.h"
43 /*** global variables ****************************************************************************/
45 /*** file scope macro definitions ****************************************************************/
47 /*** file scope type declarations ****************************************************************/
49 typedef struct aspell_struct
51 AspellConfig *config;
52 AspellSpeller *speller;
53 } spell_t;
55 /*** file scope variables ************************************************************************/
57 static GModule *spell_module = NULL;
58 static spell_t *global_speller = NULL;
60 static struct AspellConfig *(*mc_new_aspell_config) (void);
61 static int (*mc_aspell_config_replace) (struct AspellConfig * ths, const char *key,
62 const char *value);
63 static struct AspellCanHaveError *(*mc_new_aspell_speller) (struct AspellConfig * config);
64 static unsigned int (*mc_aspell_error_number) (const struct AspellCanHaveError * ths);
65 static const char *(*mc_aspell_speller_error_message) (const struct AspellSpeller * ths);
66 const struct AspellError *(*mc_aspell_speller_error) (const struct AspellSpeller * ths);
68 static struct AspellSpeller *(*mc_to_aspell_speller) (struct AspellCanHaveError * obj);
69 static int (*mc_aspell_speller_check) (struct AspellSpeller * ths, const char *word, int word_size);
70 static const struct AspellWordList *(*mc_aspell_speller_suggest) (struct AspellSpeller * ths,
71 const char *word, int word_size);
72 static struct AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList
73 * ths);
74 static const char *(*mc_aspell_config_retrieve) (struct AspellConfig * ths, const char *key);
75 static void (*mc_delete_aspell_speller) (struct AspellSpeller * ths);
76 static void (*mc_delete_aspell_config) (struct AspellConfig * ths);
77 static void (*mc_delete_aspell_can_have_error) (struct AspellCanHaveError * ths);
78 static const char *(*mc_aspell_error_message) (const struct AspellCanHaveError * ths);
79 static void (*mc_delete_aspell_string_enumeration) (struct AspellStringEnumeration * ths);
80 static struct AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
81 (const struct AspellDictInfoList * ths);
82 static struct AspellDictInfoList *(*mc_get_aspell_dict_info_list) (struct AspellConfig * config);
83 static const struct AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
84 (struct AspellDictInfoEnumeration * ths);
85 static const char *(*mc_aspell_string_enumeration_next) (struct AspellStringEnumeration * ths);
86 static void (*mc_delete_aspell_dict_info_enumeration) (struct AspellDictInfoEnumeration * ths);
87 static unsigned int (*mc_aspell_word_list_size) (const struct AspellWordList * ths);
88 static const struct AspellError *(*mc_aspell_error) (const struct AspellCanHaveError * ths);
89 static int (*mc_aspell_speller_add_to_personal) (struct AspellSpeller * ths, const char *word,
90 int word_size);
91 static int (*mc_aspell_speller_save_all_word_lists) (struct AspellSpeller * ths);
93 static struct
95 const char *code;
96 const char *name;
97 } spell_codes_map[] =
99 /* *INDENT-OFF* */
100 {"br", "Breton"},
101 {"cs", "Czech"},
102 {"cy", "Welsh"},
103 {"da", "Danish"},
104 {"de", "German"},
105 {"el", "Greek"},
106 {"en", "English"},
107 {"en_GB", "British English"},
108 {"en_CA", "Canadian English"},
109 {"en_US", "American English"},
110 {"eo", "Esperanto"},
111 {"es", "Spanish"},
112 {"fo", "Faroese"},
113 {"fr", "French"},
114 {"it", "Italian"},
115 {"nl", "Dutch"},
116 {"no", "Norwegian"},
117 {"pl", "Polish"},
118 {"pt", "Portuguese"},
119 {"ro", "Romanian"},
120 {"ru", "Russian"},
121 {"sk", "Slovak"},
122 {"sv", "Swedish"},
123 {"uk", "Ukrainian"},
124 {NULL, NULL}
125 /* *INDENT-ON* */
128 /*** file scope functions ************************************************************************/
129 /* --------------------------------------------------------------------------------------------- */
131 * Found the language name by language code. For example: en_US -> American English.
133 * @param code Short name of the language (ru, en, pl, uk, etc...)
134 * @return language name
137 static const char *
138 spell_decode_lang (const char *code)
140 size_t i;
142 for (i = 0; spell_codes_map[i].code != NULL; i++)
144 if (strcmp (spell_codes_map[i].code, code) == 0)
145 return spell_codes_map[i].name;
148 return code;
151 /* --------------------------------------------------------------------------------------------- */
153 * Checks if aspell library and symbols are available.
155 * @return FALSE or error
158 static gboolean
159 spell_available (void)
161 gchar *spell_module_fname;
162 gboolean ret = FALSE;
164 if (spell_module != NULL)
165 return TRUE;
167 spell_module_fname = g_module_build_path (NULL, "libaspell");
168 spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
170 g_free (spell_module_fname);
172 if (spell_module == NULL)
173 return FALSE;
175 if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
176 goto error_ret;
178 if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
179 (void *) &mc_aspell_dict_info_list_elements))
180 goto error_ret;
182 if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
183 (void *) &mc_aspell_dict_info_enumeration_next))
184 goto error_ret;
186 if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
187 goto error_ret;
189 if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
190 goto error_ret;
192 if (!g_module_symbol (spell_module, "aspell_speller_error_message",
193 (void *) &mc_aspell_speller_error_message))
194 goto error_ret;
196 if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
197 goto error_ret;
199 if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
200 goto error_ret;
202 if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
203 goto error_ret;
205 if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
206 goto error_ret;
208 if (!g_module_symbol
209 (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
210 goto error_ret;
212 if (!g_module_symbol
213 (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
214 goto error_ret;
216 if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
217 (void *) &mc_aspell_string_enumeration_next))
218 goto error_ret;
220 if (!g_module_symbol
221 (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
222 goto error_ret;
224 if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
225 goto error_ret;
227 if (!g_module_symbol
228 (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
229 goto error_ret;
231 if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
232 goto error_ret;
234 if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
235 (void *) &mc_delete_aspell_string_enumeration))
236 goto error_ret;
238 if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
239 (void *) &mc_get_aspell_dict_info_list))
240 goto error_ret;
242 if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
243 (void *) &mc_delete_aspell_can_have_error))
244 goto error_ret;
246 if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
247 (void *) &mc_delete_aspell_dict_info_enumeration))
248 goto error_ret;
250 if (!g_module_symbol
251 (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
252 goto error_ret;
254 if (!g_module_symbol
255 (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
256 goto error_ret;
258 if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
259 (void *) &mc_aspell_speller_add_to_personal))
260 goto error_ret;
262 if (!g_module_symbol (spell_module, "aspell_speller_save_all_word_lists",
263 (void *) &mc_aspell_speller_save_all_word_lists))
264 goto error_ret;
266 ret = TRUE;
268 error_ret:
269 if (!ret)
271 g_module_close (spell_module);
272 spell_module = NULL;
274 return ret;
277 /* --------------------------------------------------------------------------------------------- */
278 /*** public functions ****************************************************************************/
279 /* --------------------------------------------------------------------------------------------- */
281 * Initialization of Aspell support.
284 void
285 aspell_init (void)
287 AspellCanHaveError *error = NULL;
289 if (strcmp (spell_language, "NONE") == 0)
290 return;
292 if (global_speller != NULL)
293 return;
295 global_speller = g_try_malloc (sizeof (spell_t));
296 if (global_speller == NULL)
297 return;
299 if (!spell_available ())
301 g_free (global_speller);
302 global_speller = NULL;
303 return;
306 global_speller->config = mc_new_aspell_config ();
307 global_speller->speller = NULL;
309 if (spell_language != NULL)
311 int res;
313 res = mc_aspell_config_replace (global_speller->config, "lang", spell_language);
316 error = mc_new_aspell_speller (global_speller->config);
318 if (mc_aspell_error_number (error) == 0)
319 global_speller->speller = mc_to_aspell_speller (error);
320 else
322 edit_error_dialog (_("Error"), mc_aspell_error_message (error));
323 mc_delete_aspell_can_have_error (error);
324 aspell_clean ();
328 /* --------------------------------------------------------------------------------------------- */
330 * Deinitialization of Aspell support.
333 void
334 aspell_clean (void)
336 if (global_speller == NULL)
337 return;
339 if (global_speller->speller != NULL)
340 mc_delete_aspell_speller (global_speller->speller);
342 if (global_speller->config != NULL)
343 mc_delete_aspell_config (global_speller->config);
345 g_free (global_speller);
346 global_speller = NULL;
348 g_module_close (spell_module);
349 spell_module = NULL;
352 /* --------------------------------------------------------------------------------------------- */
354 * Get array of available languages.
356 * @param lang_list Array of languages. Must be cleared before use
357 * @return language list length
360 unsigned int
361 aspell_get_lang_list (GArray * lang_list)
363 AspellDictInfoList *dlist;
364 AspellDictInfoEnumeration *elem;
365 const AspellDictInfo *entry;
366 unsigned int i = 0;
368 if (spell_module == NULL)
369 return 0;
371 /* the returned pointer should _not_ need to be deleted */
372 dlist = mc_get_aspell_dict_info_list (global_speller->config);
373 elem = mc_aspell_dict_info_list_elements (dlist);
375 while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
377 if (entry->name != NULL)
379 char *tmp;
381 tmp = g_strdup (entry->name);
382 g_array_append_val (lang_list, tmp);
383 i++;
387 mc_delete_aspell_dict_info_enumeration (elem);
389 return i;
392 /* --------------------------------------------------------------------------------------------- */
394 * Clear the array of languages.
396 * @param array Array of languages
399 void
400 aspell_array_clean (GArray * array)
402 if (array != NULL)
404 guint i = 0;
406 for (i = 0; i < array->len; ++i)
408 char *tmp;
410 tmp = g_array_index (array, char *, i);
411 g_free (tmp);
413 g_array_free (array, TRUE);
417 /* --------------------------------------------------------------------------------------------- */
419 * Get the current language name.
421 * @return language name
424 const char *
425 aspell_get_lang (void)
427 const char *code;
429 code = mc_aspell_config_retrieve (global_speller->config, "lang");
430 return spell_decode_lang (code);
433 /* --------------------------------------------------------------------------------------------- */
435 * Set the language.
437 * @param lang Language name
438 * @return FALSE or error
441 gboolean
442 aspell_set_lang (const char *lang)
444 if (lang != NULL)
446 int res;
447 AspellCanHaveError *error;
448 const char *spell_codeset;
450 g_free (spell_language);
451 spell_language = g_strdup (lang);
453 #ifdef HAVE_CHARSET
454 if (mc_global.source_codepage > 0)
455 spell_codeset = get_codepage_id (mc_global.source_codepage);
456 else
457 #endif
458 spell_codeset = str_detect_termencoding ();
460 res = mc_aspell_config_replace (global_speller->config, "lang", lang);
461 res = mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
463 /* the returned pointer should _not_ need to be deleted */
464 if (global_speller->speller != NULL)
465 mc_delete_aspell_speller (global_speller->speller);
467 global_speller->speller = NULL;
469 error = mc_new_aspell_speller (global_speller->config);
470 if (mc_aspell_error (error) != 0)
472 mc_delete_aspell_can_have_error (error);
473 return FALSE;
476 global_speller->speller = mc_to_aspell_speller (error);
478 return TRUE;
481 /* --------------------------------------------------------------------------------------------- */
483 * Check word.
485 * @param word Word for spell check
486 * @param word_size Word size (in bytes)
487 * @return FALSE if word is not in the dictionary
490 gboolean
491 aspell_check (const char *word, const int word_size)
493 int res = 0;
495 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
496 res = mc_aspell_speller_check (global_speller->speller, word, word_size);
498 return (res == 1);
501 /* --------------------------------------------------------------------------------------------- */
503 * Examine dictionaries and suggest possible words that may repalce the incorrect word.
505 * @param suggest array of words to iterate through
506 * @param word Word for spell check
507 * @param word_size Word size (in bytes)
508 * @return count of suggests for the word
511 unsigned int
512 aspell_suggest (GArray * suggest, const char *word, const int word_size)
514 unsigned int size = 0;
516 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
518 const AspellWordList *wordlist;
520 wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
521 if (wordlist != NULL)
523 AspellStringEnumeration *elements = NULL;
524 unsigned int i;
526 elements = mc_aspell_word_list_elements (wordlist);
527 size = mc_aspell_word_list_size (wordlist);
529 for (i = 0; i < size; i++)
531 const char *cur_sugg_word;
533 cur_sugg_word = g_strdup (mc_aspell_string_enumeration_next (elements));
534 if (cur_sugg_word != NULL)
535 g_array_append_val (suggest, cur_sugg_word);
538 mc_delete_aspell_string_enumeration (elements);
542 return size;
545 /* --------------------------------------------------------------------------------------------- */
547 * Add word to personal dictionary.
549 * @param word Word for spell check
550 * @param word_size Word size (in bytes)
551 * @return FALSE or error
553 gboolean
554 aspell_add_to_dict (const char *word, int word_size)
556 mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
558 if (mc_aspell_speller_error (global_speller->speller) != 0)
560 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
561 return FALSE;
564 mc_aspell_speller_save_all_word_lists (global_speller->speller);
566 if (mc_aspell_speller_error (global_speller->speller) != 0)
568 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
569 return FALSE;
572 return TRUE;
575 /* --------------------------------------------------------------------------------------------- */