summaryrefslogtreecommitdiff
path: root/src/plugins/spell/spell-speller.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/spell/spell-speller.c')
-rw-r--r--src/plugins/spell/spell-speller.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/src/plugins/spell/spell-speller.c b/src/plugins/spell/spell-speller.c
new file mode 100644
index 000000000..0b0ac5492
--- /dev/null
+++ b/src/plugins/spell/spell-speller.c
@@ -0,0 +1,486 @@
+/*
+ * spell-speller.c - speller management for spell checker plugin
+ *
+ * Copyright (C) 2006 Emmanuel Bouthenot <kolter@openics.org>
+ * Copyright (C) 2006-2019 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../weechat-plugin.h"
+#include "spell.h"
+#include "spell-speller.h"
+#include "spell-config.h"
+
+
+/*
+ * spellers: one by dictionary (key is name of dictionary (eg: "fr"), value is
+ * pointer on AspellSpeller)
+ */
+struct t_hashtable *spell_spellers = NULL;
+
+/*
+ * spellers by buffer (key is buffer pointer, value is pointer on
+ * struct t_spell_speller_buffer)
+ */
+struct t_hashtable *spell_speller_buffer = NULL;
+
+
+/*
+ * Checks if a spelling dictionary is supported (installed on system).
+ *
+ * Returns:
+ * 1: spell dict is supported
+ * 0: spell dict is NOT supported
+ */
+
+int
+spell_speller_dict_supported (const char *lang)
+{
+#ifdef USE_ENCHANT
+ return enchant_broker_dict_exists (broker, lang);
+#else
+ struct AspellConfig *config;
+ AspellDictInfoList *list;
+ AspellDictInfoEnumeration *elements;
+ const AspellDictInfo *dict;
+ int rc;
+
+ rc = 0;
+
+ config = new_aspell_config ();
+ list = get_aspell_dict_info_list (config);
+ elements = aspell_dict_info_list_elements (list);
+
+ while ((dict = aspell_dict_info_enumeration_next (elements)) != NULL)
+ {
+ if (strcmp (dict->name, lang) == 0)
+ {
+ rc = 1;
+ break;
+ }
+ }
+
+ delete_aspell_dict_info_enumeration (elements);
+ delete_aspell_config (config);
+
+ return rc;
+#endif /* USE_ENCHANT */
+}
+
+/*
+ * Checks if dictionaries are valid (called when user creates/changes
+ * dictionaries for a buffer).
+ *
+ * An error is displayed for each invalid dictionary found.
+ */
+
+void
+spell_speller_check_dictionaries (const char *dict_list)
+{
+ char **argv;
+ int argc, i;
+
+ if (dict_list)
+ {
+ argv = weechat_string_split (dict_list, ",", 0, 0, &argc);
+ if (argv)
+ {
+ for (i = 0; i < argc; i++)
+ {
+ if (!spell_speller_dict_supported (argv[i]))
+ {
+ weechat_printf (NULL,
+ _("%s: warning: dictionary \"%s\" is not "
+ "available on your system"),
+ SPELL_PLUGIN_NAME, argv[i]);
+ }
+ }
+ weechat_string_free_split (argv);
+ }
+ }
+}
+
+/*
+ * Creates and adds a new speller instance in the hashtable.
+ *
+ * Returns pointer to new speller, NULL if error.
+ */
+
+#ifdef USE_ENCHANT
+EnchantDict *
+#else
+AspellSpeller *
+#endif /* USE_ENCHANT */
+spell_speller_new (const char *lang)
+{
+#ifdef USE_ENCHANT
+ EnchantDict *new_speller;
+#else
+ AspellConfig *config;
+ AspellCanHaveError *ret;
+ AspellSpeller *new_speller;
+#endif /* USE_ENCHANT */
+ struct t_infolist *infolist;
+
+ if (!lang)
+ return NULL;
+
+ if (weechat_spell_plugin->debug)
+ {
+ weechat_printf (NULL,
+ "%s: creating new speller for lang \"%s\"",
+ SPELL_PLUGIN_NAME, lang);
+ }
+
+#ifdef USE_ENCHANT
+ new_speller = enchant_broker_request_dict (broker, lang);
+ if (!new_speller)
+ {
+ weechat_printf (NULL,
+ _("%s%s: error: unable to create speller for lang \"%s\""),
+ weechat_prefix ("error"), SPELL_PLUGIN_NAME,
+ lang);
+ return NULL;
+ }
+#else
+ /* create a speller instance for the newly created cell */
+ config = new_aspell_config ();
+ aspell_config_replace (config, "lang", lang);
+#endif /* USE_ENCHANT */
+
+ /* apply all options */
+ infolist = weechat_infolist_get ("option", NULL, "spell.option.*");
+ if (infolist)
+ {
+ while (weechat_infolist_next (infolist))
+ {
+#ifdef USE_ENCHANT
+ /* TODO: set option with enchant */
+#else
+ aspell_config_replace (config,
+ weechat_infolist_string (infolist, "option_name"),
+ weechat_infolist_string (infolist, "value"));
+#endif /* USE_ENCHANT */
+ }
+ weechat_infolist_free (infolist);
+ }
+
+#ifndef USE_ENCHANT
+ ret = new_aspell_speller (config);
+
+ if (aspell_error (ret) != 0)
+ {
+ weechat_printf (NULL,
+ "%s%s: error: %s",
+ weechat_prefix ("error"), SPELL_PLUGIN_NAME,
+ aspell_error_message (ret));
+ delete_aspell_config (config);
+ delete_aspell_can_have_error (ret);
+ return NULL;
+ }
+
+ new_speller = to_aspell_speller (ret);
+#endif /* USE_ENCHANT */
+
+ weechat_hashtable_set (spell_spellers, lang, new_speller);
+
+#ifndef USE_ENCHANT
+ /* free configuration */
+ delete_aspell_config (config);
+#endif /* USE_ENCHANT */
+
+ return new_speller;
+}
+
+/*
+ * Creates hashtable entries with a string containing a list of dicts.
+ */
+
+void
+spell_speller_add_dicts_to_hash (struct t_hashtable *hashtable,
+ const char *dict)
+{
+ char **dicts;
+ int num_dicts, i;
+
+ if (!dict || !dict[0])
+ return;
+
+ dicts = weechat_string_split (dict, ",", 0, 0, &num_dicts);
+ if (dicts)
+ {
+ for (i = 0; i < num_dicts; i++)
+ {
+ weechat_hashtable_set (hashtable, dicts[i], NULL);
+ }
+ weechat_string_free_split (dicts);
+ }
+}
+
+/*
+ * Removes a speller if it is NOT in hashtable "used_spellers".
+ */
+
+void
+spell_speller_remove_unused_cb (void *data,
+ struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_hashtable *used_spellers;
+
+ /* make C compiler happy */
+ (void) value;
+
+ used_spellers = (struct t_hashtable *)data;
+
+ /* if speller is not in "used_spellers", remove it (not used any more) */
+ if (!weechat_hashtable_has_key (used_spellers, key))
+ weechat_hashtable_remove (hashtable, key);
+}
+
+/*
+ * Removes unused spellers from hashtable "spell_spellers".
+ */
+
+void
+spell_speller_remove_unused ()
+{
+ struct t_hashtable *used_spellers;
+ struct t_infolist *infolist;
+
+ if (weechat_spell_plugin->debug)
+ {
+ weechat_printf (NULL,
+ "%s: removing unused spellers",
+ SPELL_PLUGIN_NAME);
+ }
+
+ /* create a hashtable that will contain all used spellers */
+ used_spellers = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL, NULL);
+ if (!used_spellers)
+ return;
+
+ /* collect used spellers and store them in hashtable "used_spellers" */
+ spell_speller_add_dicts_to_hash (used_spellers,
+ weechat_config_string (spell_config_check_default_dict));
+ infolist = weechat_infolist_get ("option", NULL, "spell.dict.*");
+ if (infolist)
+ {
+ while (weechat_infolist_next (infolist))
+ {
+ spell_speller_add_dicts_to_hash (used_spellers,
+ weechat_infolist_string (infolist, "value"));
+ }
+ weechat_infolist_free (infolist);
+ }
+
+ /*
+ * look at current spellers, and remove spellers that are not in hashtable
+ * "used_spellers"
+ */
+ weechat_hashtable_map (spell_spellers,
+ &spell_speller_remove_unused_cb,
+ used_spellers);
+
+ weechat_hashtable_free (used_spellers);
+}
+
+/*
+ * Callback called when a key is removed in hashtable "spell_spellers".
+ */
+
+void
+spell_speller_free_value_cb (struct t_hashtable *hashtable,
+ const void *key, void *value)
+{
+#ifdef USE_ENCHANT
+ EnchantDict *ptr_speller;
+#else
+ AspellSpeller *ptr_speller;
+#endif /* USE_ENCHANT */
+
+ /* make C compiler happy */
+ (void) hashtable;
+
+ if (weechat_spell_plugin->debug)
+ {
+ weechat_printf (NULL,
+ "%s: removing speller for lang \"%s\"",
+ SPELL_PLUGIN_NAME, (const char *)key);
+ }
+
+ /* free speller */
+#ifdef USE_ENCHANT
+ ptr_speller = (EnchantDict *)value;
+ enchant_broker_free_dict (broker, ptr_speller);
+#else
+ ptr_speller = (AspellSpeller *)value;
+ aspell_speller_save_all_word_lists (ptr_speller);
+ delete_aspell_speller (ptr_speller);
+#endif /* USE_ENCHANT */
+}
+
+/*
+ * Creates a structure for buffer speller info in hashtable
+ * "spell_buffer_spellers".
+ */
+
+struct t_spell_speller_buffer *
+spell_speller_buffer_new (struct t_gui_buffer *buffer)
+{
+ const char *buffer_dicts;
+ char **dicts;
+ int num_dicts, i;
+ struct t_spell_speller_buffer *new_speller_buffer;
+#ifdef USE_ENCHANT
+ EnchantDict *ptr_speller;
+#else
+ AspellSpeller *ptr_speller;
+#endif /* USE_ENCHANT */
+
+ if (!buffer)
+ return NULL;
+
+ weechat_hashtable_remove (spell_speller_buffer, buffer);
+
+ new_speller_buffer = malloc (sizeof (*new_speller_buffer));
+ if (!new_speller_buffer)
+ return NULL;
+
+ new_speller_buffer->spellers = NULL;
+ new_speller_buffer->modifier_string = NULL;
+ new_speller_buffer->input_pos = -1;
+ new_speller_buffer->modifier_result = NULL;
+
+ buffer_dicts = spell_get_dict (buffer);
+ if (buffer_dicts)
+ {
+ dicts = weechat_string_split (buffer_dicts, ",", 0, 0, &num_dicts);
+ if (dicts && (num_dicts > 0))
+ {
+ new_speller_buffer->spellers =
+#ifdef USE_ENCHANT
+ malloc ((num_dicts + 1) * sizeof (EnchantDict *));
+#else
+ malloc ((num_dicts + 1) * sizeof (AspellSpeller *));
+#endif /* USE_ENCHANT */
+ if (new_speller_buffer->spellers)
+ {
+ for (i = 0; i < num_dicts; i++)
+ {
+ ptr_speller = weechat_hashtable_get (spell_spellers,
+ dicts[i]);
+ if (!ptr_speller)
+ ptr_speller = spell_speller_new (dicts[i]);
+ new_speller_buffer->spellers[i] = ptr_speller;
+ }
+ new_speller_buffer->spellers[num_dicts] = NULL;
+ }
+ }
+ if (dicts)
+ weechat_string_free_split (dicts);
+ }
+
+ weechat_hashtable_set (spell_speller_buffer,
+ buffer,
+ new_speller_buffer);
+
+ weechat_bar_item_update ("spell_dict");
+
+ return new_speller_buffer;
+}
+
+/*
+ * Callback called when a key is removed in hashtable
+ * "spell_speller_buffer".
+ */
+
+void
+spell_speller_buffer_free_value_cb (struct t_hashtable *hashtable,
+ const void *key, void *value)
+{
+ struct t_spell_speller_buffer *ptr_speller_buffer;
+
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) key;
+
+ ptr_speller_buffer = (struct t_spell_speller_buffer *)value;
+
+ if (ptr_speller_buffer->spellers)
+ free (ptr_speller_buffer->spellers);
+ if (ptr_speller_buffer->modifier_string)
+ free (ptr_speller_buffer->modifier_string);
+ if (ptr_speller_buffer->modifier_result)
+ free (ptr_speller_buffer->modifier_result);
+
+ free (ptr_speller_buffer);
+}
+
+/*
+ * Initializes spellers (creates hashtables).
+ *
+ * Returns:
+ * 1: OK (hashtables created)
+ * 0: error (not enough memory)
+ */
+
+int
+spell_speller_init ()
+{
+ spell_spellers = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL, NULL);
+ if (!spell_spellers)
+ return 0;
+ weechat_hashtable_set_pointer (spell_spellers,
+ "callback_free_value",
+ &spell_speller_free_value_cb);
+
+ spell_speller_buffer = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_POINTER,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL, NULL);
+ if (!spell_speller_buffer)
+ {
+ weechat_hashtable_free (spell_spellers);
+ return 0;
+ }
+ weechat_hashtable_set_pointer (spell_speller_buffer,
+ "callback_free_value",
+ &spell_speller_buffer_free_value_cb);
+
+ return 1;
+}
+
+/*
+ * Ends spellers (removes hashtables).
+ */
+
+void
+spell_speller_end ()
+{
+ weechat_hashtable_free (spell_spellers);
+ weechat_hashtable_free (spell_speller_buffer);
+}