root/src/editor/spell.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. spell_decode_lang
  2. spell_available
  3. aspell_init
  4. aspell_clean
  5. aspell_get_lang_list
  6. aspell_array_clean
  7. aspell_get_lang
  8. aspell_set_lang
  9. aspell_check
  10. aspell_suggest
  11. aspell_add_to_dict

   1 /*
   2    Editor spell checker
   3 
   4    Copyright (C) 2012-2019
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Ilia Maslakov <il.smind@gmail.com>, 2012
   9 
  10    This file is part of the Midnight Commander.
  11 
  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.
  16 
  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.
  21 
  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/>.
  24  */
  25 
  26 #include <config.h>
  27 
  28 #include <stdlib.h>
  29 #include <string.h>
  30 #include <gmodule.h>
  31 #include <aspell.h>
  32 
  33 #ifdef HAVE_CHARSET
  34 #include "lib/charsets.h"
  35 #endif
  36 #include "lib/strutil.h"
  37 
  38 #include "src/setup.h"
  39 
  40 #include "edit-impl.h"
  41 #include "spell.h"
  42 
  43 /*** global variables ****************************************************************************/
  44 
  45 /*** file scope macro definitions ****************************************************************/
  46 
  47 /*** file scope type declarations ****************************************************************/
  48 
  49 typedef struct aspell_struct
  50 {
  51     AspellConfig *config;
  52     AspellSpeller *speller;
  53 } spell_t;
  54 
  55 /*** file scope variables ************************************************************************/
  56 
  57 static GModule *spell_module = NULL;
  58 static spell_t *global_speller = NULL;
  59 
  60 static AspellConfig *(*mc_new_aspell_config) (void);
  61 static int (*mc_aspell_config_replace) (AspellConfig * ths, const char *key, const char *value);
  62 static AspellCanHaveError *(*mc_new_aspell_speller) (AspellConfig * config);
  63 static unsigned int (*mc_aspell_error_number) (const AspellCanHaveError * ths);
  64 static const char *(*mc_aspell_speller_error_message) (const AspellSpeller * ths);
  65 static const AspellError *(*mc_aspell_speller_error) (const AspellSpeller * ths);
  66 
  67 static AspellSpeller *(*mc_to_aspell_speller) (AspellCanHaveError * obj);
  68 static int (*mc_aspell_speller_check) (AspellSpeller * ths, const char *word, int word_size);
  69 static const AspellWordList *(*mc_aspell_speller_suggest) (AspellSpeller * ths,
  70                                                            const char *word, int word_size);
  71 static AspellStringEnumeration *(*mc_aspell_word_list_elements) (const struct AspellWordList * ths);
  72 static const char *(*mc_aspell_config_retrieve) (AspellConfig * ths, const char *key);
  73 static void (*mc_delete_aspell_speller) (AspellSpeller * ths);
  74 static void (*mc_delete_aspell_config) (AspellConfig * ths);
  75 static void (*mc_delete_aspell_can_have_error) (AspellCanHaveError * ths);
  76 static const char *(*mc_aspell_error_message) (const AspellCanHaveError * ths);
  77 static void (*mc_delete_aspell_string_enumeration) (AspellStringEnumeration * ths);
  78 static AspellDictInfoEnumeration *(*mc_aspell_dict_info_list_elements)
  79     (const AspellDictInfoList * ths);
  80 static AspellDictInfoList *(*mc_get_aspell_dict_info_list) (AspellConfig * config);
  81 static const AspellDictInfo *(*mc_aspell_dict_info_enumeration_next)
  82     (AspellDictInfoEnumeration * ths);
  83 static const char *(*mc_aspell_string_enumeration_next) (AspellStringEnumeration * ths);
  84 static void (*mc_delete_aspell_dict_info_enumeration) (AspellDictInfoEnumeration * ths);
  85 static unsigned int (*mc_aspell_word_list_size) (const AspellWordList * ths);
  86 static const AspellError *(*mc_aspell_error) (const AspellCanHaveError * ths);
  87 static int (*mc_aspell_speller_add_to_personal) (AspellSpeller * ths, const char *word,
  88                                                  int word_size);
  89 static int (*mc_aspell_speller_save_all_word_lists) (AspellSpeller * ths);
  90 
  91 static struct
  92 {
  93     const char *code;
  94     const char *name;
  95 } spell_codes_map[] =
  96 {
  97     /* *INDENT-OFF* */
  98     {"br", N_("Breton")},
  99     {"cs", N_("Czech")},
 100     {"cy", N_("Welsh")},
 101     {"da", N_("Danish")},
 102     {"de", N_("German")},
 103     {"el", N_("Greek")},
 104     {"en", N_("English")},
 105     {"en_GB", N_("British English")},
 106     {"en_CA", N_("Canadian English")},
 107     {"en_US", N_("American English")},
 108     {"eo", N_("Esperanto")},
 109     {"es", N_("Spanish")},
 110     {"fo", N_("Faroese")},
 111     {"fr", N_("French")},
 112     {"it", N_("Italian")},
 113     {"nl", N_("Dutch")},
 114     {"no", N_("Norwegian")},
 115     {"pl", N_("Polish")},
 116     {"pt", N_("Portuguese")},
 117     {"ro", N_("Romanian")},
 118     {"ru", N_("Russian")},
 119     {"sk", N_("Slovak")},
 120     {"sv", N_("Swedish")},
 121     {"uk", N_("Ukrainian")},
 122     {NULL, NULL}
 123     /* *INDENT-ON* */
 124 };
 125 
 126 /*** file scope functions ************************************************************************/
 127 /* --------------------------------------------------------------------------------------------- */
 128 /**
 129  * Found the language name by language code. For example: en_US -> American English.
 130  *
 131  * @param code Short name of the language (ru, en, pl, uk, etc...)
 132  * @return language name
 133  */
 134 
 135 static const char *
 136 spell_decode_lang (const char *code)
     /* [previous][next][first][last][top][bottom][index][help]  */
 137 {
 138     size_t i;
 139 
 140     for (i = 0; spell_codes_map[i].code != NULL; i++)
 141     {
 142         if (strcmp (spell_codes_map[i].code, code) == 0)
 143             return _(spell_codes_map[i].name);
 144     }
 145 
 146     return code;
 147 }
 148 
 149 /* --------------------------------------------------------------------------------------------- */
 150 /**
 151  * Checks if aspell library and symbols are available.
 152  *
 153  * @return FALSE or error
 154  */
 155 
 156 static gboolean
 157 spell_available (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 158 {
 159     gchar *spell_module_fname;
 160     gboolean ret = FALSE;
 161 
 162     if (spell_module != NULL)
 163         return TRUE;
 164 
 165     spell_module_fname = g_module_build_path (NULL, "libaspell");
 166     spell_module = g_module_open (spell_module_fname, G_MODULE_BIND_LAZY);
 167 
 168     g_free (spell_module_fname);
 169 
 170     if (spell_module == NULL)
 171         return FALSE;
 172 
 173     if (!g_module_symbol (spell_module, "new_aspell_config", (void *) &mc_new_aspell_config))
 174         goto error_ret;
 175 
 176     if (!g_module_symbol (spell_module, "aspell_dict_info_list_elements",
 177                           (void *) &mc_aspell_dict_info_list_elements))
 178         goto error_ret;
 179 
 180     if (!g_module_symbol (spell_module, "aspell_dict_info_enumeration_next",
 181                           (void *) &mc_aspell_dict_info_enumeration_next))
 182         goto error_ret;
 183 
 184     if (!g_module_symbol (spell_module, "new_aspell_speller", (void *) &mc_new_aspell_speller))
 185         goto error_ret;
 186 
 187     if (!g_module_symbol (spell_module, "aspell_error_number", (void *) &mc_aspell_error_number))
 188         goto error_ret;
 189 
 190     if (!g_module_symbol (spell_module, "aspell_speller_error_message",
 191                           (void *) &mc_aspell_speller_error_message))
 192         goto error_ret;
 193 
 194     if (!g_module_symbol (spell_module, "aspell_speller_error", (void *) &mc_aspell_speller_error))
 195         goto error_ret;
 196 
 197     if (!g_module_symbol (spell_module, "aspell_error", (void *) &mc_aspell_error))
 198         goto error_ret;
 199 
 200     if (!g_module_symbol (spell_module, "to_aspell_speller", (void *) &mc_to_aspell_speller))
 201         goto error_ret;
 202 
 203     if (!g_module_symbol (spell_module, "aspell_speller_check", (void *) &mc_aspell_speller_check))
 204         goto error_ret;
 205 
 206     if (!g_module_symbol
 207         (spell_module, "aspell_speller_suggest", (void *) &mc_aspell_speller_suggest))
 208         goto error_ret;
 209 
 210     if (!g_module_symbol
 211         (spell_module, "aspell_word_list_elements", (void *) &mc_aspell_word_list_elements))
 212         goto error_ret;
 213 
 214     if (!g_module_symbol (spell_module, "aspell_string_enumeration_next",
 215                           (void *) &mc_aspell_string_enumeration_next))
 216         goto error_ret;
 217 
 218     if (!g_module_symbol
 219         (spell_module, "aspell_config_replace", (void *) &mc_aspell_config_replace))
 220         goto error_ret;
 221 
 222     if (!g_module_symbol (spell_module, "aspell_error_message", (void *) &mc_aspell_error_message))
 223         goto error_ret;
 224 
 225     if (!g_module_symbol
 226         (spell_module, "delete_aspell_speller", (void *) &mc_delete_aspell_speller))
 227         goto error_ret;
 228 
 229     if (!g_module_symbol (spell_module, "delete_aspell_config", (void *) &mc_delete_aspell_config))
 230         goto error_ret;
 231 
 232     if (!g_module_symbol (spell_module, "delete_aspell_string_enumeration",
 233                           (void *) &mc_delete_aspell_string_enumeration))
 234         goto error_ret;
 235 
 236     if (!g_module_symbol (spell_module, "get_aspell_dict_info_list",
 237                           (void *) &mc_get_aspell_dict_info_list))
 238         goto error_ret;
 239 
 240     if (!g_module_symbol (spell_module, "delete_aspell_can_have_error",
 241                           (void *) &mc_delete_aspell_can_have_error))
 242         goto error_ret;
 243 
 244     if (!g_module_symbol (spell_module, "delete_aspell_dict_info_enumeration",
 245                           (void *) &mc_delete_aspell_dict_info_enumeration))
 246         goto error_ret;
 247 
 248     if (!g_module_symbol
 249         (spell_module, "aspell_config_retrieve", (void *) &mc_aspell_config_retrieve))
 250         goto error_ret;
 251 
 252     if (!g_module_symbol
 253         (spell_module, "aspell_word_list_size", (void *) &mc_aspell_word_list_size))
 254         goto error_ret;
 255 
 256     if (!g_module_symbol (spell_module, "aspell_speller_add_to_personal",
 257                           (void *) &mc_aspell_speller_add_to_personal))
 258         goto error_ret;
 259 
 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;
 263 
 264     ret = TRUE;
 265 
 266   error_ret:
 267     if (!ret)
 268     {
 269         g_module_close (spell_module);
 270         spell_module = NULL;
 271     }
 272     return ret;
 273 }
 274 
 275 /* --------------------------------------------------------------------------------------------- */
 276 /*** public functions ****************************************************************************/
 277 /* --------------------------------------------------------------------------------------------- */
 278 /**
 279  * Initialization of Aspell support.
 280  */
 281 
 282 void
 283 aspell_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 284 {
 285     AspellCanHaveError *error = NULL;
 286 
 287     if (strcmp (spell_language, "NONE") == 0)
 288         return;
 289 
 290     if (global_speller != NULL)
 291         return;
 292 
 293     global_speller = g_try_malloc (sizeof (spell_t));
 294     if (global_speller == NULL)
 295         return;
 296 
 297     if (!spell_available ())
 298     {
 299         MC_PTR_FREE (global_speller);
 300         return;
 301     }
 302 
 303     global_speller->config = mc_new_aspell_config ();
 304     global_speller->speller = NULL;
 305 
 306     if (spell_language != NULL)
 307         mc_aspell_config_replace (global_speller->config, "lang", spell_language);
 308 
 309     error = mc_new_aspell_speller (global_speller->config);
 310 
 311     if (mc_aspell_error_number (error) == 0)
 312         global_speller->speller = mc_to_aspell_speller (error);
 313     else
 314     {
 315         edit_error_dialog (_("Error"), mc_aspell_error_message (error));
 316         mc_delete_aspell_can_have_error (error);
 317         aspell_clean ();
 318     }
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 /**
 323  * Deinitialization of Aspell support.
 324  */
 325 
 326 void
 327 aspell_clean (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     if (global_speller == NULL)
 330         return;
 331 
 332     if (global_speller->speller != NULL)
 333         mc_delete_aspell_speller (global_speller->speller);
 334 
 335     if (global_speller->config != NULL)
 336         mc_delete_aspell_config (global_speller->config);
 337 
 338     MC_PTR_FREE (global_speller);
 339 
 340     g_module_close (spell_module);
 341     spell_module = NULL;
 342 }
 343 
 344 /* --------------------------------------------------------------------------------------------- */
 345 /**
 346  * Get array of available languages.
 347  *
 348  * @param lang_list Array of languages. Must be cleared before use
 349  * @return language list length
 350  */
 351 
 352 unsigned int
 353 aspell_get_lang_list (GArray * lang_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 354 {
 355     AspellDictInfoList *dlist;
 356     AspellDictInfoEnumeration *elem;
 357     const AspellDictInfo *entry;
 358     unsigned int i = 0;
 359 
 360     if (spell_module == NULL)
 361         return 0;
 362 
 363     /* the returned pointer should _not_ need to be deleted */
 364     dlist = mc_get_aspell_dict_info_list (global_speller->config);
 365     elem = mc_aspell_dict_info_list_elements (dlist);
 366 
 367     while ((entry = mc_aspell_dict_info_enumeration_next (elem)) != NULL)
 368     {
 369         if (entry->name != NULL)
 370         {
 371             char *tmp;
 372 
 373             tmp = g_strdup (entry->name);
 374             g_array_append_val (lang_list, tmp);
 375             i++;
 376         }
 377     }
 378 
 379     mc_delete_aspell_dict_info_enumeration (elem);
 380 
 381     return i;
 382 }
 383 
 384 /* --------------------------------------------------------------------------------------------- */
 385 /**
 386  * Clear the array of languages.
 387  *
 388  * @param array Array of languages
 389  */
 390 
 391 void
 392 aspell_array_clean (GArray * array)
     /* [previous][next][first][last][top][bottom][index][help]  */
 393 {
 394     if (array != NULL)
 395     {
 396         guint i = 0;
 397 
 398         for (i = 0; i < array->len; ++i)
 399         {
 400             char *tmp;
 401 
 402             tmp = g_array_index (array, char *, i);
 403             g_free (tmp);
 404         }
 405         g_array_free (array, TRUE);
 406     }
 407 }
 408 
 409 /* --------------------------------------------------------------------------------------------- */
 410 /**
 411  * Get the current language name.
 412  *
 413  * @return language name
 414  */
 415 
 416 const char *
 417 aspell_get_lang (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 418 {
 419     const char *code;
 420 
 421     code = mc_aspell_config_retrieve (global_speller->config, "lang");
 422     return spell_decode_lang (code);
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */
 426 /**
 427  * Set the language.
 428  *
 429  * @param lang Language name
 430  * @return FALSE or error
 431  */
 432 
 433 gboolean
 434 aspell_set_lang (const char *lang)
     /* [previous][next][first][last][top][bottom][index][help]  */
 435 {
 436     if (lang != NULL)
 437     {
 438         AspellCanHaveError *error;
 439         const char *spell_codeset;
 440 
 441         g_free (spell_language);
 442         spell_language = g_strdup (lang);
 443 
 444 #ifdef HAVE_CHARSET
 445         if (mc_global.source_codepage > 0)
 446             spell_codeset = get_codepage_id (mc_global.source_codepage);
 447         else
 448 #endif
 449             spell_codeset = str_detect_termencoding ();
 450 
 451         mc_aspell_config_replace (global_speller->config, "lang", lang);
 452         mc_aspell_config_replace (global_speller->config, "encoding", spell_codeset);
 453 
 454         /* the returned pointer should _not_ need to be deleted */
 455         if (global_speller->speller != NULL)
 456             mc_delete_aspell_speller (global_speller->speller);
 457 
 458         global_speller->speller = NULL;
 459 
 460         error = mc_new_aspell_speller (global_speller->config);
 461         if (mc_aspell_error (error) != 0)
 462         {
 463             mc_delete_aspell_can_have_error (error);
 464             return FALSE;
 465         }
 466 
 467         global_speller->speller = mc_to_aspell_speller (error);
 468     }
 469     return TRUE;
 470 }
 471 
 472 /* --------------------------------------------------------------------------------------------- */
 473 /**
 474  * Check word.
 475  *
 476  * @param word Word for spell check
 477  * @param word_size Word size (in bytes)
 478  * @return FALSE if word is not in the dictionary
 479  */
 480 
 481 gboolean
 482 aspell_check (const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 483 {
 484     int res = 0;
 485 
 486     if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
 487         res = mc_aspell_speller_check (global_speller->speller, word, word_size);
 488 
 489     return (res == 1);
 490 }
 491 
 492 /* --------------------------------------------------------------------------------------------- */
 493 /**
 494  * Examine dictionaries and suggest possible words that may repalce the incorrect word.
 495  *
 496  * @param suggest array of words to iterate through
 497  * @param word Word for spell check
 498  * @param word_size Word size (in bytes)
 499  * @return count of suggests for the word
 500  */
 501 
 502 unsigned int
 503 aspell_suggest (GArray * suggest, const char *word, const int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 504 {
 505     unsigned int size = 0;
 506 
 507     if (word != NULL && global_speller != NULL && global_speller->speller != NULL)
 508     {
 509         const AspellWordList *wordlist;
 510 
 511         wordlist = mc_aspell_speller_suggest (global_speller->speller, word, word_size);
 512         if (wordlist != NULL)
 513         {
 514             AspellStringEnumeration *elements = NULL;
 515             unsigned int i;
 516 
 517             elements = mc_aspell_word_list_elements (wordlist);
 518             size = mc_aspell_word_list_size (wordlist);
 519 
 520             for (i = 0; i < size; i++)
 521             {
 522                 const char *cur_sugg_word;
 523 
 524                 cur_sugg_word = g_strdup (mc_aspell_string_enumeration_next (elements));
 525                 if (cur_sugg_word != NULL)
 526                     g_array_append_val (suggest, cur_sugg_word);
 527             }
 528 
 529             mc_delete_aspell_string_enumeration (elements);
 530         }
 531     }
 532 
 533     return size;
 534 }
 535 
 536 /* --------------------------------------------------------------------------------------------- */
 537 /*
 538  * Add word to personal dictionary.
 539  *
 540  * @param word Word for spell check
 541  * @param word_size  Word size (in bytes)
 542  * @return FALSE or error
 543  */
 544 gboolean
 545 aspell_add_to_dict (const char *word, int word_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 546 {
 547     mc_aspell_speller_add_to_personal (global_speller->speller, word, word_size);
 548 
 549     if (mc_aspell_speller_error (global_speller->speller) != 0)
 550     {
 551         edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
 552         return FALSE;
 553     }
 554 
 555     mc_aspell_speller_save_all_word_lists (global_speller->speller);
 556 
 557     if (mc_aspell_speller_error (global_speller->speller) != 0)
 558     {
 559         edit_error_dialog (_("Error"), mc_aspell_speller_error_message (global_speller->speller));
 560         return FALSE;
 561     }
 562 
 563     return TRUE;
 564 }
 565 
 566 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */