From bfc1477ec7353e9bceaad554a16d359035c5c630 Mon Sep 17 00:00:00 2001 From: Reuben Thomas Date: Tue, 21 Feb 2017 15:25:35 +0000 Subject: Revert "Improve HACKING" --- .dir-locals.el | 4 - HACKING | 74 ++- configure.ac | 45 +- data/enchant.ordering | 10 +- doc/enchant.1 | 18 +- src/Makefile.am | 2 +- src/enchant.c | 2 +- src/hunspell/Makefile.am | 21 - src/hunspell/README | 4 - src/hunspell/hunspell_checker.cpp | 574 --------------------- src/hunspell/libenchant_hunspell.rc | 37 -- src/myspell/Makefile.am | 21 + src/myspell/README | 4 + src/myspell/libenchant_myspell.rc | 37 ++ src/myspell/myspell_checker.cpp | 574 +++++++++++++++++++++ tests/Makefile.am | 2 +- .../broker/enchant_broker_set_ordering_tests.cpp | 4 +- 17 files changed, 723 insertions(+), 710 deletions(-) delete mode 100644 .dir-locals.el delete mode 100644 src/hunspell/Makefile.am delete mode 100644 src/hunspell/README delete mode 100644 src/hunspell/hunspell_checker.cpp delete mode 100644 src/hunspell/libenchant_hunspell.rc create mode 100644 src/myspell/Makefile.am create mode 100644 src/myspell/README create mode 100644 src/myspell/libenchant_myspell.rc create mode 100644 src/myspell/myspell_checker.cpp diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index 127a4ad..0000000 --- a/.dir-locals.el +++ /dev/null @@ -1,4 +0,0 @@ -((c-mode . ((c-file-style . "K&R") - (c-basic-offset . 8) - (tab-width . 8) - (indent-tabs-mode . t)))) diff --git a/HACKING b/HACKING index 28c85a2..db307e4 100644 --- a/HACKING +++ b/HACKING @@ -18,58 +18,80 @@ Working in libenchant 2) Maintainable & Documented 3) Modular and well designed - When you submit code for inclusion in libenchant, please keep those -things in mind. While performance is important please note that -well-designed algorithms and data structures are fertile areas for -development; obfuscated code to make a loop 3% faster is not. Specifically, -this means: + When you submit code to inclusion in libenchant, or when you modify the sources +directly on the CVS repository, please keep those things in mind. While +performance is important please note that we do not want to hand tune code +to shave milliseconds at this point. Well designed algorithms and data +structures are fertile areas for development, obfuscated code to make a +loop 3% faster is not. Specifically, this means: - Clarity of design and function are paramount - - Make sure your code does not generate warnings + - Make sure your code does not generate warnings at all. - Please follow the formatting style Formatting style ---------------- - The formatting style of libenchant is a mix of various styles. Please -familiarise yourself with the GNU coding standards (shipped with most + The formatting style of libenchant is a mix of various styles, make +yourself familiar with the GNU coding standards (shipped with most GNU/Linux systems as the standards.info file), then read the Linux -kernel coding standards and ignore Linus's jokes. Then look at the -Gtk+ header files to get acquainted with how to write nice header files -that are almost self documenting. +kernel coding standards and ignore Linus' jokes. Then look at the +Gtk+ header files to get acquainted on how to write nice header files +that are almost self documenting. - Emacs users should use the supplied .dir-locals.el. - - Remember: Use tabs for indentation: that will keep your -code honest as you will be forced to split your routines into more + Remember: Use 8 space tabs for indentation: that will keep your +code honest as you will be forced to split your routines in more modular chunks (as detailed by Linus). - On top of that, please: + Emacs users can get the default indentation style with this: + (set-c-style "K&R") + (setq c-basic-offset 8) + + On top of that, you will have to: - Follow the Gtk+ cleanliness conventions for function prototypes. - - Follow the Gtk+ namespace convention for function names: + - Follow the Gtk+ namespace convention for function names. module_submodule_operation + - Make sure your code does not have a single warning (with the + default strong warnings that Gnumeric compiles with) before + your code is submitted. (Although we do not advocate -Werror) + - Every entry point to a public routine should use the g_return_if_fail and g_return_val_if_fail macros to verify that the parameters passed are valid. - Under no circumstances use magic variables. Use typedef enum { ... } type; to create enumerations. Do not use - integers to hold enumeration values: the compiler can help - catch various errors. + integers to hold references to enumerations, the compiler + can help catch various errors. - Use g_warning to mark spots that need to be reviewed or are - not finished. + not finished to let me fix it eventually. - Do not submit code that is just a temporary workaround for a - full fledged feature. We do not want to maintain limited - features. It is better to submit an implementation designed - to be expanded and enhanced, even if it is not completely - finished. + full fledged feature. i.e. don't submit a quick hack at + "search text" which is not designed to be expanded upon. We + do not want to maintain limited features. It is better submit an + implementation that has been designed to be expanded and enhanced, + even if it is not completely finished. + + - It is more important to be correct than to be fast. + + - Do not optimize unnecessarily. Do profile, do look for the + weak spots before applying "optimization by feeling". This + is not a Ouija-based project. + + - It is more important to keep the code maintainable and + readable than to be fast. If you have a great idea about + optimizing the code, make sure it is implemented cleanly, + that there is a clean and maintainable way to do it: + + - Fast code that is difficult to maintain has no place in + Enchant and will be dropped. - Follow the libenchant commenting style, which is not the Gtk style; @@ -82,8 +104,8 @@ reasonable margins of maintainability for the future: Remember, in two years you will probably be far too busy to maintain your own contributions, and they might become a burden to the program maintainers. - libenchant is intended to be a foundation for a various document-centric + libenchant is intended to be a foundation for a various document centric projects. Cleaning code in libenchant is more important than trying not to break -existing code. Code clean-ups are always welcome. +existing code. Code clean ups are always welcome. diff --git a/configure.ac b/configure.ac index 94df41c..3d92a6d 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,8 @@ ENCHANT_LT_OBJDIR=$lt_cv_objdir AC_SUBST(ENCHANT_LT_OBJDIR) +PKG_PROG_PKG_CONFIG + PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.6 gmodule-2.0]) dnl Extra warnings with GCC and compatible compilers @@ -261,29 +263,22 @@ AC_SUBST(ISPELL_CFLAGS) dnl Hunspell -AC_ARG_WITH([hunspell], - AS_HELP_STRING([--with-hunspell], - [enable the hunspell backend @<:@default=check@:>@]), - [], - [with_hunspell=check]) - -hunspell_dir=${datadir}/hunspell -AC_ARG_WITH([hunspell-dir], - AS_HELP_STRING([--with-hunspell-dir=PATH], - [path to installed Hunspell dicts])) -AS_IF([test "x$with_hunspell_dir" != "x"], - [hunspell_dir=$with_hunspell_dir]) - -AS_IF([test "x$with_hunspell" != xno], - [PKG_CHECK_MODULES([HUNSPELL], [hunspell], - [HUNSPELL_CFLAGS="$HUNSPELL_CFLAGS -DENCHANT_HUNSPELL_DICT_DIR='\"$hunspell_dir\"'" - with_hunspell=yes], - [if test "x$with_hunspell" != xcheck; then - AC_MSG_FAILURE([--with-hunspell was given, but test for Hunspell failed]) - fi - with_hunspell=no])]) +AC_ARG_ENABLE(myspell, AS_HELP_STRING([--disable-myspell],[enable the myspell backend @<:@default=auto@:>@]), build_myspell="$enableval", build_myspell=yes) + +AM_CONDITIONAL(WITH_MYSPELL, test "x$build_myspell" = "xyes") -AM_CONDITIONAL(WITH_HUNSPELL, test "x$with_hunspell" = xyes) +myspell_dir=${datadir}/myspell/dicts +AC_ARG_WITH(myspell-dir, AS_HELP_STRING([--with-myspell-dir=PATH],[path to installed myspell dicts])) +if test "x$with_myspell_dir" != "x" ; then + myspell_dir=$with_myspell_dir +fi + +if test "x$build_myspell" = xyes; then + PKG_CHECK_MODULES(MYSPELL, [hunspell]) + AC_SUBST(MYSPELL_CFLAGS) + AC_SUBST(MYSPELL_LIBS) + MYSPELL_CFLAGS="$MYSPELL_CFLAGS -DENCHANT_MYSPELL_DICT_DIR='\"$myspell_dir\"'" +fi dnl Aspell @@ -466,7 +461,7 @@ src/Makefile src/aspell/Makefile src/ispell/Makefile src/uspell/Makefile -src/hunspell/Makefile +src/myspell/Makefile src/hspell/Makefile src/applespell/Makefile src/voikko/Makefile @@ -493,8 +488,8 @@ $PACKAGE-$VERSION Build Ispell backend: ${build_ispell} Build Uspell backend: ${build_uspell} Build Hspell backend: ${build_hspell} - Build Hunspell backend: ${with_hunspell} - Build Voikko backend: ${build_voikko} + Build Myspell/Hunspell backend: ${build_myspell} + Build Voikko backend (Linux only): ${build_voikko} Build Zemberek backend: ${build_zemberek} Build Apple Spell backend (OS X only): ${build_applespell}" diff --git a/data/enchant.ordering b/data/enchant.ordering index 012de34..8e380fb 100644 --- a/data/enchant.ordering +++ b/data/enchant.ordering @@ -1,8 +1,8 @@ -*:hunspell,aspell,ispell -fi:voikko,ispell,hunspell,aspell -fi_FI:voikko,ispell,hunspell,aspell -he:hspell,hunspell -he_IL:hspell,hunspell +*:myspell,aspell,ispell +fi:voikko,ispell,myspell,aspell +fi_FI:voikko,ispell,myspell,aspell +he:hspell,myspell +he_IL:hspell,myspell yi:uspell tr:zemberek tr_TR:zemberek diff --git a/doc/enchant.1 b/doc/enchant.1 index 2336cc1..4c557fb 100644 --- a/doc/enchant.1 +++ b/doc/enchant.1 @@ -38,19 +38,19 @@ located in \fI~/.enchant\fR. The per-user file takes precedence, if found. .PP The ordering file takes the form language_tag:. I am currently aware of the following backends: aspell, -hunspell, ispell, uspell, hspell, voikko, zemberek, and Apple Spell (macOS only). '*' is +backends>. I am currently aware of the following backends: aspell/pspell, +myspell/hunspell, ispell, uspell, hspell, voikko, zemberek, and Apple Spell (macOS only). '*' is used to mean "use this ordering for all languages, unless instructed otherwise." For example: .PP -*:aspell,hunspell,ispell +*:aspell,myspell,ispell .br -en:aspell,hunspell,ispell +en:aspell,myspell,ispell .br -en_GB:hunspell,aspell,ispell +en_GB:myspell,aspell,ispell .br -fr:hunspell,ispell:aspell +fr:myspell,ispell:aspell .SH DIRECTORIES IMPORTANT TO ENCHANT -Unless configured otherwise, Enchant's Hunspell, Ispell, and Uspell +Unless configured otherwise, Enchant's Myspell, Ispell, and Uspell backends will look for dictionaries in directories specific to Enchant, and will not use your system-wide installed dictionaries. This is for pragmatic reasons since many distributions install these dictionaries @@ -59,11 +59,11 @@ into different locations. Like the \fIenchant.ordering\fR file described above, Enchant looks in the global directory for these dictionaries and a per-user directory. The per-user directory takes precedence if it is found. -Enchant looks for Hunspell dictionaries in \fI$(datadir)/enchant/hunspell\fR and \fI~/.enchant/hunspell\fR. +Enchant looks for Myspell dictionaries in \fI$(datadir)/enchant/myspell\fR and \fI~/.enchant/myspell\fR. Enchant looks for Ispell dictionaries in \fI$(datadir)/enchant/ispell\fR and \fI~/.enchant/ispell\fR. Enchant looks for Uspell dictionaries in \fI$(datadir)/enchant/uspell\fR and \fI~/.enchant/uspell\fR. .PP -Packagers and users may wish to make symbolic links to the system\-wide dictionary directories. Or, preferably, use the \-\-with-hunspell-dir, \-\-with\-ispell\-dir, and \-\-with\-uspell\-dir 'configure' arguments. +Packagers and users may wish to make symbolic links to the system\-wide dictionary directories. Or, preferably, use the \-\-with-myspell-dir, \-\-with\-ispell\-dir, and \-\-with\-uspell\-dir 'configure' arguments. .SH MORE INFORMATION http://www.abisource.com/enchant/ .SH "SEE ALSO" diff --git a/src/Makefile.am b/src/Makefile.am index 73f9d3b..0d2f32b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=. aspell ispell uspell hunspell hspell applespell voikko zemberek +SUBDIRS=. aspell ispell uspell myspell hspell applespell voikko zemberek AM_CPPFLAGS = -I$(top_srcdir) $(ISYSTEM)$(top_builddir)/lib $(ISYSTEM)$(top_srcdir)/lib $(ENCHANT_CFLAGS) $(WARN_CFLAGS) diff --git a/src/enchant.c b/src/enchant.c index fe478d8..98756bc 100644 --- a/src/enchant.c +++ b/src/enchant.c @@ -1905,7 +1905,7 @@ enchant_broker_dict_exists (EnchantBroker * broker, * enchant_broker_set_ordering * @broker: A non-null #EnchantBroker * @tag: A non-null language tag (en_US) - * @ordering: A non-null ordering (aspell,hunspell,ispell,uspell,hspell) + * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell) * * Declares a preference of dictionaries to use for the language * described/referred to by @tag. The ordering is a comma delimited diff --git a/src/hunspell/Makefile.am b/src/hunspell/Makefile.am deleted file mode 100644 index b31de26..0000000 --- a/src/hunspell/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -if WITH_HUNSPELL -target_lib = libenchant_hunspell.la -else -target_lib = -endif - -AM_CPPFLAGS = -I$(top_srcdir) $(ISYSTEM)$(top_builddir)/lib $(ISYSTEM)$(top_srcdir)/lib -I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(HUNSPELL_CFLAGS) $(WARN_CXXFLAGS) -D_ENCHANT_BUILD=1 - -hunspell_LTLIBRARIES = $(target_lib) -hunspelldir= $(libdir)/enchant - -libenchant_hunspell_lalibdir=$(libdir)/enchant -libenchant_hunspell_la_LIBADD= $(HUNSPELL_LIBS) $(ENCHANT_LIBS) $(top_builddir)/src/libenchant.la -libenchant_hunspell_la_LDFLAGS = -module -avoid-version -no-undefined - -libenchant_hunspell_la_SOURCES = \ - hunspell_checker.cpp - -EXTRA_DIST= \ - libenchant_hunspell.rc - diff --git a/src/hunspell/README b/src/hunspell/README deleted file mode 100644 index 6b587c9..0000000 --- a/src/hunspell/README +++ /dev/null @@ -1,4 +0,0 @@ -This is the Myspell/Hunspell Enchant Backend. - -Hunspell is by László Németh. You can find out more information -about Hunspell at http://hunspell.github.io/ diff --git a/src/hunspell/hunspell_checker.cpp b/src/hunspell/hunspell_checker.cpp deleted file mode 100644 index 145324e..0000000 --- a/src/hunspell/hunspell_checker.cpp +++ /dev/null @@ -1,574 +0,0 @@ -/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* enchant - * Copyright (C) 2003-2004 Joan Moratinos , Dom Lachowicz - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02110-1301, USA. - * - * In addition, as a special exception, Dom Lachowicz - * gives permission to link the code of this program with - * non-LGPL Spelling Provider libraries (eg: a MSFT Office - * spell checker backend) and distribute linked combinations including - * the two. You must obey the GNU General Public License in all - * respects for all of the code used other than said providers. If you modify - * this file, you may extend this exception to your version of the - * file, but you are not obligated to do so. If you do not wish to - * do so, delete this exception statement from your version. - */ - -#include "config.h" - -#include -#include -#include - -#include -#include - -#include "enchant.h" -#include "enchant-provider.h" -#include "unused-parameter.h" - -#include - -/* Some versions of hunspell (1.4.x) don't have this defined. */ -/* This is the defined value at that point */ -#ifndef MAXWORDLEN -#define MAXWORDLEN 176 -#endif - -ENCHANT_PLUGIN_DECLARE("Hunspell") - -#define G_ICONV_INVALID (GIConv)-1 - -#include - -/***************************************************************************/ - -class HunspellChecker -{ -public: - HunspellChecker(EnchantBroker * broker); - ~HunspellChecker(); - - bool checkWord (const char *word, size_t len); - char **suggestWord (const char* const word, size_t len, size_t *out_n_suggs); - - bool requestDictionary (const char * szLang); - -private: - GIConv m_translate_in; /* Selected translation from/to Unicode */ - GIConv m_translate_out; - Hunspell *hunspell; - EnchantBroker *m_broker; -}; - -static void -hunspell_checker_free_helper (gpointer p, gpointer user _GL_UNUSED_PARAMETER) -{ - g_free (p); -} - -/***************************************************************************/ - -static bool -g_iconv_is_valid(GIConv i) -{ - return (i != G_ICONV_INVALID); -} - -HunspellChecker::HunspellChecker(EnchantBroker * broker) -: m_translate_in(G_ICONV_INVALID), m_translate_out(G_ICONV_INVALID), hunspell(0), m_broker(broker) -{ -} - -HunspellChecker::~HunspellChecker() -{ - delete hunspell; - if (g_iconv_is_valid (m_translate_in )) - g_iconv_close(m_translate_in); - if (g_iconv_is_valid(m_translate_out)) - g_iconv_close(m_translate_out); -} - -bool -HunspellChecker::checkWord(const char *utf8Word, size_t len) -{ - if (len > MAXWORDLEN || !g_iconv_is_valid(m_translate_in)) - return false; - - // the 8bit encodings use precomposed forms - char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); - char *in = normalizedWord; - char word8[MAXWORDLEN + 1]; - char *out = word8; - size_t len_in = strlen(in); - size_t len_out = sizeof( word8 ) - 1; - size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); - g_free(normalizedWord); - if (static_cast(-1) == result) - return false; - *out = '\0'; - if (hunspell->spell(word8)) - return true; - else - return false; -} - -char** -HunspellChecker::suggestWord(const char* const utf8Word, size_t len, size_t *nsug) -{ - if (len > MAXWORDLEN - || !g_iconv_is_valid(m_translate_in) - || !g_iconv_is_valid(m_translate_out)) - return nullptr; - - // the 8bit encodings use precomposed forms - char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); - char *in = normalizedWord; - char word8[MAXWORDLEN + 1]; - char *out = word8; - size_t len_in = strlen(in); - size_t len_out = sizeof(word8) - 1; - size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); - g_free(normalizedWord); - if (static_cast(-1) == result) - return nullptr; - - *out = '\0'; - char **sugMS; - *nsug = hunspell->suggest(&sugMS, word8); - if (*nsug > 0) { - char **sug = g_new0 (char *, *nsug + 1); - for (size_t i=0; i<*nsug; i++) { - in = sugMS[i]; - len_in = strlen(in); - len_out = MAXWORDLEN; - char *word = g_new0(char, len_out + 1); - out = word; - if (static_cast(-1) == g_iconv(m_translate_out, &in, &len_in, &out, &len_out)) { - for (size_t j = i; j < *nsug; j++) - free(sugMS[j]); - free(sugMS); - - *nsug = i; - return sug; - } - *(out) = 0; - sug[i] = word; - free(sugMS[i]); - } - free(sugMS); - return sug; - } - else - return nullptr; -} - -static GSList * -hunspell_checker_get_dictionary_dirs (EnchantBroker * broker) -{ - GSList *dirs = NULL; - - { - GSList *config_dirs, *iter; - - config_dirs = enchant_get_user_config_dirs (); - - for (iter = config_dirs; iter; iter = iter->next) - { - dirs = g_slist_append (dirs, g_build_filename (static_cast(iter->data), - "hunspell", nullptr)); - } - - g_slist_foreach (config_dirs, hunspell_checker_free_helper, nullptr); - g_slist_free (config_dirs); - } - - { - const gchar* const * system_data_dirs = g_get_system_data_dirs (); - const gchar* const * iter; - - for (iter = system_data_dirs; *iter; iter++) - { - dirs = g_slist_append (dirs, g_build_filename (*iter, "hunspell", "dicts", nullptr)); - } - } - - /* Dynamically locate library and search for modules relative to it. */ - char * enchant_prefix = enchant_get_prefix_dir(); - if(enchant_prefix) - { - char * hunspell_prefix = g_build_filename(enchant_prefix, "share", "enchant", "hunspell", nullptr); - g_free(enchant_prefix); - dirs = g_slist_append (dirs, hunspell_prefix); - } - -#ifdef ENCHANT_HUNSPELL_DICT_DIR - dirs = g_slist_append (dirs, g_strdup (ENCHANT_HUNSPELL_DICT_DIR)); -#endif - - { - GSList *config_dirs, *iter; - - config_dirs = enchant_get_dirs_from_param (broker, "enchant.hunspell.dictionary.path"); - - for (iter = config_dirs; iter; iter = iter->next) - { - dirs = g_slist_append (dirs, g_strdup (static_cast(iter->data))); - } - - g_slist_foreach (config_dirs, hunspell_checker_free_helper, nullptr); - g_slist_free (config_dirs); - } - - { - const gchar* hun_dir = g_getenv("DICPATH"); - if (hun_dir) - { - dirs = g_slist_append(dirs, g_strdup(hun_dir)); - } - } - - return dirs; -} - -static void -s_buildDictionaryDirs (std::vector & dirs, EnchantBroker * broker) -{ - GSList *hunspell_dirs, *iter; - - dirs.clear (); - - hunspell_dirs = hunspell_checker_get_dictionary_dirs (broker); - for (iter = hunspell_dirs; iter; iter = iter->next) - { - dirs.push_back (static_cast(iter->data)); - } - - g_slist_foreach (hunspell_dirs, hunspell_checker_free_helper, nullptr); - g_slist_free (hunspell_dirs); -} - -static void -s_buildHashNames (std::vector & names, EnchantBroker * broker, const char * dict) -{ - names.clear (); - - std::vector dirs; - s_buildDictionaryDirs (dirs, broker); - - char *dict_dic = g_strconcat(dict, ".dic", nullptr); - for (size_t i = 0; i < dirs.size(); i++) - { - char *tmp = g_build_filename (dirs[i].c_str(), dict_dic, nullptr); - names.push_back (tmp); - g_free (tmp); - } - - g_free(dict_dic); -} - -static bool -s_hasCorrespondingAffFile(const std::string & dicFile) -{ - std::string aff = dicFile; - aff.replace(aff.end()-3,aff.end(), "aff"); - return g_file_test(aff.c_str(), G_FILE_TEST_EXISTS) != 0; -} - -static bool is_plausible_dict_for_tag(const char *dir_entry, const char *tag) -{ - const char *dic_suffix = ".dic"; - size_t dic_suffix_len = strlen(dic_suffix); - size_t dir_entry_len = strlen(dir_entry); - size_t tag_len = strlen(tag); - - if (dir_entry_len - dic_suffix_len < tag_len) - return false; - if (strcmp(dir_entry+dir_entry_len-dic_suffix_len, dic_suffix) != 0) - return false; - if (strncmp (dir_entry, tag, tag_len) != 0) - return false; - //e.g. requested dict for "fi", - //reject "fil_PH.dic" - //allow "fi-FOO.dic", "fi_FOO.dic", "fi.dic", etc. - if (!ispunct(dir_entry[tag_len])) - return false; - return true; -} - -static char * -hunspell_request_dictionary (EnchantBroker * broker, const char * tag) -{ - std::vector names; - - s_buildHashNames (names, broker, tag); - - for (size_t i = 0; i < names.size (); i++) { - if (g_file_test(names[i].c_str(), G_FILE_TEST_EXISTS)) { - if(s_hasCorrespondingAffFile(names[i])){ - return g_strdup (names[i].c_str()); - } - } - } - - std::vector dirs; - s_buildDictionaryDirs (dirs, broker); - - for (size_t i = 0; i < dirs.size(); i++) { - GDir *dir = g_dir_open (dirs[i].c_str(), 0, nullptr); - if (dir) { - const char *dir_entry; - while ((dir_entry = g_dir_read_name (dir)) != NULL) { - if (is_plausible_dict_for_tag(dir_entry, tag)) { - char *dict = g_build_filename (dirs[i].c_str(), - dir_entry, nullptr); - if(s_hasCorrespondingAffFile(dict)){ - g_dir_close (dir); - return dict; - } - } - } - - g_dir_close (dir); - } - } - - return NULL; -} - -bool -HunspellChecker::requestDictionary(const char *szLang) -{ - char *dic = NULL, *aff = NULL; - - dic = hunspell_request_dictionary (m_broker, szLang); - if (!dic) - return false; - - aff = g_strdup(dic); - int len_dic = strlen(dic); - strcpy(aff+len_dic-3, "aff"); - if (g_file_test(aff, G_FILE_TEST_EXISTS)) - { - hunspell = new Hunspell(aff, dic); - } - g_free(dic); - g_free(aff); - if(hunspell == NULL){ - return false; - } - char *enc = hunspell->get_dic_encoding(); - - m_translate_in = g_iconv_open(enc, "UTF-8"); - m_translate_out = g_iconv_open("UTF-8", enc); - - return true; -} - -/* - * Enchant - */ - -static char ** -hunspell_dict_suggest (EnchantDict * me, const char *const word, - size_t len, size_t * out_n_suggs) -{ - HunspellChecker * checker; - - checker = static_cast(me->user_data); - return checker->suggestWord (word, len, out_n_suggs); -} - -static int -hunspell_dict_check (EnchantDict * me, const char *const word, size_t len) -{ - HunspellChecker * checker; - - checker = static_cast(me->user_data); - - if (checker->checkWord(word, len)) - return 0; - - return 1; -} - -static void -hunspell_provider_enum_dicts (const char * const directory, - std::vector & out_dicts) -{ - GDir * dir = g_dir_open (directory, 0, nullptr); - if (dir) { - const char * entry; - - while ((entry = g_dir_read_name (dir)) != NULL) { - char * utf8_entry = g_filename_to_utf8 (entry, -1, nullptr, nullptr, nullptr); - if (utf8_entry) { - std::string dir_entry (utf8_entry); - g_free (utf8_entry); - - int hit = dir_entry.rfind (".dic"); - if (hit != -1) { - /* don't include hyphenation dictionaries - and require .aff file to be present*/ - if(dir_entry.compare (0, 5, "hyph_") != 0) - { - std::string name(dir_entry.substr (0, hit)); - std::string affFileName(name + ".aff"); - char * aff = g_build_filename(directory, affFileName.c_str(), nullptr); - if (g_file_test(aff, G_FILE_TEST_EXISTS)) - { - out_dicts.push_back (dir_entry.substr (0, hit)); - } - g_free(aff); - } - } - } - } - - g_dir_close (dir); - } -} - -extern "C" { - -ENCHANT_MODULE_EXPORT (EnchantProvider *) - init_enchant_provider (void); - -static char ** -hunspell_provider_list_dicts (EnchantProvider * me, - size_t * out_n_dicts) -{ - std::vector dict_dirs, dicts; - char ** dictionary_list = NULL; - - s_buildDictionaryDirs (dict_dirs, me->owner); - - for (size_t i = 0; i < dict_dirs.size(); i++) - { - hunspell_provider_enum_dicts (dict_dirs[i].c_str(), dicts); - } - - if (dicts.size () > 0) { - dictionary_list = g_new0 (char *, dicts.size() + 1); - - for (size_t i = 0; i < dicts.size(); i++) - dictionary_list[i] = g_strdup (dicts[i].c_str()); - } - - *out_n_dicts = dicts.size (); - return dictionary_list; -} - -static void -hunspell_provider_free_string_list (EnchantProvider * me _GL_UNUSED_PARAMETER, char **str_list) -{ - g_strfreev (str_list); -} - -static EnchantDict * -hunspell_provider_request_dict(EnchantProvider * me, const char *const tag) -{ - EnchantDict *dict; - HunspellChecker * checker; - - checker = new HunspellChecker(me->owner); - - if (!checker) - return NULL; - - if (!checker->requestDictionary(tag)) { - delete checker; - return NULL; - } - - dict = g_new0(EnchantDict, 1); - dict->user_data = (void *) checker; - dict->check = hunspell_dict_check; - dict->suggest = hunspell_dict_suggest; - // don't implement personal, session - - return dict; -} - -static void -hunspell_provider_dispose_dict (EnchantProvider * me _GL_UNUSED_PARAMETER, EnchantDict * dict) -{ - HunspellChecker *checker; - - checker = (HunspellChecker *) dict->user_data; - delete checker; - - g_free (dict); -} - -static int -hunspell_provider_dictionary_exists (struct str_enchant_provider * me, - const char *const tag) -{ - std::vector names; - - s_buildHashNames (names, me->owner, tag); - for (size_t i = 0; i < names.size(); i++) { - if (g_file_test (names[i].c_str(), G_FILE_TEST_EXISTS)) - { - std::string aff(names[i]); - aff.replace(aff.end() - 3, aff.end(), "aff"); - if (g_file_test(aff.c_str(), G_FILE_TEST_EXISTS)) - return 1; - } - } - - return 0; -} - -static void -hunspell_provider_dispose (EnchantProvider * me) -{ - g_free (me); -} - -static const char * -hunspell_provider_identify (EnchantProvider * me _GL_UNUSED_PARAMETER) -{ - return "hunspell"; -} - -static const char * -hunspell_provider_describe (EnchantProvider * me _GL_UNUSED_PARAMETER) -{ - return "Hunspell Provider"; -} - -EnchantProvider * -init_enchant_provider (void) -{ - EnchantProvider *provider; - - provider = g_new0(EnchantProvider, 1); - provider->dispose = hunspell_provider_dispose; - provider->request_dict = hunspell_provider_request_dict; - provider->dispose_dict = hunspell_provider_dispose_dict; - provider->dictionary_exists = hunspell_provider_dictionary_exists; - provider->identify = hunspell_provider_identify; - provider->describe = hunspell_provider_describe; - provider->free_string_list = hunspell_provider_free_string_list; - provider->list_dicts = hunspell_provider_list_dicts; - - return provider; -} - -} // extern C linkage diff --git a/src/hunspell/libenchant_hunspell.rc b/src/hunspell/libenchant_hunspell.rc deleted file mode 100644 index d7178bc..0000000 --- a/src/hunspell/libenchant_hunspell.rc +++ /dev/null @@ -1,37 +0,0 @@ -//LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -LANGUAGE 0, 0 - -10 VERSIONINFO - FILEVERSION 1,0,0,1 - PRODUCTVERSION 1,0,0,1 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "\0" - VALUE "FileDescription", "Libenchant Hunspell module\0" - VALUE "FileVersion", "1, 7, 0, 1\0" - VALUE "InternalName", "libenchant_hunspell\0" - VALUE "LegalCopyright", "Copyright \xA9 2003-2011 Joan Moratinos, Dom Lachowicz et al.\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "libenchant_hunspell.dll\0" - VALUE "PrivateBuild", "Dev version\0" - VALUE "ProductName", "libenchant\0" - VALUE "ProductVersion", "1, 7, 0\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/src/myspell/Makefile.am b/src/myspell/Makefile.am new file mode 100644 index 0000000..cfea67b --- /dev/null +++ b/src/myspell/Makefile.am @@ -0,0 +1,21 @@ +if WITH_MYSPELL +target_lib = libenchant_myspell.la +else +target_lib = +endif + +AM_CPPFLAGS = -I$(top_srcdir) $(ISYSTEM)$(top_builddir)/lib $(ISYSTEM)$(top_srcdir)/lib -I$(top_srcdir)/src $(ENCHANT_CFLAGS) $(MYSPELL_CFLAGS) $(WARN_CXXFLAGS) -D_ENCHANT_BUILD=1 + +myspell_LTLIBRARIES = $(target_lib) +myspelldir= $(libdir)/enchant + +libenchant_myspell_lalibdir=$(libdir)/enchant +libenchant_myspell_la_LIBADD= $(MYSPELL_LIBS) $(ENCHANT_LIBS) $(top_builddir)/src/libenchant.la +libenchant_myspell_la_LDFLAGS = -module -avoid-version -no-undefined + +libenchant_myspell_la_SOURCES = \ + myspell_checker.cpp + +EXTRA_DIST= \ + libenchant_myspell.rc + diff --git a/src/myspell/README b/src/myspell/README new file mode 100644 index 0000000..6b587c9 --- /dev/null +++ b/src/myspell/README @@ -0,0 +1,4 @@ +This is the Myspell/Hunspell Enchant Backend. + +Hunspell is by László Németh. You can find out more information +about Hunspell at http://hunspell.github.io/ diff --git a/src/myspell/libenchant_myspell.rc b/src/myspell/libenchant_myspell.rc new file mode 100644 index 0000000..75384da --- /dev/null +++ b/src/myspell/libenchant_myspell.rc @@ -0,0 +1,37 @@ +//LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +LANGUAGE 0, 0 + +10 VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "Libenchant Myspell module\0" + VALUE "FileVersion", "1, 7, 0, 1\0" + VALUE "InternalName", "libenchant_myspell\0" + VALUE "LegalCopyright", "Copyright \xA9 2003-2011 Joan Moratinos, Dom Lachowicz et al.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libenchant_myspell.dll\0" + VALUE "PrivateBuild", "Dev version\0" + VALUE "ProductName", "libenchant\0" + VALUE "ProductVersion", "1, 7, 0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/myspell/myspell_checker.cpp b/src/myspell/myspell_checker.cpp new file mode 100644 index 0000000..8f4f128 --- /dev/null +++ b/src/myspell/myspell_checker.cpp @@ -0,0 +1,574 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* enchant + * Copyright (C) 2003-2004 Joan Moratinos , Dom Lachowicz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, Dom Lachowicz + * gives permission to link the code of this program with + * non-LGPL Spelling Provider libraries (eg: a MSFT Office + * spell checker backend) and distribute linked combinations including + * the two. You must obey the GNU General Public License in all + * respects for all of the code used other than said providers. If you modify + * this file, you may extend this exception to your version of the + * file, but you are not obligated to do so. If you do not wish to + * do so, delete this exception statement from your version. + */ + +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "enchant.h" +#include "enchant-provider.h" +#include "unused-parameter.h" + +#include + +/* Some versions of hunspell (1.4.x) don't have this defined. */ +/* This is the defined value at that point */ +#ifndef MAXWORDLEN +#define MAXWORDLEN 176 +#endif + +ENCHANT_PLUGIN_DECLARE("Myspell") + +#define G_ICONV_INVALID (GIConv)-1 + +#include + +/***************************************************************************/ + +class MySpellChecker +{ +public: + MySpellChecker(EnchantBroker * broker); + ~MySpellChecker(); + + bool checkWord (const char *word, size_t len); + char **suggestWord (const char* const word, size_t len, size_t *out_n_suggs); + + bool requestDictionary (const char * szLang); + +private: + GIConv m_translate_in; /* Selected translation from/to Unicode */ + GIConv m_translate_out; + Hunspell *myspell; + EnchantBroker *m_broker; +}; + +static void +myspell_checker_free_helper (gpointer p, gpointer user _GL_UNUSED_PARAMETER) +{ + g_free (p); +} + +/***************************************************************************/ + +static bool +g_iconv_is_valid(GIConv i) +{ + return (i != G_ICONV_INVALID); +} + +MySpellChecker::MySpellChecker(EnchantBroker * broker) +: m_translate_in(G_ICONV_INVALID), m_translate_out(G_ICONV_INVALID), myspell(0), m_broker(broker) +{ +} + +MySpellChecker::~MySpellChecker() +{ + delete myspell; + if (g_iconv_is_valid (m_translate_in )) + g_iconv_close(m_translate_in); + if (g_iconv_is_valid(m_translate_out)) + g_iconv_close(m_translate_out); +} + +bool +MySpellChecker::checkWord(const char *utf8Word, size_t len) +{ + if (len > MAXWORDLEN || !g_iconv_is_valid(m_translate_in)) + return false; + + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); + char *in = normalizedWord; + char word8[MAXWORDLEN + 1]; + char *out = word8; + size_t len_in = strlen(in); + size_t len_out = sizeof( word8 ) - 1; + size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); + g_free(normalizedWord); + if (static_cast(-1) == result) + return false; + *out = '\0'; + if (myspell->spell(word8)) + return true; + else + return false; +} + +char** +MySpellChecker::suggestWord(const char* const utf8Word, size_t len, size_t *nsug) +{ + if (len > MAXWORDLEN + || !g_iconv_is_valid(m_translate_in) + || !g_iconv_is_valid(m_translate_out)) + return nullptr; + + // the 8bit encodings use precomposed forms + char *normalizedWord = g_utf8_normalize (utf8Word, len, G_NORMALIZE_NFC); + char *in = normalizedWord; + char word8[MAXWORDLEN + 1]; + char *out = word8; + size_t len_in = strlen(in); + size_t len_out = sizeof(word8) - 1; + size_t result = g_iconv(m_translate_in, &in, &len_in, &out, &len_out); + g_free(normalizedWord); + if (static_cast(-1) == result) + return nullptr; + + *out = '\0'; + char **sugMS; + *nsug = myspell->suggest(&sugMS, word8); + if (*nsug > 0) { + char **sug = g_new0 (char *, *nsug + 1); + for (size_t i=0; i<*nsug; i++) { + in = sugMS[i]; + len_in = strlen(in); + len_out = MAXWORDLEN; + char *word = g_new0(char, len_out + 1); + out = word; + if (static_cast(-1) == g_iconv(m_translate_out, &in, &len_in, &out, &len_out)) { + for (size_t j = i; j < *nsug; j++) + free(sugMS[j]); + free(sugMS); + + *nsug = i; + return sug; + } + *(out) = 0; + sug[i] = word; + free(sugMS[i]); + } + free(sugMS); + return sug; + } + else + return nullptr; +} + +static GSList * +myspell_checker_get_dictionary_dirs (EnchantBroker * broker) +{ + GSList *dirs = NULL; + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_user_config_dirs (); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_build_filename (static_cast(iter->data), + "myspell", nullptr)); + } + + g_slist_foreach (config_dirs, myspell_checker_free_helper, nullptr); + g_slist_free (config_dirs); + } + + { + const gchar* const * system_data_dirs = g_get_system_data_dirs (); + const gchar* const * iter; + + for (iter = system_data_dirs; *iter; iter++) + { + dirs = g_slist_append (dirs, g_build_filename (*iter, "myspell", "dicts", nullptr)); + } + } + + /* Dynamically locate library and search for modules relative to it. */ + char * enchant_prefix = enchant_get_prefix_dir(); + if(enchant_prefix) + { + char * myspell_prefix = g_build_filename(enchant_prefix, "share", "enchant", "myspell", nullptr); + g_free(enchant_prefix); + dirs = g_slist_append (dirs, myspell_prefix); + } + +#ifdef ENCHANT_MYSPELL_DICT_DIR + dirs = g_slist_append (dirs, g_strdup (ENCHANT_MYSPELL_DICT_DIR)); +#endif + + { + GSList *config_dirs, *iter; + + config_dirs = enchant_get_dirs_from_param (broker, "enchant.myspell.dictionary.path"); + + for (iter = config_dirs; iter; iter = iter->next) + { + dirs = g_slist_append (dirs, g_strdup (static_cast(iter->data))); + } + + g_slist_foreach (config_dirs, myspell_checker_free_helper, nullptr); + g_slist_free (config_dirs); + } + + { + const gchar* hun_dir = g_getenv("DICPATH"); + if (hun_dir) + { + dirs = g_slist_append(dirs, g_strdup(hun_dir)); + } + } + + return dirs; +} + +static void +s_buildDictionaryDirs (std::vector & dirs, EnchantBroker * broker) +{ + GSList *myspell_dirs, *iter; + + dirs.clear (); + + myspell_dirs = myspell_checker_get_dictionary_dirs (broker); + for (iter = myspell_dirs; iter; iter = iter->next) + { + dirs.push_back (static_cast(iter->data)); + } + + g_slist_foreach (myspell_dirs, myspell_checker_free_helper, nullptr); + g_slist_free (myspell_dirs); +} + +static void +s_buildHashNames (std::vector & names, EnchantBroker * broker, const char * dict) +{ + names.clear (); + + std::vector dirs; + s_buildDictionaryDirs (dirs, broker); + + char *dict_dic = g_strconcat(dict, ".dic", nullptr); + for (size_t i = 0; i < dirs.size(); i++) + { + char *tmp = g_build_filename (dirs[i].c_str(), dict_dic, nullptr); + names.push_back (tmp); + g_free (tmp); + } + + g_free(dict_dic); +} + +static bool +s_hasCorrespondingAffFile(const std::string & dicFile) +{ + std::string aff = dicFile; + aff.replace(aff.end()-3,aff.end(), "aff"); + return g_file_test(aff.c_str(), G_FILE_TEST_EXISTS) != 0; +} + +static bool is_plausible_dict_for_tag(const char *dir_entry, const char *tag) +{ + const char *dic_suffix = ".dic"; + size_t dic_suffix_len = strlen(dic_suffix); + size_t dir_entry_len = strlen(dir_entry); + size_t tag_len = strlen(tag); + + if (dir_entry_len - dic_suffix_len < tag_len) + return false; + if (strcmp(dir_entry+dir_entry_len-dic_suffix_len, dic_suffix) != 0) + return false; + if (strncmp (dir_entry, tag, tag_len) != 0) + return false; + //e.g. requested dict for "fi", + //reject "fil_PH.dic" + //allow "fi-FOO.dic", "fi_FOO.dic", "fi.dic", etc. + if (!ispunct(dir_entry[tag_len])) + return false; + return true; +} + +static char * +myspell_request_dictionary (EnchantBroker * broker, const char * tag) +{ + std::vector names; + + s_buildHashNames (names, broker, tag); + + for (size_t i = 0; i < names.size (); i++) { + if (g_file_test(names[i].c_str(), G_FILE_TEST_EXISTS)) { + if(s_hasCorrespondingAffFile(names[i])){ + return g_strdup (names[i].c_str()); + } + } + } + + std::vector dirs; + s_buildDictionaryDirs (dirs, broker); + + for (size_t i = 0; i < dirs.size(); i++) { + GDir *dir = g_dir_open (dirs[i].c_str(), 0, nullptr); + if (dir) { + const char *dir_entry; + while ((dir_entry = g_dir_read_name (dir)) != NULL) { + if (is_plausible_dict_for_tag(dir_entry, tag)) { + char *dict = g_build_filename (dirs[i].c_str(), + dir_entry, nullptr); + if(s_hasCorrespondingAffFile(dict)){ + g_dir_close (dir); + return dict; + } + } + } + + g_dir_close (dir); + } + } + + return NULL; +} + +bool +MySpellChecker::requestDictionary(const char *szLang) +{ + char *dic = NULL, *aff = NULL; + + dic = myspell_request_dictionary (m_broker, szLang); + if (!dic) + return false; + + aff = g_strdup(dic); + int len_dic = strlen(dic); + strcpy(aff+len_dic-3, "aff"); + if (g_file_test(aff, G_FILE_TEST_EXISTS)) + { + myspell = new Hunspell(aff, dic); + } + g_free(dic); + g_free(aff); + if(myspell == NULL){ + return false; + } + char *enc = myspell->get_dic_encoding(); + + m_translate_in = g_iconv_open(enc, "UTF-8"); + m_translate_out = g_iconv_open("UTF-8", enc); + + return true; +} + +/* + * Enchant + */ + +static char ** +myspell_dict_suggest (EnchantDict * me, const char *const word, + size_t len, size_t * out_n_suggs) +{ + MySpellChecker * checker; + + checker = static_cast(me->user_data); + return checker->suggestWord (word, len, out_n_suggs); +} + +static int +myspell_dict_check (EnchantDict * me, const char *const word, size_t len) +{ + MySpellChecker * checker; + + checker = static_cast(me->user_data); + + if (checker->checkWord(word, len)) + return 0; + + return 1; +} + +static void +myspell_provider_enum_dicts (const char * const directory, + std::vector & out_dicts) +{ + GDir * dir = g_dir_open (directory, 0, nullptr); + if (dir) { + const char * entry; + + while ((entry = g_dir_read_name (dir)) != NULL) { + char * utf8_entry = g_filename_to_utf8 (entry, -1, nullptr, nullptr, nullptr); + if (utf8_entry) { + std::string dir_entry (utf8_entry); + g_free (utf8_entry); + + int hit = dir_entry.rfind (".dic"); + if (hit != -1) { + /* don't include hyphenation dictionaries + and require .aff file to be present*/ + if(dir_entry.compare (0, 5, "hyph_") != 0) + { + std::string name(dir_entry.substr (0, hit)); + std::string affFileName(name + ".aff"); + char * aff = g_build_filename(directory, affFileName.c_str(), nullptr); + if (g_file_test(aff, G_FILE_TEST_EXISTS)) + { + out_dicts.push_back (dir_entry.substr (0, hit)); + } + g_free(aff); + } + } + } + } + + g_dir_close (dir); + } +} + +extern "C" { + +ENCHANT_MODULE_EXPORT (EnchantProvider *) + init_enchant_provider (void); + +static char ** +myspell_provider_list_dicts (EnchantProvider * me, + size_t * out_n_dicts) +{ + std::vector dict_dirs, dicts; + char ** dictionary_list = NULL; + + s_buildDictionaryDirs (dict_dirs, me->owner); + + for (size_t i = 0; i < dict_dirs.size(); i++) + { + myspell_provider_enum_dicts (dict_dirs[i].c_str(), dicts); + } + + if (dicts.size () > 0) { + dictionary_list = g_new0 (char *, dicts.size() + 1); + + for (size_t i = 0; i < dicts.size(); i++) + dictionary_list[i] = g_strdup (dicts[i].c_str()); + } + + *out_n_dicts = dicts.size (); + return dictionary_list; +} + +static void +myspell_provider_free_string_list (EnchantProvider * me _GL_UNUSED_PARAMETER, char **str_list) +{ + g_strfreev (str_list); +} + +static EnchantDict * +myspell_provider_request_dict(EnchantProvider * me, const char *const tag) +{ + EnchantDict *dict; + MySpellChecker * checker; + + checker = new MySpellChecker(me->owner); + + if (!checker) + return NULL; + + if (!checker->requestDictionary(tag)) { + delete checker; + return NULL; + } + + dict = g_new0(EnchantDict, 1); + dict->user_data = (void *) checker; + dict->check = myspell_dict_check; + dict->suggest = myspell_dict_suggest; + // don't implement personal, session + + return dict; +} + +static void +myspell_provider_dispose_dict (EnchantProvider * me _GL_UNUSED_PARAMETER, EnchantDict * dict) +{ + MySpellChecker *checker; + + checker = (MySpellChecker *) dict->user_data; + delete checker; + + g_free (dict); +} + +static int +myspell_provider_dictionary_exists (struct str_enchant_provider * me, + const char *const tag) +{ + std::vector names; + + s_buildHashNames (names, me->owner, tag); + for (size_t i = 0; i < names.size(); i++) { + if (g_file_test (names[i].c_str(), G_FILE_TEST_EXISTS)) + { + std::string aff(names[i]); + aff.replace(aff.end() - 3, aff.end(), "aff"); + if (g_file_test(aff.c_str(), G_FILE_TEST_EXISTS)) + return 1; + } + } + + return 0; +} + +static void +myspell_provider_dispose (EnchantProvider * me) +{ + g_free (me); +} + +static const char * +myspell_provider_identify (EnchantProvider * me _GL_UNUSED_PARAMETER) +{ + return "myspell"; +} + +static const char * +myspell_provider_describe (EnchantProvider * me _GL_UNUSED_PARAMETER) +{ + return "Myspell Provider"; +} + +EnchantProvider * +init_enchant_provider (void) +{ + EnchantProvider *provider; + + provider = g_new0(EnchantProvider, 1); + provider->dispose = myspell_provider_dispose; + provider->request_dict = myspell_provider_request_dict; + provider->dispose_dict = myspell_provider_dispose_dict; + provider->dictionary_exists = myspell_provider_dictionary_exists; + provider->identify = myspell_provider_identify; + provider->describe = myspell_provider_describe; + provider->free_string_list = myspell_provider_free_string_list; + provider->list_dicts = myspell_provider_list_dicts; + + return provider; +} + +} // extern C linkage diff --git a/tests/Makefile.am b/tests/Makefile.am index 7f95d52..139f963 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,7 +6,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src $(ENCHANT_CFLAGS) # FIXME: Following line is ugly (but inserting backslashes stops it # working), and not portable to Windows (need semicolons instead of colons) AM_TESTS_ENVIRONMENT = \ - ENCHANT_MODULE_PATH=$(top_builddir)/src/applespell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/applespell:$(top_builddir)/src/aspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/aspell:$(top_builddir)/src/hspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/hspell:$(top_builddir)/src/ispell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/ispell:$(top_builddir)/src/hunspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/hunspell:$(top_builddir)/src/uspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/uspell:$(top_builddir)/src/voikko/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/voikko:$(top_builddir)/src/zemberek/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/zemberek; \ + ENCHANT_MODULE_PATH=$(top_builddir)/src/applespell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/applespell:$(top_builddir)/src/aspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/aspell:$(top_builddir)/src/hspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/hspell:$(top_builddir)/src/ispell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/ispell:$(top_builddir)/src/myspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/myspell:$(top_builddir)/src/uspell/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/uspell:$(top_builddir)/src/voikko/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/voikko:$(top_builddir)/src/zemberek/$(ENCHANT_LT_OBJDIR):$(top_builddir)/src/zemberek; \ export ENCHANT_MODULE_PATH; \ rm -f test.pwl; \ cp $(srcdir)/test.pwl.orig $(builddir)/test.pwl; \ diff --git a/unittests/broker/enchant_broker_set_ordering_tests.cpp b/unittests/broker/enchant_broker_set_ordering_tests.cpp index bad4202..6e1d8ea 100644 --- a/unittests/broker/enchant_broker_set_ordering_tests.cpp +++ b/unittests/broker/enchant_broker_set_ordering_tests.cpp @@ -141,7 +141,7 @@ struct EnchantBrokerFileSetOrdering_TestFixture: EnchantBrokerSetOrdering_TestFi * enchant_broker_set_ordering * @broker: A non-null #EnchantBroker * @tag: A non-null language tag (en_US) - * @ordering: A non-null ordering (aspell,hunspell,ispell,uspell,hspell) + * @ordering: A non-null ordering (aspell,myspell,ispell,uspell,hspell) * * Declares a preference of dictionaries to use for the language * described/referred to by @tag. The ordering is a comma delimited @@ -323,7 +323,7 @@ TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, EnchantBrokerSetOrdering_EmptyLanguageTag_DoNothing) { - enchant_broker_set_ordering(_broker, "", "aspell,hunspell,ispell"); + enchant_broker_set_ordering(_broker, "", "aspell,myspell,ispell"); } TEST_FIXTURE(EnchantBrokerSetOrdering_TestFixture, -- cgit v1.2.1