Avoid compiler errors like: error: variable 'xxx' set but not used [-Werror=unused...
[midnight-commander.git] / src / editor / spell.c
blob8d8ec406eb1eff91109d55ead0548aaf2163dfee
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)
310 mc_aspell_config_replace (global_speller->config, "lang", spell_language);
312 error = mc_new_aspell_speller (global_speller->config);
314 if (mc_aspell_error_number (error) == 0)
315 global_speller->speller = mc_to_aspell_speller (error);
316 else
318 edit_error_dialog (_("Error"), mc_aspell_error_message (error));
319 mc_delete_aspell_can_have_error (error);
320 aspell_clean ();
324 /* --------------------------------------------------------------------------------------------- */
326 * Deinitialization of Aspell support.
329 void
330 aspell_clean (void)
332 if (global_speller == NULL)
333 return;
335 if (global_speller->speller != NULL)
336 mc_delete_aspell_speller (global_speller->speller);
338 if (global_speller->config != NULL)
339 mc_delete_aspell_config (global_speller->config);
341 g_free (global_speller);
342 global_speller = NULL;
344 g_module_close (spell_module);
345 spell_module = NULL;
348 /* --------------------------------------------------------------------------------------------- */
350 * Get array of available languages.
352 * @param lang_list Array of languages. Must be cleared before use
353 * @return language list length
356 unsigned int
357 aspell_get_lang_list (GArray * lang_list)
359 AspellDictInfoList *dlist;
360 AspellDictInfoEnumeration *elem;
361 const AspellDictInfo *entry;
362 unsigned int i = 0;
364 if (spell_module == NULL)
365 return 0;
367 /* the returned pointer should _not_ need to be deleted */
368 dlist = mc_get_aspell_dict_info_list (global_speller->config);
369 elem = mc_aspell_dict_info_list_elements (dlist);
371 while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
373 if (entry->name != NULL)
375 char *tmp;
377 tmp = g_strdup (entry->name);
378 g_array_append_val (lang_list, tmp);
379 i++;
383 mc_delete_aspell_dict_info_enumeration (elem);
385 return i;
388 /* --------------------------------------------------------------------------------------------- */
390 * Clear the array of languages.
392 * @param array Array of languages
395 void
396 aspell_array_clean (GArray * array)
398 if (array != NULL)
400 guint i = 0;
402 for (i = 0; i < array->len; ++i)
404 char *tmp;
406 tmp = g_array_index (array, char *, i);
407 g_free (tmp);
409 g_array_free (array, TRUE);
413 /* --------------------------------------------------------------------------------------------- */
415 * Get the current language name.
417 * @return language name
420 const char *
421 aspell_get_lang (void)
423 const char *code;
425 code = mc_aspell_config_retrieve (global_speller->config, "lang");
426 return spell_decode_lang (code);
429 /* --------------------------------------------------------------------------------------------- */
431 * Set the language.
433 * @param lang Language name
434 * @return FALSE or error
437 gboolean
438 aspell_set_lang (const char *lang)
440 if (lang != NULL)
442 AspellCanHaveError *error;
443 const char *spell_codeset;
445 g_free (spell_language);
446 spell_language = g_strdup (lang);
448 #ifdef HAVE_CHARSET
449 if (mc_global.source_codepage > 0)
450 spell_codeset = get_codepage_id (mc_global.source_codepage);
451 else
452 #endif
453 spell_codeset = str_detect_termencoding ();
455 mc_aspell_config_replace (global_speller->config, "lang", lang);
456 mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
458 /* the returned pointer should _not_ need to be deleted */
459 if (global_speller->speller != NULL)
460 mc_delete_aspell_speller (global_speller->speller);
462 global_speller->speller = NULL;
464 error = mc_new_aspell_speller (global_speller->config);
465 if (mc_aspell_error (error) != 0)
467 mc_delete_aspell_can_have_error (error);
468 return FALSE;
471 global_speller->speller = mc_to_aspell_speller (error);
473 return TRUE;
476 /* --------------------------------------------------------------------------------------------- */
478 * Check word.
480 * @param word Word for spell check
481 * @param word_size Word size (in bytes)
482 * @return FALSE if word is not in the dictionary
485 gboolean
486 aspell_check (const char *word, const int word_size)
488 int res = 0;
490 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
491 res = mc_aspell_speller_check (global_speller->speller, word, word_size);
493 return (res == 1);
496 /* --------------------------------------------------------------------------------------------- */
498 * Examine dictionaries and suggest possible words that may repalce the incorrect word.
500 * @param suggest array of words to iterate through
501 * @param word Word for spell check
502 * @param word_size Word size (in bytes)
503 * @return count of suggests for the word
506 unsigned int
507 aspell_suggest (GArray * suggest, const char *word, const int word_size)
509 unsigned int size = 0;
511 if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
513 const AspellWordList *wordlist;
515 wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
516 if (wordlist != NULL)
518 AspellStringEnumeration *elements = NULL;
519 unsigned int i;
521 elements = mc_aspell_word_list_elements (wordlist);
522 size = mc_aspell_word_list_size (wordlist);
524 for (i = 0; i < size; i++)
526 const char *cur_sugg_word;
528 cur_sugg_word = g_strdup (mc_aspell_string_enumeration_next (elements));
529 if (cur_sugg_word != NULL)
530 g_array_append_val (suggest, cur_sugg_word);
533 mc_delete_aspell_string_enumeration (elements);
537 return size;
540 /* --------------------------------------------------------------------------------------------- */
542 * Add word to personal dictionary.
544 * @param word Word for spell check
545 * @param word_size Word size (in bytes)
546 * @return FALSE or error
548 gboolean
549 aspell_add_to_dict (const char *word, int word_size)
551 mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
553 if (mc_aspell_speller_error (global_speller->speller) != 0)
555 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
556 return FALSE;
559 mc_aspell_speller_save_all_word_lists (global_speller->speller);
561 if (mc_aspell_speller_error (global_speller->speller) != 0)
563 edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
564 return FALSE;
567 return TRUE;
570 /* --------------------------------------------------------------------------------------------- */