diff --git a/src/aspell/rclaspell.cpp b/src/aspell/rclaspell.cpp index 60dfa60d..15efb1da 100644 --- a/src/aspell/rclaspell.cpp +++ b/src/aspell/rclaspell.cpp @@ -1,5 +1,4 @@ -#ifndef TEST_RCLASPELL -/* Copyright (C) 2006 J.F.Dockes +/* Copyright (C) 2006-2019 J.F.Dockes * This program 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 2 of the License, or @@ -15,49 +14,47 @@ * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifdef HAVE_CONFIG_H #include "autoconfig.h" -#endif #ifdef RCL_USE_ASPELL -#include - -#include -#include -#include - #include ASPELL_INCLUDE +#include +#include +#include + +#include "dlib.h" #include "pathut.h" #include "execmd.h" #include "rclaspell.h" #include "log.h" #include "unacpp.h" +#include "rclutil.h" using namespace std; -// Just a place where we keep the Aspell library entry points together +// Aspell library entry points class AspellApi { public: struct AspellConfig *(*new_aspell_config)(); int (*aspell_config_replace)(struct AspellConfig *, const char * key, - const char * value); + const char * value); struct AspellCanHaveError *(*new_aspell_speller)(struct AspellConfig *); void (*delete_aspell_config)(struct AspellConfig *); void (*delete_aspell_can_have_error)(struct AspellCanHaveError *); struct AspellSpeller * (*to_aspell_speller)(struct AspellCanHaveError *); struct AspellConfig * (*aspell_speller_config)(struct AspellSpeller *); const struct AspellWordList * (*aspell_speller_suggest) - (struct AspellSpeller *, const char *, int); + (struct AspellSpeller *, const char *, int); int (*aspell_speller_check)(struct AspellSpeller *, const char *, int); struct AspellStringEnumeration * (*aspell_word_list_elements) - (const struct AspellWordList * ths); + (const struct AspellWordList * ths); const char * (*aspell_string_enumeration_next) - (struct AspellStringEnumeration * ths); + (struct AspellStringEnumeration * ths); void (*delete_aspell_string_enumeration)(struct AspellStringEnumeration *); const struct AspellError *(*aspell_error) - (const struct AspellCanHaveError *); + (const struct AspellCanHaveError *); const char *(*aspell_error_message)(const struct AspellCanHaveError *); const char *(*aspell_speller_error_message)(const struct AspellSpeller *); void (*delete_aspell_speller)(struct AspellSpeller *); @@ -66,49 +63,51 @@ public: static AspellApi aapi; static std::mutex o_aapi_mutex; -#define NMTOPTR(NM, TP) \ - if ((aapi.NM = TP dlsym(m_data->m_handle, #NM)) == 0) { \ - badnames += #NM + string(" "); \ +#define NMTOPTR(NM, TP) \ + if ((aapi.NM = TP dlib_sym(m_data->m_handle, #NM)) == 0) { \ + badnames += #NM + string(" "); \ } static const vector aspell_lib_suffixes { #if defined(__APPLE__) - ".15.dylib", + ".15.dylib", ".dylib", +#elif defined(_WIN32) + "-15.dll", #else ".so", ".so.15", - ".so.16", #endif -}; + }; -// Stuff that we don't wish to see in the .h (possible sysdeps, etc.) +// Private rclaspell data class AspellData { public: - AspellData() - : m_handle(0), m_speller(0) - {} ~AspellData() { LOGDEB2("~AspellData\n" ); - if (m_handle) { - dlclose(m_handle); - m_handle = 0; + if (m_handle) { + dlib_close(m_handle); + m_handle = nullptr; } if (m_speller) { // Dumps core if I do this?? //aapi.delete_aspell_speller(m_speller); - m_speller = 0; + m_speller = nullptr; LOGDEB2("~AspellData: speller done\n" ); } } - void *m_handle; + void *m_handle{nullptr}; string m_exec; - AspellSpeller *m_speller; + AspellSpeller *m_speller{nullptr}; +#ifdef _WIN32 + string m_datadir; +#endif + string m_addCreateParam; }; Aspell::Aspell(const RclConfig *cnf) - : m_config(cnf), m_data(0) + : m_config(cnf) { } @@ -126,15 +125,15 @@ bool Aspell::init(string &reason) // environment. The aspell language names used for selecting language // definition files (used to create dictionaries) are like en, fr if (!m_config->getConfParam("aspellLanguage", m_lang) || m_lang.empty()) { - string lang = "en"; - const char *cp; - if ((cp = getenv("LC_ALL"))) - lang = cp; - else if ((cp = getenv("LANG"))) - lang = cp; - if (!lang.compare("C")) - lang = "en"; - m_lang = lang.substr(0, lang.find_first_of("_")); + string lang = "en"; + const char *cp; + if ((cp = getenv("LC_ALL"))) + lang = cp; + else if ((cp = getenv("LANG"))) + lang = cp; + if (!lang.compare("C")) + lang = "en"; + m_lang = lang.substr(0, lang.find_first_of("_")); if (!m_lang.compare("ja")) { // Aspell has no support for Japanese. We substitute // english, as Japanese users often have texts with @@ -147,23 +146,38 @@ bool Aspell::init(string &reason) m_data = new AspellData; + m_config->getConfParam("aspellAddCreateParam", m_data->m_addCreateParam); +#ifdef _WIN32 + m_data->m_datadir = path_cat( + path_pkgdatadir(), + "filters/aspell-installed/mingw32/lib/aspell-0.60"); + if (m_data->m_addCreateParam.empty()) { + m_data->m_addCreateParam = string("--local-data-dir=") + + path_cat(m_config->getConfDir(), "aspell"); + } +#endif // WIN32 + const char *aspell_prog_from_env = getenv("ASPELL_PROG"); if (aspell_prog_from_env && access(aspell_prog_from_env, X_OK) == 0) { - m_data->m_exec = aspell_prog_from_env; + m_data->m_exec = aspell_prog_from_env; + } #ifdef ASPELL_PROG - } else if (access(ASPELL_PROG, X_OK) == 0) { - m_data->m_exec = ASPELL_PROG; -#endif // ASPELL_PROG - } else { - ExecCmd::which("aspell", m_data->m_exec); - } - if (m_data->m_exec.empty()) { - reason = "aspell program not found or not executable"; - deleteZ(m_data); - return false; + string cmd = m_config->findFilter(ASPELL_PROG); + LOGDEB("rclaspell::init: findFilter returns " << cmd << endl); + if (path_isabsolute(cmd)) { + m_data->m_exec.swap(cmd); + } + } +#endif // ASPELL_PROG + if (m_data->m_exec.empty()) { + ExecCmd::which("aspell", m_data->m_exec); + } + if (m_data->m_exec.empty()) { + reason = "aspell program not found or not executable"; + deleteZ(m_data); + return false; } - // Don't know what with Apple and (DY)LD_LIBRARY_PATH. Does not work // So we look in all ../lib in the PATH... @@ -181,16 +195,28 @@ bool Aspell::init(string &reason) for (const auto& suff : aspell_lib_suffixes) { lib = libbase + suff; reason += string("[") + lib + "] "; - if ((m_data->m_handle = dlopen(lib.c_str(), RTLD_LAZY)) != 0) { + if ((m_data->m_handle = dlib_open(lib)) != 0) { reason.erase(); goto found; } -#if defined(__APPLE__) // Above was the normal lookup: let dlopen search the directories. - // Here is for Apple. Also look at all ../lib along the PATH + // Also look in other places for Apple and Windows. +#if defined(__APPLE__) for (const auto& dir : path) { string lib1 = path_canon(dir + "/../lib/" + lib); - if ((m_data->m_handle = dlopen(lib1.c_str(), RTLD_LAZY)) != 0) { + if ((m_data->m_handle = dlib_open(lib1)) != 0) { + reason.erase(); + lib=lib1; + goto found; + } + } +#endif +#if defined(_WIN32) + // Look in the directory of the aspell binary + { + string bindir = path_getfather(m_data->m_exec); + string lib1 = path_cat(bindir, lib); + if ((m_data->m_handle = dlib_open(lib1)) != 0) { reason.erase(); lib=lib1; goto found; @@ -199,9 +225,9 @@ bool Aspell::init(string &reason) #endif } - found: +found: if (m_data->m_handle == 0) { - reason += string(" : ") + dlerror(); + reason += string(" : ") + dlib_error(); deleteZ(m_data); return false; } @@ -209,41 +235,41 @@ bool Aspell::init(string &reason) string badnames; NMTOPTR(new_aspell_config, (struct AspellConfig *(*)())); NMTOPTR(aspell_config_replace, (int (*)(struct AspellConfig *, - const char *, const char *))); + const char *, const char *))); NMTOPTR(new_aspell_speller, - (struct AspellCanHaveError *(*)(struct AspellConfig *))); + (struct AspellCanHaveError *(*)(struct AspellConfig *))); NMTOPTR(delete_aspell_config, - (void (*)(struct AspellConfig *))); + (void (*)(struct AspellConfig *))); NMTOPTR(delete_aspell_can_have_error, - (void (*)(struct AspellCanHaveError *))); + (void (*)(struct AspellCanHaveError *))); NMTOPTR(to_aspell_speller, - (struct AspellSpeller *(*)(struct AspellCanHaveError *))); + (struct AspellSpeller *(*)(struct AspellCanHaveError *))); NMTOPTR(aspell_speller_config, - (struct AspellConfig *(*)(struct AspellSpeller *))); + (struct AspellConfig *(*)(struct AspellSpeller *))); NMTOPTR(aspell_speller_suggest, - (const struct AspellWordList *(*)(struct AspellSpeller *, - const char *, int))); + (const struct AspellWordList *(*)(struct AspellSpeller *, + const char *, int))); NMTOPTR(aspell_speller_check, - (int (*)(struct AspellSpeller *, const char *, int))); + (int (*)(struct AspellSpeller *, const char *, int))); NMTOPTR(aspell_word_list_elements, - (struct AspellStringEnumeration *(*) - (const struct AspellWordList *))); + (struct AspellStringEnumeration *(*) + (const struct AspellWordList *))); NMTOPTR(aspell_string_enumeration_next, - (const char * (*)(struct AspellStringEnumeration *))); + (const char * (*)(struct AspellStringEnumeration *))); NMTOPTR(delete_aspell_string_enumeration, - (void (*)(struct AspellStringEnumeration *))); + (void (*)(struct AspellStringEnumeration *))); NMTOPTR(aspell_error, - (const struct AspellError*(*)(const struct AspellCanHaveError *))); + (const struct AspellError*(*)(const struct AspellCanHaveError *))); NMTOPTR(aspell_error_message, - (const char *(*)(const struct AspellCanHaveError *))); + (const char *(*)(const struct AspellCanHaveError *))); NMTOPTR(aspell_speller_error_message, - (const char *(*)(const struct AspellSpeller *))); + (const char *(*)(const struct AspellSpeller *))); NMTOPTR(delete_aspell_speller, (void (*)(struct AspellSpeller *))); if (!badnames.empty()) { - reason = string("Aspell::init: symbols not found:") + badnames; + reason = string("Aspell::init: symbols not found:") + badnames; deleteZ(m_data); - return false; + return false; } return true; @@ -274,29 +300,29 @@ public: Rcl::TermIter *m_tit; Rcl::Db &m_db; AspExecPv(string *i, Rcl::TermIter *tit, Rcl::Db &db) - : m_input(i), m_tit(tit), m_db(db) - {} + : m_input(i), m_tit(tit), m_db(db) + {} void newData() { - while (m_db.termWalkNext(m_tit, *m_input)) { - LOGDEB2("Aspell::buildDict: term: [" << (m_input) << "]\n" ); - if (!Rcl::Db::isSpellingCandidate(*m_input)) { - LOGDEB2("Aspell::buildDict: SKIP\n" ); - continue; - } - if (!o_index_stripchars) { - string lower; - if (!unacmaybefold(*m_input, lower, "UTF-8", UNACOP_FOLD)) - continue; - m_input->swap(lower); - } - // Got a non-empty sort-of appropriate term, let's send it to - // aspell - LOGDEB2("Apell::buildDict: SEND\n" ); - m_input->append("\n"); - return; - } - // End of data. Tell so. Exec will close cmd. - m_input->erase(); + while (m_db.termWalkNext(m_tit, *m_input)) { + LOGDEB2("Aspell::buildDict: term: [" << (m_input) << "]\n" ); + if (!Rcl::Db::isSpellingCandidate(*m_input)) { + LOGDEB2("Aspell::buildDict: SKIP\n" ); + continue; + } + if (!o_index_stripchars) { + string lower; + if (!unacmaybefold(*m_input, lower, "UTF-8", UNACOP_FOLD)) + continue; + m_input->swap(lower); + } + // Got a non-empty sort-of appropriate term, let's send it to + // aspell + LOGDEB2("Apell::buildDict: SEND\n" ); + m_input->append("\n"); + return; + } + // End of data. Tell so. Exec will close cmd. + m_input->erase(); } }; @@ -304,10 +330,7 @@ public: bool Aspell::buildDict(Rcl::Db &db, string &reason) { if (!ok()) - return false; - - string addCreateParam; - m_config->getConfParam("aspellAddCreateParam", addCreateParam); + return false; // We create the dictionary by executing the aspell command: // aspell --lang=[lang] create master [dictApath] @@ -318,9 +341,12 @@ bool Aspell::buildDict(Rcl::Db &db, string &reason) cmdstring += string(" ") + string("--lang=") + m_lang; args.push_back("--encoding=utf-8"); cmdstring += string(" ") + "--encoding=utf-8"; - if (!addCreateParam.empty()) { - args.push_back(addCreateParam); - cmdstring += string(" ") + addCreateParam; +#ifdef _WIN32 + args.push_back(string("--data-dir=") + m_data->m_datadir); +#endif + if (!m_data->m_addCreateParam.empty()) { + args.push_back(m_data->m_addCreateParam); + cmdstring += string(" ") + m_data->m_addCreateParam; } args.push_back("create"); cmdstring += string(" ") + "create"; @@ -335,54 +361,58 @@ bool Aspell::buildDict(Rcl::Db &db, string &reason) bool keepStderr = false; m_config->getConfParam("aspellKeepStderr", &keepStderr); if (!keepStderr) - aspell.setStderr("/dev/null"); + aspell.setStderr("/dev/null"); Rcl::TermIter *tit = db.termWalkOpen(); if (tit == 0) { - reason = "termWalkOpen failed\n"; - return false; + reason = "termWalkOpen failed\n"; + return false; } string termbuf; AspExecPv pv(&termbuf, tit, db); aspell.setProvide(&pv); if (aspell.doexec(m_data->m_exec, args, &termbuf)) { - ExecCmd cmd; - args.clear(); - args.push_back("dicts"); - string dicts; - bool hasdict = false; - if (cmd.doexec(m_data->m_exec, args, 0, &dicts)) { - vector vdicts; - stringToTokens(dicts, vdicts, "\n\r\t "); - if (find(vdicts.begin(), vdicts.end(), m_lang) != vdicts.end()) { - hasdict = true; - } - } - if (hasdict) - reason = string( - "\naspell dictionary creation command [") + - cmdstring + string("] failed. Reason unknown.\n" - "Try to set aspellKeepStderr = 1 in recoll.conf, and execute \n" - "the indexing command in a terminal to see the aspell " - "diagnostic output.\n"); - else - reason = string("aspell dictionary creation command failed:\n") + + ExecCmd cmd; + args.clear(); + args.push_back("dicts"); + string dicts; + bool hasdict = false; + if (cmd.doexec(m_data->m_exec, args, 0, &dicts)) { + vector vdicts; + stringToTokens(dicts, vdicts, "\n\r\t "); + if (find(vdicts.begin(), vdicts.end(), m_lang) != vdicts.end()) { + hasdict = true; + } + } + if (hasdict) { + reason = string("\naspell dictionary creation command [") + + cmdstring; + reason += string( + "] failed. Reason unknown.\n" + "Try to set aspellKeepStderr = 1 in recoll.conf, and execute \n" + "the indexing command in a terminal to see the aspell " + "diagnostic output.\n"); + } else { + reason = string("aspell dictionary creation command failed:\n") + cmdstring + "\n" "One possible reason might be missing language " "data files for lang = " + m_lang + ". Maybe try to execute the command by hand for a better diag."; - return false; + } + return false; } db.termWalkClose(tit); return true; } +static const unsigned int ldatadiroptsz = + string("--local-data-dir=").size(); bool Aspell::make_speller(string& reason) { if (!ok()) - return false; + return false; if (m_data->m_speller != 0) return true; @@ -393,14 +423,18 @@ bool Aspell::make_speller(string& reason) aapi.aspell_config_replace(config, "encoding", "utf-8"); aapi.aspell_config_replace(config, "master", dicPath().c_str()); aapi.aspell_config_replace(config, "sug-mode", "fast"); + aapi.aspell_config_replace(config, "data-dir", m_data->m_datadir.c_str()); + aapi.aspell_config_replace( + config, "local-data-dir", + m_data->m_addCreateParam.substr(ldatadiroptsz).c_str()); // aapi.aspell_config_replace(config, "sug-edit-dist", "2"); ret = aapi.new_aspell_speller(config); aapi.delete_aspell_config(config); if (aapi.aspell_error(ret) != 0) { - reason = aapi.aspell_error_message(ret); - aapi.delete_aspell_can_have_error(ret); - return false; + reason = aapi.aspell_error_message(ret); + aapi.delete_aspell_can_have_error(ret); + return false; } m_data->m_speller = aapi.to_aspell_speller(ret); return true; @@ -417,17 +451,17 @@ bool Aspell::check(const string &iterm, string& reason) return true; } if (!ok() || !make_speller(reason)) - return false; + return false; if (iterm.empty()) return true; //?? if (!o_index_stripchars) { - string lower; - if (!unacmaybefold(mterm, lower, "UTF-8", UNACOP_FOLD)) { - LOGERR("Aspell::check: cant lowercase input\n"); - return false; - } - mterm.swap(lower); + string lower; + if (!unacmaybefold(mterm, lower, "UTF-8", UNACOP_FOLD)) { + LOGERR("Aspell::check: cant lowercase input\n"); + return false; + } + mterm.swap(lower); } int ret = aapi.aspell_speller_check(m_data->m_speller, @@ -449,7 +483,7 @@ bool Aspell::suggest(Rcl::Db &db, const string &_term, { LOGDEB("Aspell::suggest: term [" << _term << "]\n"); if (!ok() || !make_speller(reason)) - return false; + return false; string mterm(_term); if (mterm.empty()) return true; //?? @@ -461,178 +495,36 @@ bool Aspell::suggest(Rcl::Db &db, const string &_term, } if (!o_index_stripchars) { - string lower; - if (!unacmaybefold(mterm, lower, "UTF-8", UNACOP_FOLD)) { - LOGERR("Aspell::check : cant lowercase input\n"); - return false; - } - mterm.swap(lower); + string lower; + if (!unacmaybefold(mterm, lower, "UTF-8", UNACOP_FOLD)) { + LOGERR("Aspell::check : cant lowercase input\n"); + return false; + } + mterm.swap(lower); } - AspellCanHaveError *ret; - const AspellWordList *wl = - aapi.aspell_speller_suggest(m_data->m_speller, + aapi.aspell_speller_suggest(m_data->m_speller, mterm.c_str(), mterm.length()); if (wl == 0) { - reason = aapi.aspell_speller_error_message(m_data->m_speller); - return false; + reason = aapi.aspell_speller_error_message(m_data->m_speller); + return false; } AspellStringEnumeration *els = aapi.aspell_word_list_elements(wl); const char *word; while ((word = aapi.aspell_string_enumeration_next(els)) != 0) { LOGDEB0("Aspell::suggest: got [" << word << "]\n"); - // Check that the word exists in the index (we don't want - // aspell computed stuff, only exact terms from the - // dictionary). We used to also check that it stems - // differently from the base word but this is complicated - // (stemming on/off + language), so we now leave this to the - // caller. - if (db.termExists(word)) - suggestions.push_back(word); + // Check that the word exists in the index (we don't want + // aspell computed stuff, only exact terms from the + // dictionary). We used to also check that it stems + // differently from the base word but this is complicated + // (stemming on/off + language), so we now leave this to the + // caller. + if (db.termExists(word)) + suggestions.push_back(word); } aapi.delete_aspell_string_enumeration(els); return true; } #endif // RCL_USE_ASPELL - -#else // TEST_RCLASPELL test driver -> - -#ifdef HAVE_CONFIG_H -#include "autoconfig.h" -#endif - -#ifdef RCL_USE_ASPELL - -#include -#include -#include -#include -#include - -#include -using namespace std; - -#include "rclinit.h" -#include "rclconfig.h" -#include "rcldb.h" -#include "rclaspell.h" - -static char *thisprog; -RclConfig *rclconfig; - -static char usage [] = -" -b : build dictionary\n" -" -s : suggestions for term\n" -" -c : check term\n" -"\n" -; -static void -Usage(void) -{ - fprintf(stderr, "%s: usage:\n%s", thisprog, usage); - exit(1); -} - -static int op_flags; -#define OPT_MOINS 0x1 -#define OPT_s 0x2 -#define OPT_b 0x4 -#define OPT_c 0x8 - -int main(int argc, char **argv) -{ - string word; - - thisprog = argv[0]; - argc--; argv++; - - while (argc > 0 && **argv == '-') { - (*argv)++; - if (!(**argv)) - /* Cas du "adb - core" */ - Usage(); - while (**argv) - switch (*(*argv)++) { - case 'b': op_flags |= OPT_b; break; - case 'c': op_flags |= OPT_c; if (argc < 2) Usage(); - word = *(++argv); - argc--; - goto b1; - case 's': op_flags |= OPT_s; if (argc < 2) Usage(); - word = *(++argv); - argc--; - goto b1; - default: Usage(); break; - } - b1: argc--; argv++; - } - - if (argc != 0 || op_flags == 0) - Usage(); - - string reason; - rclconfig = recollinit(0, 0, 0, reason); - if (!rclconfig || !rclconfig->ok()) { - fprintf(stderr, "Configuration problem: %s\n", reason.c_str()); - exit(1); - } - - string dbdir = rclconfig->getDbDir(); - if (dbdir.empty()) { - fprintf(stderr, "No db directory in configuration"); - exit(1); - } - - Rcl::Db rcldb(rclconfig); - - if (!rcldb.open(Rcl::Db::DbRO, 0)) { - fprintf(stderr, "Could not open database in %s\n", dbdir.c_str()); - exit(1); - } - - Aspell aspell(rclconfig); - - if (!aspell.init(reason)) { - cerr << "Init failed: " << reason << endl; - exit(1); - } - if (op_flags & OPT_b) { - if (!aspell.buildDict(rcldb, reason)) { - cerr << "buildDict failed: " << reason << endl; - exit(1); - } - } else if (op_flags & OPT_c) { - bool ret = aspell.check(word, reason); - if (!ret && reason.size()) { - cerr << "Aspell error: " << reason << endl; - return 1; - } - cout << word; - if (ret) { - cout << " is in dictionary" << endl; - } else { - cout << " not in dictionary" << endl; - } - } else { - list suggs; - if (!aspell.suggest(rcldb, word, suggs, reason)) { - cerr << "suggest failed: " << reason << endl; - exit(1); - } - cout << "Suggestions for " << word << ":" << endl; - for (list::iterator it = suggs.begin(); - it != suggs.end(); it++) { - cout << *it << endl; - } - } - exit(0); -} -#else -int main(int argc, char **argv) -{return 1;} -#endif // RCL_USE_ASPELL - -#endif // TEST_RCLASPELL test driver - diff --git a/src/aspell/rclaspell.h b/src/aspell/rclaspell.h index 2db6c350..241c70be 100644 --- a/src/aspell/rclaspell.h +++ b/src/aspell/rclaspell.h @@ -70,7 +70,7 @@ class Aspell { std::string dicPath(); const RclConfig *m_config; std::string m_lang; - AspellData *m_data; + AspellData *m_data{nullptr}; bool make_speller(std::string& reason); }; diff --git a/src/common/autoconfig-win.h b/src/common/autoconfig-win.h index 739ed7ef..245636e4 100644 --- a/src/common/autoconfig-win.h +++ b/src/common/autoconfig-win.h @@ -6,10 +6,10 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* Path to the aspell api include file */ -/* #undef ASPELL_INCLUDE "aspell-local.h" */ +#define ASPELL_INCLUDE "aspell-local.h" -/* Path to the aspell program */ -/* #define ASPELL_PROG "/usr/bin/aspell" */ +/* Aspell program parameter to findFilter(). */ +#define ASPELL_PROG "aspell-installed/mingw32/bin/aspell" /* No X11 session monitoring support */ #define DISABLE_X11MON @@ -24,14 +24,16 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ #define HAVE_CXX0X_UNORDERED 1 /* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 +#undef HAVE_DLFCN_H + +#undef HAVE_DLOPEN + +/* Define if you have the iconv() function and it works. */ +#define HAVE_ICONV 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 -/* Define to 1 if you have the `dl' library (-ldl). */ -#define HAVE_LIBDL 1 - /* Define to 1 if you have the `pthread' library (-lpthread). */ #define HAVE_LIBPTHREAD 1 @@ -98,11 +100,16 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ /* Define to 1 if you have the header file. */ /* #undef HAVE_UNISTD_H */ +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST + /* Use multiple threads for indexing */ #define IDX_THREADS 1 -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ +/* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Define to the address where bug reports for this package should be sent. */ @@ -112,10 +119,10 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ #define PACKAGE_NAME "Recoll" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Recoll 1.25.21" +#define PACKAGE_STRING "Recoll 1.26.0~pre4" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.25.21" +#define PACKAGE_VERSION "1.26.0~pre4" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "recoll" @@ -126,9 +133,6 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ /* putenv parameter is const */ /* #undef PUTENV_ARG_CONST */ -/* Define as const if the declaration of iconv() needs const. */ -#define ICONV_CONST - /* Real time monitoring option */ #undef RCL_MONITOR @@ -136,7 +140,7 @@ overriden in the c++ code by ifdefs _WIN32 anyway */ /* #undef RCL_SPLIT_CAMELCASE */ /* Compile the aspell interface */ -/* #undef RCL_USE_ASPELL */ +#define RCL_USE_ASPELL 1 /* Compile the fam interface */ /* #undef RCL_USE_FAM */ diff --git a/src/query/reslistpager.cpp b/src/query/reslistpager.cpp index a32d1864..a6515d6a 100644 --- a/src/query/reslistpager.cpp +++ b/src/query/reslistpager.cpp @@ -14,9 +14,7 @@ * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifdef HAVE_CONFIG_H #include "autoconfig.h" -#endif #include #include diff --git a/src/testmains/traspell.cpp b/src/testmains/traspell.cpp new file mode 100644 index 00000000..8b26e1c6 --- /dev/null +++ b/src/testmains/traspell.cpp @@ -0,0 +1,146 @@ +/* Copyright (C) 2006-2019 J.F.Dockes + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "autoconfig.h" + +#ifdef RCL_USE_ASPELL + +#include +#include +#include +#include +#include + +#include +using namespace std; + +#include "rclinit.h" +#include "rclconfig.h" +#include "rcldb.h" +#include "rclaspell.h" + +static char *thisprog; +RclConfig *rclconfig; + +static char usage [] = +" -b : build dictionary\n" +" -s : suggestions for term\n" +" -c : check term\n" +"\n" +; +static void +Usage(void) +{ + fprintf(stderr, "%s: usage:\n%s", thisprog, usage); + exit(1); +} + +static int op_flags; +#define OPT_MOINS 0x1 +#define OPT_s 0x2 +#define OPT_b 0x4 +#define OPT_c 0x8 + +int main(int argc, char **argv) +{ + string word; + + thisprog = argv[0]; + argc--; argv++; + + while (argc > 0 && **argv == '-') { + (*argv)++; + if (!(**argv)) + /* Cas du "adb - core" */ + Usage(); + while (**argv) + switch (*(*argv)++) { + case 'b': op_flags |= OPT_b; break; + case 'c': op_flags |= OPT_c; if (argc < 2) Usage(); + word = *(++argv); + argc--; + goto b1; + case 's': op_flags |= OPT_s; if (argc < 2) Usage(); + word = *(++argv); + argc--; + goto b1; + default: Usage(); break; + } + b1: argc--; argv++; + } + + if (argc != 0 || op_flags == 0) + Usage(); + + string reason; + rclconfig = recollinit(0, 0, 0, reason); + if (!rclconfig || !rclconfig->ok()) { + fprintf(stderr, "Configuration problem: %s\n", reason.c_str()); + exit(1); + } + + string dbdir = rclconfig->getDbDir(); + if (dbdir.empty()) { + fprintf(stderr, "No db directory in configuration"); + exit(1); + } + + Rcl::Db rcldb(rclconfig); + + if (!rcldb.open(Rcl::Db::DbRO, 0)) { + fprintf(stderr, "Could not open database in %s\n", dbdir.c_str()); + exit(1); + } + + Aspell aspell(rclconfig); + + if (!aspell.init(reason)) { + cerr << "Init failed: " << reason << endl; + exit(1); + } + if (op_flags & OPT_b) { + if (!aspell.buildDict(rcldb, reason)) { + cerr << "buildDict failed: " << reason << endl; + exit(1); + } + } else if (op_flags & OPT_c) { + bool ret = aspell.check(word, reason); + if (!ret && reason.size()) { + cerr << "Aspell error: " << reason << endl; + return 1; + } + cout << word; + if (ret) { + cout << " is in dictionary" << endl; + } else { + cout << " not in dictionary" << endl; + } + } else { + list suggs; + if (!aspell.suggest(rcldb, word, suggs, reason)) { + cerr << "suggest failed: " << reason << endl; + exit(1); + } + cout << "Suggestions for " << word << ":" << endl; + for (list::iterator it = suggs.begin(); + it != suggs.end(); it++) { + cout << *it << endl; + } + } + exit(0); +} +#endif // RCL_USE_ASPELL diff --git a/src/utils/dlib.cpp b/src/utils/dlib.cpp new file mode 100644 index 00000000..588c5575 --- /dev/null +++ b/src/utils/dlib.cpp @@ -0,0 +1,82 @@ +/* Copyright (C) 2017-2019 J.F.Dockes + * + * License: GPL 2.1 + * + * This program 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 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifdef BUILDING_RECOLL +#include "autoconfig.h" +#else +#include "config.h" +#endif + +#include "dlib.h" + +#include "pathut.h" +#include "smallut.h" + +#ifdef _WIN32 +#include "safewindows.h" +#elif defined(HAVE_DLOPEN) +#include +#else +#error dlib.cpp not ported on this system +#endif + +void *dlib_open(const std::string& libname, int flags) +{ +#ifdef _WIN32 + return LoadLibraryA(libname.c_str()); +#elif defined(HAVE_DLOPEN) + return dlopen(libname.c_str(), RTLD_LAZY); +#else + return nullptr; +#endif +} + +void *dlib_sym(void *handle, const char *name) +{ +#ifdef _WIN32 + return (void *)::GetProcAddress((HMODULE)handle, name); +#elif defined(HAVE_DLOPEN) + return dlsym(handle, name); +#else + return nullptr; +#endif +} + +void dlib_close(void *handle) +{ +#ifdef _WIN32 + ::FreeLibrary((HMODULE)handle); +#elif defined(HAVE_DLOPEN) + dlclose(handle); +#endif +} + +const char *dlib_error() +{ +#ifdef _WIN32 + int error = GetLastError(); + static std::string errorstring; + errorstring = std::string("dlopen/dlsym error: ") + lltodecstr(error); + return errorstring.c_str(); +#elif defined(HAVE_DLOPEN) + return dlerror(); +#else + return "??? dlib not ported"; +#endif +} diff --git a/src/utils/dlib.h b/src/utils/dlib.h new file mode 100644 index 00000000..af7d70f3 --- /dev/null +++ b/src/utils/dlib.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2017-2019 J.F.Dockes + * + * License: GPL 2.1 + * + * This program 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 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef _DLIB_H_INCLUDED_ +#define _DLIB_H_INCLUDED_ + +/** Dynamic library functions */ + +#include + +extern void *dlib_open(const std::string& libname, int flags = 0); +extern void *dlib_sym(void *handle, const char *name); +extern void dlib_close(void *handle); +extern const char *dlib_error(); + +#endif /* _DLIB_H_INCLUDED_ */ diff --git a/src/windows/mkinstdir.sh b/src/windows/mkinstdir.sh index e99c349d..3a1383ba 100644 --- a/src/windows/mkinstdir.sh +++ b/src/windows/mkinstdir.sh @@ -46,12 +46,17 @@ LIBREVENGE=${RCLDEPS}libwpd/librevenge-0.0.1.jfd/ CHM=${RCLDEPS}pychm MISC=${RCLDEPS}misc LIBPFF=${RCLDEPS}pffinstall +ASPELL=${RCLDEPS}/aspell-0.60.7/aspell-installed # Where to copy the Qt Dlls from: QTBIN=C:/Qt/Qt5.8.0/5.8/mingw53_32/bin QTGCCBIN=C:/qt/Qt5.8.0/Tools/mingw530_32/bin/ -# Where to find libgcc_s_dw2-1.dll for progs which need it copied -MINGWBIN=$QTBIN + +# Where to find libgcc_s_dw2-1.dll et all for progs compiled with c:/MinGW +# (as opposed to the mingw bundled with qt +MINGWBIN=C:/MinGW/bin + + PATH=$MINGWBIN:$QTGCCBIN:$PATH export PATH @@ -158,7 +163,7 @@ copyrecoll() chkcp $RCL/python/recoll/recoll/rclconfig.py $FILTERS chkcp $RCL/python/recoll/recoll/conftree.py $FILTERS - rm -f $FILTERS/rclimg + rm -f $FILTERS/rclimg* chkcp $RCL/filters/* $FILTERS rm -f $FILTERS/rclimg $FILTERS/rclimg.py chkcp $RCLDEPS/rclimg/rclimg.exe $FILTERS @@ -273,6 +278,17 @@ copypff() chkcp $QTBIN/libwinpthread-1.dll $DEST } +copyaspell() +{ + DEST=$FILTERS + cp -rp $ASPELL $DEST || fatal "can't copy $ASPELL" + DEST=$DEST/aspell-installed/mingw32/bin + # Check that we do have an aspell.exe. + chkcp $ASPELL/mingw32/bin/aspell.exe $DEST + chkcp $MINGWBIN/libgcc_s_dw2-1.dll $DEST + chkcp $MINGWBIN/libstdc++-6.dll $DEST +} + for d in doc examples filters images translations; do test -d $DESTDIR/Share/$d || mkdir -p $DESTDIR/Share/$d || \ fatal mkdir $d failed @@ -290,6 +306,7 @@ test "$VERSION" = "$CFVERS" || echo Packaging version $CFVERS +copyaspell # copyrecoll must stay before copyqt so that windeployqt can do its thing copyrecoll copyqt diff --git a/src/windows/qmkrecoll/librecoll.pro b/src/windows/qmkrecoll/librecoll.pro index 7a1affa4..48a59a4a 100644 --- a/src/windows/qmkrecoll/librecoll.pro +++ b/src/windows/qmkrecoll/librecoll.pro @@ -96,6 +96,7 @@ SOURCES += \ ../../utils/conftree.cpp \ ../../utils/copyfile.cpp \ ../../utils/cpuconf.cpp \ +../../utils/dlib.cpp \ ../../utils/ecrontab.cpp \ ../../utils/utf8iter.cpp \ ../../utils/zlibut.cpp \ diff --git a/src/windows/recoll-setup.iss b/src/windows/recoll-setup.iss index d4214058..f2f0f7f3 100644 --- a/src/windows/recoll-setup.iss +++ b/src/windows/recoll-setup.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Recoll" -#define MyAppVersion "1.25.0-20190125-540140bd" +#define MyAppVersion "1.26.0-pre4-20191011-2491388e" #define MyAppPublisher "Recoll.org" #define MyAppURL "http://www.recoll.org" #define MyAppExeName "recoll.exe"