487 lines
13 KiB
C
487 lines
13 KiB
C
/*
|
|
* 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);
|
|
}
|