diff --git a/src/aspell/rclaspell.cpp b/src/aspell/rclaspell.cpp
index 0874118d..d08b6ad1 100644
--- a/src/aspell/rclaspell.cpp
+++ b/src/aspell/rclaspell.cpp
@@ -34,6 +34,8 @@
#include "rclaspell.h"
#include "debuglog.h"
+#include "ptmutex.h"
+
// Just a place where we keep the Aspell library entry points together
class AspellApi {
public:
@@ -61,6 +63,7 @@ public:
};
static AspellApi aapi;
+static PTMutexInit o_aapi_mutex;
#define NMTOPTR(NM, TP) \
if ((aapi.NM = TP dlsym(m_data->m_handle, #NM)) == 0) { \
@@ -111,6 +114,7 @@ Aspell::~Aspell()
bool Aspell::init(string &reason)
{
+ PTMutexLocker locker(o_aapi_mutex);
deleteZ(m_data);
// Language: we get this from the configuration, else from the NLS
@@ -227,7 +231,7 @@ bool Aspell::init(string &reason)
return true;
}
-bool Aspell::ok()
+bool Aspell::ok() const
{
return m_data != 0 && m_data->m_handle != 0;
}
diff --git a/src/aspell/rclaspell.h b/src/aspell/rclaspell.h
index 9f2db884..06032495 100644
--- a/src/aspell/rclaspell.h
+++ b/src/aspell/rclaspell.h
@@ -50,7 +50,7 @@ class Aspell {
~Aspell();
/** Check health */
- bool ok();
+ bool ok() const;
/** Find the aspell command and shared library, init function pointers */
bool init(string &reason);
diff --git a/src/common/rclinit.cpp b/src/common/rclinit.cpp
index c2ec8728..fdfa7747 100644
--- a/src/common/rclinit.cpp
+++ b/src/common/rclinit.cpp
@@ -29,6 +29,7 @@
#include "rclconfig.h"
#include "rclinit.h"
#include "pathut.h"
+#include "unac.h"
static const int catchedSigs[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM,
SIGUSR1, SIGUSR2};
@@ -99,6 +100,8 @@ RclConfig *recollinit(RclInitFlags flags,
// Make sure the locale charset is initialized (so that multiple
// threads don't try to do it at once).
config->getDefCharset();
+ // Init unac locking
+ unac_init_mt();
int flushmb;
if (config->getConfParam("idxflushmb", &flushmb) && flushmb > 0) {
diff --git a/src/index/beaglequeue.cpp b/src/index/beaglequeue.cpp
index b7e19e2d..56f6d0dc 100644
--- a/src/index/beaglequeue.cpp
+++ b/src/index/beaglequeue.cpp
@@ -172,8 +172,6 @@ public:
ifstream m_input;
};
-const string badtmpdirname = "/no/such/dir/really/can/exist";
-
// Initialize. Compute paths and create a temporary directory that will be
// used by internfile()
BeagleQueueIndexer::BeagleQueueIndexer(RclConfig *cnf, Rcl::Db *db,
diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp
index 516eb003..2014c9b9 100644
--- a/src/internfile/internfile.cpp
+++ b/src/internfile/internfile.cpp
@@ -51,20 +51,21 @@ using namespace std;
#include "pxattr.h"
#endif // RCL_USE_XATTR
-static const string stxtplain("text/plain");
+static const string cstr_stxtplain("text/plain");
// The internal path element separator. This can't be the same as the rcldb
// file to ipath separator : "|"
// We replace it with a control char if it comes out of a filter (ie:
// rclzip or rclchm can do this). If you want the SOH control char
// inside an ipath, you're out of luck (and a bit weird).
-static const string isep(":");
-static const char colon_repl = '\x01';
+static const string cstr_isep(":");
+
+static const char cchar_colon_repl = '\x01';
static string colon_hide(const string& in)
{
string out;
for (string::const_iterator it = in.begin(); it != in.end(); it++) {
- out += *it == ':' ? colon_repl : *it;
+ out += *it == ':' ? cchar_colon_repl : *it;
}
return out;
}
@@ -72,7 +73,7 @@ static string colon_restore(const string& in)
{
string out;
for (string::const_iterator it = in.begin(); it != in.end(); it++) {
- out += *it == colon_repl ? ':' : *it;
+ out += *it == cchar_colon_repl ? ':' : *it;
}
return out;
}
@@ -115,7 +116,7 @@ bool FileInterner::getEnclosing(const string &url, const string &ipath,
url.c_str(), eipath.c_str()));
if (eipath.empty())
return false;
- if ((colon = eipath.find_last_of(isep)) != string::npos) {
+ if ((colon = eipath.find_last_of(cstr_isep)) != string::npos) {
eipath.erase(colon);
} else {
eipath.erase();
@@ -365,12 +366,12 @@ void FileInterner::initcommon(RclConfig *cnf, int flags)
m_handlers.reserve(MAXHANDLERS);
for (unsigned int i = 0; i < MAXHANDLERS; i++)
m_tmpflgs[i] = false;
- m_targetMType = stxtplain;
+ m_targetMType = cstr_stxtplain;
}
// We used a single beagle cache object to access beagle data. We protect it
// against multiple thread access.
-static PTMutexInit o_lock;
+static PTMutexInit o_beagler_mutex;
FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf,
TempDir& td, int flags)
@@ -422,12 +423,12 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf,
string udi = it->second;
{
- PTMutexLocker locker(o_lock);
+ PTMutexLocker locker(o_beagler_mutex);
// Retrieve from our webcache (beagle data). The beagler
// object is created at the first call of this routine and
// deleted when the program exits.
- static BeagleQueueCache beagler(cnf);
- if (!beagler.getFromCache(udi, dotdoc, data)) {
+ static BeagleQueueCache o_beagler(cnf);
+ if (!o_beagler.getFromCache(udi, dotdoc, data)) {
LOGINFO(("FileInterner:: failed fetch from Beagle cache for [%s]\n",
udi.c_str()));
return;
@@ -564,14 +565,14 @@ static inline bool getKeyValue(const map& docdata,
// These defs are for the Dijon meta array. Rcl::Doc predefined field
// names are used where appropriate. In some cases, Rcl::Doc names are
// used inside the Dijon metadata (ex: origcharset)
-static const string keyau("author");
-static const string keycs("charset");
-static const string keyct("content");
-static const string keyds("description");
-static const string keyfn("filename");
-static const string keymd("modificationdate");
-static const string keymt("mimetype");
-static const string keytt("title");
+static const string cstr_keyau("author");
+static const string cstr_keycs("charset");
+static const string cstr_keyct("content");
+static const string cstr_keyds("description");
+static const string cstr_keyfn("filename");
+static const string cstr_keymd("modificationdate");
+static const string cstr_keymt("mimetype");
+static const string cstr_keytt("title");
bool FileInterner::dijontorcl(Rcl::Doc& doc)
{
@@ -585,21 +586,21 @@ bool FileInterner::dijontorcl(Rcl::Doc& doc)
for (map::const_iterator it = docdata.begin();
it != docdata.end(); it++) {
- if (it->first == keyct) {
+ if (it->first == cstr_keyct) {
doc.text = it->second;
- } else if (it->first == keymd) {
+ } else if (it->first == cstr_keymd) {
doc.dmtime = it->second;
} else if (it->first == Rcl::Doc::keyoc) {
doc.origcharset = it->second;
- } else if (it->first == keymt || it->first == keycs) {
+ } else if (it->first == cstr_keymt || it->first == cstr_keycs) {
// don't need/want these.
} else {
doc.meta[it->first] = it->second;
}
}
- if (doc.meta[Rcl::Doc::keyabs].empty() && !doc.meta[keyds].empty()) {
- doc.meta[Rcl::Doc::keyabs] = doc.meta[keyds];
- doc.meta.erase(keyds);
+ if (doc.meta[Rcl::Doc::keyabs].empty() && !doc.meta[cstr_keyds].empty()) {
+ doc.meta[Rcl::Doc::keyabs] = doc.meta[cstr_keyds];
+ doc.meta.erase(cstr_keyds);
}
return true;
}
@@ -635,21 +636,21 @@ void FileInterner::collectIpathAndMT(Rcl::Doc& doc) const
if (!ipathel.empty()) {
// We have a non-empty ipath
hasipath = true;
- getKeyValue(docdata, keymt, doc.mimetype);
- getKeyValue(docdata, keyfn, doc.utf8fn);
+ getKeyValue(docdata, cstr_keymt, doc.mimetype);
+ getKeyValue(docdata, cstr_keyfn, doc.utf8fn);
}
- doc.ipath += colon_hide(ipathel) + isep;
+ doc.ipath += colon_hide(ipathel) + cstr_isep;
} else {
- doc.ipath += isep;
+ doc.ipath += cstr_isep;
}
- getKeyValue(docdata, keyau, doc.meta[Rcl::Doc::keyau]);
- getKeyValue(docdata, keymd, doc.dmtime);
+ getKeyValue(docdata, cstr_keyau, doc.meta[Rcl::Doc::keyau]);
+ getKeyValue(docdata, cstr_keymd, doc.dmtime);
}
// Trim empty tail elements in ipath.
if (hasipath) {
LOGDEB2(("IPATH [%s]\n", doc.ipath.c_str()));
- string::size_type sit = doc.ipath.find_last_not_of(isep);
+ string::size_type sit = doc.ipath.find_last_not_of(cstr_isep);
if (sit == string::npos)
doc.ipath.erase();
else if (sit < doc.ipath.length() -1)
@@ -681,8 +682,8 @@ int FileInterner::addHandler()
{
const map& docdata = m_handlers.back()->get_meta_data();
string charset, mimetype;
- getKeyValue(docdata, keycs, charset);
- getKeyValue(docdata, keymt, mimetype);
+ getKeyValue(docdata, cstr_keycs, charset);
+ getKeyValue(docdata, cstr_keymt, mimetype);
LOGDEB(("FileInterner::addHandler: next_doc is %s\n", mimetype.c_str()));
@@ -690,7 +691,7 @@ int FileInterner::addHandler()
// general), we're done decoding. If we hit text/plain, we're done
// in any case
if (!stringicmp(mimetype, m_targetMType) ||
- !stringicmp(mimetype, stxtplain)) {
+ !stringicmp(mimetype, cstr_stxtplain)) {
m_reachedMType = mimetype;
LOGDEB1(("FileInterner::addHandler: target reached\n"));
return ADD_BREAK;
@@ -723,7 +724,7 @@ int FileInterner::addHandler()
const string *txt = &ns;
{
map::const_iterator it;
- it = docdata.find(keyct);
+ it = docdata.find(cstr_keyct);
if (it != docdata.end())
txt = &it->second;
}
@@ -795,7 +796,7 @@ FileInterner::Status FileInterner::internfile(Rcl::Doc& doc, const string& ipath
int vipathidx = 0;
if (!ipath.empty()) {
vector lipath;
- stringToTokens(ipath, lipath, isep, true);
+ stringToTokens(ipath, lipath, cstr_isep, true);
for (vector::iterator it = lipath.begin();
it != lipath.end(); it++) {
*it = colon_restore(*it);
diff --git a/src/internfile/mh_mbox.cpp b/src/internfile/mh_mbox.cpp
index b3d21ce1..5d08b1b8 100644
--- a/src/internfile/mh_mbox.cpp
+++ b/src/internfile/mh_mbox.cpp
@@ -53,7 +53,7 @@ public:
private: FILE **m_fpp;
};
-static PTMutexInit o_mutex;
+static PTMutexInit o_mcache_mutex;
/**
* Handles a cache for message numbers to offset translations. Permits direct
@@ -86,7 +86,7 @@ public:
LOGDEB0(("MboxCache::get_offsets: init failed\n"));
return -1;
}
- PTMutexLocker locker(o_mutex);
+ PTMutexLocker locker(o_mcache_mutex);
string fn = makefilename(udi);
FILE *fp = 0;
if ((fp = fopen(fn.c_str(), "r")) == 0) {
@@ -133,7 +133,7 @@ public:
return;
if (fsize < m_minfsize)
return;
- PTMutexLocker locker(o_mutex);
+ PTMutexLocker locker(o_mcache_mutex);
string fn = makefilename(udi);
FILE *fp;
if ((fp = fopen(fn.c_str(), "w")) == 0) {
@@ -163,7 +163,7 @@ public:
// Check state, possibly initialize
bool ok(RclConfig *config) {
- PTMutexLocker locker(o_mutex);
+ PTMutexLocker locker(o_mcache_mutex);
if (m_minfsize == -1)
return false;
if (!m_ok) {
@@ -224,9 +224,9 @@ private:
const size_t MboxCache::o_b1size = 1024;
-static class MboxCache mcache;
+static class MboxCache o_mcache;
-static const string keyquirks("mhmboxquirks");
+static const string cstr_keyquirks("mhmboxquirks");
MimeHandlerMbox::~MimeHandlerMbox()
{
@@ -271,7 +271,7 @@ bool MimeHandlerMbox::set_document_file(const string &fn)
// Check for location-based quirks:
string quirks;
- if (m_config && m_config->getConfParam(keyquirks, quirks)) {
+ if (m_config && m_config->getConfParam(cstr_keyquirks, quirks)) {
if (quirks == "tbird") {
LOGDEB(("MimeHandlerMbox: setting quirks TBIRD\n"));
m_quirks |= MBOXQUIRK_TBIRD;
@@ -358,6 +358,20 @@ static const char *miniTbirdFrom = "^From $";
static regex_t fromregex;
static regex_t minifromregex;
static bool regcompiled;
+static PTMutexInit o_regex_mutex;
+
+static void compileregexes()
+{
+ PTMutexLocker locker(o_regex_mutex);
+ // As the initial test of regcompiled is unprotected the value may
+ // have changed while we were waiting for the lock. Test again now
+ // that we are alone.
+ if (regcompiled)
+ return;
+ regcomp(&fromregex, frompat, REG_NOSUB|REG_EXTENDED);
+ regcomp(&minifromregex, miniTbirdFrom, REG_NOSUB|REG_EXTENDED);
+ regcompiled = true;
+}
bool MimeHandlerMbox::next_document()
{
@@ -383,9 +397,7 @@ bool MimeHandlerMbox::next_document()
mtarg = -1;
if (!regcompiled) {
- regcomp(&fromregex, frompat, REG_NOSUB|REG_EXTENDED);
- regcomp(&minifromregex, miniTbirdFrom, REG_NOSUB|REG_EXTENDED);
- regcompiled = true;
+ compileregexes();
}
// If we are called to retrieve a specific message, seek to bof
@@ -403,7 +415,7 @@ bool MimeHandlerMbox::next_document()
LOGDEB0(("MimeHandlerMbox::next_doc: mtarg %d m_udi[%s]\n",
mtarg, m_udi.c_str()));
if (!m_udi.empty() &&
- (off = mcache.get_offset(m_config, m_udi, mtarg)) >= 0 &&
+ (off = o_mcache.get_offset(m_config, m_udi, mtarg)) >= 0 &&
fseeko(fp, (off_t)off, SEEK_SET) >= 0 &&
fgets(line, LL, fp) &&
(!regexec(&fromregex, line, 0, 0, 0) ||
@@ -492,7 +504,7 @@ bool MimeHandlerMbox::next_document()
LOGDEB2(("MimeHandlerMbox::next: eof hit\n"));
m_havedoc = false;
if (!m_udi.empty() && storeoffsets) {
- mcache.put_offsets(m_config, m_udi, m_fsize, m_offsets);
+ o_mcache.put_offsets(m_config, m_udi, m_fsize, m_offsets);
}
}
return msgtxt.empty() ? false : true;
diff --git a/src/query/reslistpager.cpp b/src/query/reslistpager.cpp
index 1f04aa5f..f50fa074 100644
--- a/src/query/reslistpager.cpp
+++ b/src/query/reslistpager.cpp
@@ -34,12 +34,14 @@ using std::endl;
#include "plaintorich.h"
#include "mimehandler.h"
-// Default highlighter
+// Default highlighter. No need for locking, this is query-only.
+static const string cstr_hlfontcolor("");
+static const string cstr_hlendfont("");
class PlainToRichHtReslist : public PlainToRich {
public:
virtual ~PlainToRichHtReslist() {}
- virtual string startMatch() {return string("");}
- virtual string endMatch() {return string("");}
+ virtual string startMatch() {return cstr_hlfontcolor;}
+ virtual string endMatch() {return cstr_hlendfont;}
};
static PlainToRichHtReslist g_hiliter;
@@ -406,10 +408,10 @@ string ResListPager::detailsLink()
const string &ResListPager::parFormat()
{
- static const string format("
"
- "%R %S %L %T
"
- "%M %D %U
"
- "%A %K");
- return format;
+ static const string cstr_format("
"
+ "%R %S %L %T
"
+ "%M %D %U
"
+ "%A %K");
+ return cstr_format;
}
diff --git a/src/rcldb/rcldb.cpp b/src/rcldb/rcldb.cpp
index 59ada1ff..96fb24e5 100644
--- a/src/rcldb/rcldb.cpp
+++ b/src/rcldb/rcldb.cpp
@@ -51,6 +51,7 @@ using namespace std;
#include "md5.h"
#include "rclversion.h"
#include "cancelcheck.h"
+#include "ptmutex.h"
#ifndef MAX
#define MAX(A,B) (A>B?A:B)
@@ -61,8 +62,8 @@ using namespace std;
// Recoll index format version is stored in user metadata. When this change,
// we can't open the db and will have to reindex.
-static const string RCL_IDX_VERSION_KEY("RCL_IDX_VERSION_KEY");
-static const string RCL_IDX_VERSION("1");
+static const string cstr_RCL_IDX_VERSION_KEY("RCL_IDX_VERSION_KEY");
+static const string cstr_RCL_IDX_VERSION("1");
// This is the word position offset at which we index the body text
// (abstract, keywords, etc.. are stored before this)
@@ -79,7 +80,7 @@ const string end_of_field_term = "XXND";
// This is used as a marker inside the abstract frag lists, but
// normally doesn't remain in final output (which is built with a
// custom sep. by our caller).
-static const string ellipsis("...");
+static const string cstr_ellipsis("...");
string version_string(){
return string("Recoll ") + string(rclversionstr) + string(" + Xapian ") +
@@ -88,12 +89,12 @@ string version_string(){
// Synthetic abstract marker (to discriminate from abstract actually
// found in document)
-static const string rclSyntAbs("?!#@");
+static const string cstr_syntAbs("?!#@");
// Only ONE field name inside the index data record differs from the
// Rcl::Doc ones: caption<->title, for a remnant of compatibility with
// omega
-static const string keycap("caption");
+static const string cstr_keycap("caption");
// Static/Default table for field->prefix/weight translation.
// This is logically const after initialization. Can't use a
@@ -106,8 +107,16 @@ static const string keycap("caption");
// suppressed.
static map fldToTraits;
+static PTMutexInit o_fldToTraits_mutex;
+
static void initFldToTraits()
{
+ PTMutexLocker locker(o_fldToTraits_mutex);
+ // As we perform non-locked testing of initialization, check again with
+ // the lock held
+ if (fldToTraits.size())
+ return;
+
// Can't remember why "abstract" is indexed without a prefix
// (result: it's indexed twice actually). Maybe I'll dare change
// this one day
@@ -116,7 +125,7 @@ static void initFldToTraits()
fldToTraits["ext"] = FieldTraits("XE");
fldToTraits[Doc::keyfn] = FieldTraits("XSFN");
- fldToTraits[keycap] = FieldTraits("S");
+ fldToTraits[cstr_keycap] = FieldTraits("S");
fldToTraits[Doc::keytt] = FieldTraits("S");
fldToTraits["subject"] = FieldTraits("S");
@@ -189,14 +198,14 @@ bool Db::Native::dbDataToRclDoc(Xapian::docid docid, std::string &data,
parms.get(Doc::keyfmt, doc.fmtime);
parms.get(Doc::keydmt, doc.dmtime);
parms.get(Doc::keyoc, doc.origcharset);
- parms.get(keycap, doc.meta[Doc::keytt]);
+ parms.get(cstr_keycap, doc.meta[Doc::keytt]);
parms.get(Doc::keykw, doc.meta[Doc::keykw]);
parms.get(Doc::keyabs, doc.meta[Doc::keyabs]);
// Possibly remove synthetic abstract indicator (if it's there, we
// used to index the beginning of the text as abstract).
doc.syntabs = false;
- if (doc.meta[Doc::keyabs].find(rclSyntAbs) == 0) {
- doc.meta[Doc::keyabs] = doc.meta[Doc::keyabs].substr(rclSyntAbs.length());
+ if (doc.meta[Doc::keyabs].find(cstr_syntAbs) == 0) {
+ doc.meta[Doc::keyabs] = doc.meta[Doc::keyabs].substr(cstr_syntAbs.length());
doc.syntabs = true;
}
parms.get(Doc::keyipt, doc.ipath);
@@ -417,7 +426,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query)
} else if (ii > (unsigned int)ipos &&
ii < (unsigned int)ipos + qtrmwrdcnt) {
sparseDoc[ii] = occupiedmarker;
- } else if (!sparseDoc[ii].compare(ellipsis)) {
+ } else if (!sparseDoc[ii].compare(cstr_ellipsis)) {
// For an empty slot, the test has a side
// effect of inserting an empty string which
// is what we want
@@ -429,7 +438,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query)
// empty string here, we really want an empty slot,
// use find()
if (sparseDoc.find(sto+1) == sparseDoc.end()) {
- sparseDoc[sto+1] = ellipsis;
+ sparseDoc[sto+1] = cstr_ellipsis;
}
// Limit to allocated occurences and total size
@@ -531,7 +540,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query)
if (!incjk || (incjk && !newcjk))
chunk += " ";
incjk = newcjk;
- if (it->second == ellipsis) {
+ if (it->second == cstr_ellipsis) {
vabs.push_back(chunk);
chunk.clear();
} else {
@@ -612,8 +621,8 @@ bool Db::open(OpenMode mode, OpenError *error)
// If db is empty, write the data format version at once
// to avoid stupid error messages:
if (m_ndb->xwdb.get_doccount() == 0)
- m_ndb->xwdb.set_metadata(RCL_IDX_VERSION_KEY,
- RCL_IDX_VERSION);
+ m_ndb->xwdb.set_metadata(cstr_RCL_IDX_VERSION_KEY,
+ cstr_RCL_IDX_VERSION);
m_ndb->m_iswritable = true;
// We open a readonly object in all cases (possibly in
// addition to the r/w one) because some operations
@@ -650,11 +659,11 @@ bool Db::open(OpenMode mode, OpenError *error)
// Check index format version. Must not try to check a just created or
// truncated db
if (mode != DbTrunc && m_ndb->xdb().get_doccount() > 0) {
- string version = m_ndb->xdb().get_metadata(RCL_IDX_VERSION_KEY);
- if (version.compare(RCL_IDX_VERSION)) {
+ string version = m_ndb->xdb().get_metadata(cstr_RCL_IDX_VERSION_KEY);
+ if (version.compare(cstr_RCL_IDX_VERSION)) {
m_ndb->m_noversionwrite = true;
LOGERR(("Rcl::Db::open: file index [%s], software [%s]\n",
- version.c_str(), RCL_IDX_VERSION.c_str()));
+ version.c_str(), cstr_RCL_IDX_VERSION.c_str()));
throw Xapian::DatabaseError("Recoll index version mismatch",
"", "");
}
@@ -693,7 +702,7 @@ bool Db::i_close(bool final)
bool w = m_ndb->m_iswritable;
if (w) {
if (!m_ndb->m_noversionwrite)
- m_ndb->xwdb.set_metadata(RCL_IDX_VERSION_KEY, RCL_IDX_VERSION);
+ m_ndb->xwdb.set_metadata(cstr_RCL_IDX_VERSION_KEY, cstr_RCL_IDX_VERSION);
LOGDEB(("Rcl::Db:close: xapian will close. May take some time\n"));
}
// Used to do a flush here. Cant see why it should be necessary.
@@ -952,7 +961,7 @@ void Db::setAbstractParams(int idxtrunc, int syntlen, int syntctxlen)
}
static const int MB = 1024 * 1024;
-static const string nc("\n\r\x0c");
+static const string cstr_nc("\n\r\x0c");
#define RECORD_APPEND(R, NM, VAL) {R += NM + "=" + VAL + "\n";}
@@ -1168,13 +1177,13 @@ bool Db::addOrUpdate(const string &udi, const string &parent_udi,
if (doc.meta[Doc::keytt].empty())
doc.meta[Doc::keytt] = doc.utf8fn;
doc.meta[Doc::keytt] =
- neutchars(truncate_to_word(doc.meta[Doc::keytt], 150), nc);
+ neutchars(truncate_to_word(doc.meta[Doc::keytt], 150), cstr_nc);
if (!doc.meta[Doc::keytt].empty())
- RECORD_APPEND(record, keycap, doc.meta[Doc::keytt]);
+ RECORD_APPEND(record, cstr_keycap, doc.meta[Doc::keytt]);
trimstring(doc.meta[Doc::keykw], " \t\r\n");
doc.meta[Doc::keykw] =
- neutchars(truncate_to_word(doc.meta[Doc::keykw], 300), nc);
+ neutchars(truncate_to_word(doc.meta[Doc::keykw], 300), cstr_nc);
if (!doc.meta[Doc::keykw].empty())
RECORD_APPEND(record, Doc::keykw, doc.meta[Doc::keykw]);
@@ -1189,12 +1198,12 @@ bool Db::addOrUpdate(const string &udi, const string &parent_udi,
if (doc.meta[Doc::keyabs].empty()) {
syntabs = true;
if (!doc.text.empty())
- doc.meta[Doc::keyabs] = rclSyntAbs +
- neutchars(truncate_to_word(doc.text, m_idxAbsTruncLen), nc);
+ doc.meta[Doc::keyabs] = cstr_syntAbs +
+ neutchars(truncate_to_word(doc.text, m_idxAbsTruncLen), cstr_nc);
} else {
doc.meta[Doc::keyabs] =
neutchars(truncate_to_word(doc.meta[Doc::keyabs], m_idxAbsTruncLen),
- nc);
+ cstr_nc);
}
if (!doc.meta[Doc::keyabs].empty())
RECORD_APPEND(record, Doc::keyabs, doc.meta[Doc::keyabs]);
@@ -1205,7 +1214,7 @@ bool Db::addOrUpdate(const string &udi, const string &parent_udi,
string nm = m_config->fieldCanon(*it);
if (!doc.meta[*it].empty()) {
string value =
- neutchars(truncate_to_word(doc.meta[*it], 150), nc);
+ neutchars(truncate_to_word(doc.meta[*it], 150), cstr_nc);
RECORD_APPEND(record, nm, value);
}
}
@@ -1611,8 +1620,8 @@ static void addPrefix(list& terms, const string& prefix)
// Characters that can begin a wildcard or regexp expression. We use skipto
// to begin the allterms search with terms that begin with the portion of
// the input string prior to these chars.
-const string wildSpecChars = "*?[";
-const string regSpecChars = "(.[{";
+const string cstr_wildSpecChars = "*?[";
+const string cstr_regSpecChars = "(.[{";
// Find all index terms that match a wildcard or regular expression
bool Db::termMatch(MatchType typ, const string &lang,
@@ -1639,7 +1648,7 @@ bool Db::termMatch(MatchType typ, const string &lang,
LOGERR(("Db::termMatch: unac failed for [%s]\n", root.c_str()));
return false;
}
- string nochars = typ == ET_WILD ? wildSpecChars : regSpecChars;
+ string nochars = typ == ET_WILD ? cstr_wildSpecChars : cstr_regSpecChars;
string prefix;
if (!field.empty()) {
@@ -1852,7 +1861,7 @@ bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract)
for (vector::const_iterator it = vab.begin();
it != vab.end(); it++) {
abstract.append(*it);
- abstract.append(ellipsis);
+ abstract.append(cstr_ellipsis);
}
return m_reason.empty() ? true : false;
}
diff --git a/src/rcldb/rclquery.cpp b/src/rcldb/rclquery.cpp
index d7e6e6e1..e46d7260 100644
--- a/src/rcldb/rclquery.cpp
+++ b/src/rcldb/rclquery.cpp
@@ -39,16 +39,17 @@
namespace Rcl {
#endif
+static const string cstr_keycap("caption");
+static const string cstr_keydmtime("dmtime");
+
// Field names inside the index data record may differ from the rcldoc ones
// (esp.: caption / title)
static const string& docfToDatf(const string& df)
{
- static const string keycap("caption");
- static const string keydmtime("dmtime");
if (!df.compare(Doc::keytt)) {
- return keycap;
+ return cstr_keycap;
} else if (!df.compare(Doc::keymt)) {
- return keydmtime;
+ return cstr_keydmtime;
} else {
return df;
}
diff --git a/src/rcldb/searchdata.cpp b/src/rcldb/searchdata.cpp
index ad4b39d0..f7e3e0eb 100644
--- a/src/rcldb/searchdata.cpp
+++ b/src/rcldb/searchdata.cpp
@@ -860,13 +860,13 @@ bool StringToXapianQ::processUserString(const string &iq,
return true;
}
-static const string nullstemlang;
+static const string cstr_null;
// Translate a simple OR, AND, or EXCL search clause.
bool SearchDataClauseSimple::toNativeQuery(Rcl::Db &db, void *p,
const string& stemlang)
{
- const string& l_stemlang = (m_modifiers&SDCM_NOSTEMMING)? nullstemlang:
+ const string& l_stemlang = (m_modifiers&SDCM_NOSTEMMING)? cstr_null:
stemlang;
m_terms.clear();
@@ -945,7 +945,7 @@ bool SearchDataClauseFilename::toNativeQuery(Rcl::Db &db, void *p,
bool SearchDataClauseDist::toNativeQuery(Rcl::Db &db, void *p,
const string& stemlang)
{
- const string& l_stemlang = (m_modifiers&SDCM_NOSTEMMING)? nullstemlang:
+ const string& l_stemlang = (m_modifiers&SDCM_NOSTEMMING)? cstr_null:
stemlang;
LOGDEB(("SearchDataClauseDist::toNativeQuery\n"));
m_terms.clear();
diff --git a/src/rcldb/stemdb.cpp b/src/rcldb/stemdb.cpp
index e5275c79..4b506a04 100644
--- a/src/rcldb/stemdb.cpp
+++ b/src/rcldb/stemdb.cpp
@@ -38,21 +38,21 @@ namespace Rcl {
namespace StemDb {
-static const string stemdirstem = "stem_";
+static const string cstr_stemdirstem = "stem_";
/// Compute name of stem db for given base database and language
static string stemdbname(const string& dbdir, const string& lang)
{
- return path_cat(dbdir, stemdirstem + lang);
+ return path_cat(dbdir, cstr_stemdirstem + lang);
}
list getLangs(const string& dbdir)
{
- string pattern = stemdirstem + "*";
+ string pattern = cstr_stemdirstem + "*";
list dirs = path_dirglob(dbdir, pattern);
for (list::iterator it = dirs.begin(); it != dirs.end(); it++) {
*it = path_basename(*it);
- *it = it->substr(stemdirstem.length(), string::npos);
+ *it = it->substr(cstr_stemdirstem.length(), string::npos);
}
return dirs;
}
diff --git a/src/unac/unac.c b/src/unac/unac.c
index 69d1ee90..92c8dea7 100644
--- a/src/unac/unac.c
+++ b/src/unac/unac.c
@@ -36,6 +36,7 @@
#include
#include
#endif /* HAVE_VSNPRINTF */
+#include
#include "unac.h"
#include "unac_version.h"
@@ -10555,8 +10556,6 @@ int unacfold_string_utf16(const char* in, size_t in_length,
outp, out_lengthp, 1);
}
-#define MAXOUT 1024
-
static int convert(const char* from, const char* to,
const char* in, size_t in_length,
char** outp, size_t* out_lengthp);
@@ -10564,6 +10563,14 @@ static int convert(const char* from, const char* to,
static const char *utf16be = "UTF-16BE";
static iconv_t u8tou16_cd = (iconv_t)-1;
static iconv_t u16tou8_cd = (iconv_t)-1;
+static pthread_mutex_t o_unac_mutex;
+static int unac_mutex_is_init;
+// Call this or take your chances with the auto init.
+void unac_init_mt()
+{
+ pthread_mutex_init(&o_unac_mutex, 0);
+ unac_mutex_is_init = 1;
+}
/*
* Convert buffer containing string encoded in charset into
@@ -10576,6 +10583,7 @@ static int convert(const char* from, const char* to,
const char* in, size_t in_length,
char** outp, size_t* out_lengthp)
{
+ int ret = -1;
iconv_t cd;
char* out;
size_t out_remain;
@@ -10584,6 +10592,15 @@ static int convert(const char* from, const char* to,
int from_utf16, from_utf8, to_utf16, to_utf8, u8tou16, u16tou8;
const char space[] = { 0x00, 0x20 };
+ /* Note: better call explicit unac_init_mt() before starting threads than
+ rely on this.
+ */
+ if (unac_mutex_is_init == 0) {
+ pthread_mutex_init(&o_unac_mutex, 0);
+ unac_mutex_is_init = 1;
+ }
+ pthread_mutex_lock(&o_unac_mutex);
+
if (!strcmp(utf16be, from)) {
from_utf8 = 0;
from_utf16 = 1;
@@ -10614,7 +10631,7 @@ static int convert(const char* from, const char* to,
/* *outp still valid, no freeing */
if(debug_level >= UNAC_DEBUG_LOW)
DEBUG("realloc %d bytes failed\n", out_size+1);
- return -1;
+ goto out;
}
} else {
/* +1 for null */
@@ -10622,7 +10639,7 @@ static int convert(const char* from, const char* to,
if(out == 0) {
if(debug_level >= UNAC_DEBUG_LOW)
DEBUG("malloc %d bytes failed\n", out_size+1);
- return -1;
+ goto out;
}
}
out_remain = out_size;
@@ -10631,7 +10648,7 @@ static int convert(const char* from, const char* to,
if (u8tou16) {
if (u8tou16_cd == (iconv_t)-1) {
if((u8tou16_cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
} else {
iconv(u8tou16_cd, 0, 0, 0, 0);
@@ -10640,7 +10657,7 @@ static int convert(const char* from, const char* to,
} else if (u16tou8) {
if (u16tou8_cd == (iconv_t)-1) {
if((u16tou8_cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
} else {
iconv(u16tou8_cd, 0, 0, 0, 0);
@@ -10648,7 +10665,7 @@ static int convert(const char* from, const char* to,
cd = u16tou8_cd;
} else {
if((cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
}
@@ -10682,7 +10699,7 @@ static int convert(const char* from, const char* to,
if(errno == E2BIG)
/* fall thru to the E2BIG case below */;
else
- return -1;
+ goto out;
} else {
/* The offending character was replaced by a SPACE, skip it. */
in += 2;
@@ -10691,7 +10708,7 @@ static int convert(const char* from, const char* to,
break;
}
} else {
- return -1;
+ goto out;
}
case E2BIG:
{
@@ -10711,7 +10728,7 @@ static int convert(const char* from, const char* to,
DEBUG("realloc %d bytes failed\n", out_size+1);
free(saved);
*outp = 0;
- return -1;
+ goto out;
}
}
out = out_base + length;
@@ -10719,7 +10736,7 @@ static int convert(const char* from, const char* to,
}
break;
default:
- return -1;
+ goto out;
break;
}
}
@@ -10732,7 +10749,10 @@ static int convert(const char* from, const char* to,
*out_lengthp = out - out_base;
(*outp)[*out_lengthp] = '\0';
- return 0;
+ ret = 0;
+out:
+ pthread_mutex_unlock(&o_unac_mutex);
+ return ret;
}
int unacmaybefold_string(const char* charset,
diff --git a/src/unac/unac.h b/src/unac/unac.h
index c447c8a1..83fb1fea 100644
--- a/src/unac/unac.h
+++ b/src/unac/unac.h
@@ -113,6 +113,9 @@ int unacfold_string(const char* charset,
const char* in, size_t in_length,
char** out, size_t* out_length);
+/* To be called before starting threads in mt programs */
+void unac_init_mt();
+
/*
* Return unac version number.
*/
diff --git a/src/utils/ptmutex.cpp b/src/utils/ptmutex.cpp
new file mode 100644
index 00000000..7e6e2e87
--- /dev/null
+++ b/src/utils/ptmutex.cpp
@@ -0,0 +1,106 @@
+/* Copyright (C) 2004 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.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+//
+// Small test program to evaluate the cost of using mutex locks: calls
+// to methods doing a small (150 bytes) base64 encoding job + string
+// manips, with and without locking. The performance cost is
+// negligible on all machines I tested (around 0.3% to 2% depending on
+// the system and machine), but not inexistent, you would not want
+// this in a tight loop.
+
+#include
+#include
+#include
+
+#include
+using namespace std;
+
+#include "ptmutex.h"
+#include "base64.h"
+
+static char *thisprog;
+static char usage [] =
+"ptmutex [-l] count\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_l 0x2
+
+static const string convertbuffer =
+ "* The recoll GUI program sometimes crashes when running a query while\
+ the indexing thread is active. Possible workarounds:";
+
+static PTMutexInit o_lock;
+void workerlock(string& out)
+{
+ PTMutexLocker locker(o_lock);
+ base64_encode(convertbuffer, out);
+}
+
+void workernolock(string& out)
+{
+ base64_encode(convertbuffer, out);
+}
+
+int main(int argc, char **argv)
+{
+ int count = 0;
+ thisprog = argv[0];
+ argc--; argv++;
+
+ while (argc > 0 && **argv == '-') {
+ (*argv)++;
+ if (!(**argv))
+ /* Cas du "adb - core" */
+ Usage();
+ while (**argv)
+ switch (*(*argv)++) {
+ case 'l': op_flags |= OPT_l; break;
+ default: Usage(); break;
+ }
+ b1: argc--; argv++;
+ }
+
+ if (argc != 1)
+ Usage();
+ count = atoi(*argv++);argc--;
+
+ if (op_flags & OPT_l) {
+ fprintf(stderr, "Looping %d, locking\n", count);
+ for (int i = 0; i < count; i++) {
+ string s;
+ workerlock(s);
+ }
+ } else {
+ fprintf(stderr, "Looping %d, no locking\n", count);
+ for (int i = 0; i < count; i++) {
+ string s;
+ workernolock(s);
+ }
+ }
+ exit(0);
+}
+
diff --git a/src/utils/pxattr.cpp b/src/utils/pxattr.cpp
index 166fa81b..83e1aab1 100644
--- a/src/utils/pxattr.cpp
+++ b/src/utils/pxattr.cpp
@@ -405,7 +405,7 @@ list(int fd, const string& path, vector* names, flags flags, nspace dom)
return true;
}
-static const string nullstring("");
+static const string cstr_nullstring("");
bool get(const string& path, const string& _name, string *value,
flags flags, nspace dom)
@@ -414,7 +414,7 @@ bool get(const string& path, const string& _name, string *value,
}
bool get(int fd, const string& _name, string *value, flags flags, nspace dom)
{
- return get(fd, nullstring, _name, value, flags, dom);
+ return get(fd, cstr_nullstring, _name, value, flags, dom);
}
bool set(const string& path, const string& _name, const string& value,
flags flags, nspace dom)
@@ -424,7 +424,7 @@ bool set(const string& path, const string& _name, const string& value,
bool set(int fd, const string& _name, const string& value,
flags flags, nspace dom)
{
- return set(fd, nullstring, _name, value, flags, dom);
+ return set(fd, cstr_nullstring, _name, value, flags, dom);
}
bool del(const string& path, const string& _name, flags flags, nspace dom)
{
@@ -432,7 +432,7 @@ bool del(const string& path, const string& _name, flags flags, nspace dom)
}
bool del(int fd, const string& _name, flags flags, nspace dom)
{
- return del(fd, nullstring, _name, flags, dom);
+ return del(fd, cstr_nullstring, _name, flags, dom);
}
bool list(const string& path, vector* names, flags flags, nspace dom)
{
@@ -440,17 +440,17 @@ bool list(const string& path, vector* names, flags flags, nspace dom)
}
bool list(int fd, vector* names, flags flags, nspace dom)
{
- return list(fd, nullstring, names, flags, dom);
+ return list(fd, cstr_nullstring, names, flags, dom);
}
-static const string userstring("user.");
+static const string cstr_userstring("user.");
bool sysname(nspace dom, const string& pname, string* sname)
{
if (dom != PXATTR_USER) {
errno = EINVAL;
return false;
}
- *sname = userstring + pname;
+ *sname = cstr_userstring + pname;
return true;
}
@@ -460,7 +460,7 @@ bool pxname(nspace dom, const string& sname, string* pname)
errno = EINVAL;
return false;
}
- *pname = sname.substr(userstring.length());
+ *pname = sname.substr(cstr_userstring.length());
return true;
}
diff --git a/src/utils/smallut.cpp b/src/utils/smallut.cpp
index af92128a..4b91dc04 100644
--- a/src/utils/smallut.cpp
+++ b/src/utils/smallut.cpp
@@ -428,7 +428,7 @@ void neutchars(const string &str, string &out, const string& chars)
* if reasonably possible. Note: we could also use textsplit, stopping when
* we have enough, this would be cleanly utf8-aware but would remove
* punctuation */
-static const string SEPAR = " \t\n\r-:.;,/[]{}";
+static const string cstr_SEPAR = " \t\n\r-:.;,/[]{}";
string truncate_to_word(const string &input, string::size_type maxlen)
{
string output;
@@ -436,7 +436,7 @@ string truncate_to_word(const string &input, string::size_type maxlen)
output = input;
} else {
output = input.substr(0, maxlen);
- string::size_type space = output.find_last_of(SEPAR);
+ string::size_type space = output.find_last_of(cstr_SEPAR);
// Original version only truncated at space if space was found after
// maxlen/2. But we HAVE to truncate at space, else we'd need to do
// utf8 stuff to avoid truncating at multibyte char. In any case,
@@ -676,6 +676,9 @@ static void gettime(int, struct m_timespec *ts)
}
///// End system interface
+// Note: this not protected against multithread access and not reentrant, but
+// this is mostly debug code, and it won't crash, just show bad results. Also
+// the frozen thing is not used that much
static m_timespec frozen_tv;
void Chrono::refnow()
{
diff --git a/src/utils/transcode.cpp b/src/utils/transcode.cpp
index fcc08f4b..35ddd7fd 100644
--- a/src/utils/transcode.cpp
+++ b/src/utils/transcode.cpp
@@ -29,15 +29,21 @@ using std::string;
#include "transcode.h"
#include "debuglog.h"
-
+#include "ptmutex.h"
#ifdef RCL_ICONV_INBUF_CONST
#define ICV_P2_TYPE const char**
#else
#define ICV_P2_TYPE char**
#endif
-// We gain approximately 28% exec time for word at a time conversions by
+// We gain approximately 25% exec time for word at a time conversions by
// caching the iconv_open thing.
+//
+// We may also lose some concurrency on multiproc because of the
+// necessary locking, but we only have one processing-intensive
+// possible thread for now (the indexing one), so this is probably not
+// an issue (and could be worked around with a slightly more
+// sohisticated approach).
#define ICONV_CACHE_OPEN
bool transcode(const string &in, string &out, const string &icode,
@@ -48,6 +54,8 @@ bool transcode(const string &in, string &out, const string &icode,
static iconv_t ic = (iconv_t)-1;
static string cachedicode;
static string cachedocode;
+ static PTMutexInit o_cachediconv_mutex;
+ PTMutexLocker locker(o_cachediconv_mutex);
#else
iconv_t ic;
#endif
@@ -163,13 +171,14 @@ using namespace std;
// Repeatedly transcode a small string for timing measurements
static const string testword("\xc3\xa9\x6c\x69\x6d\x69\x6e\xc3\xa9\xc3\xa0");
-// Without cache 10e6 reps on macpro -> 1.88 S
-// With cache -> 1.56
+// Without cache 10e6 reps on y -> 6.68
+// With cache -> 4.73
+// With cache and lock -> 4.9
void looptest()
{
cout << testword << endl;
string out;
- for (int i = 0; i < 1000*1000; i++) {
+ for (int i = 0; i < 10*1000*1000; i++) {
if (!transcode(testword, out, "UTF-8", "UTF-16BE")) {
cerr << "Transcode failed" << endl;
break;
@@ -184,7 +193,7 @@ int main(int argc, char **argv)
exit(0);
#endif
if (argc != 5) {
- cerr << "Usage: trcsguess ifilename icode ofilename ocode" << endl;
+ cerr << "Usage: transcode ifilename icode ofilename ocode" << endl;
exit(1);
}
const string ifilename = argv[1];
diff --git a/unac/unac.c b/unac/unac.c
index 69d1ee90..92c8dea7 100644
--- a/unac/unac.c
+++ b/unac/unac.c
@@ -36,6 +36,7 @@
#include
#include
#endif /* HAVE_VSNPRINTF */
+#include
#include "unac.h"
#include "unac_version.h"
@@ -10555,8 +10556,6 @@ int unacfold_string_utf16(const char* in, size_t in_length,
outp, out_lengthp, 1);
}
-#define MAXOUT 1024
-
static int convert(const char* from, const char* to,
const char* in, size_t in_length,
char** outp, size_t* out_lengthp);
@@ -10564,6 +10563,14 @@ static int convert(const char* from, const char* to,
static const char *utf16be = "UTF-16BE";
static iconv_t u8tou16_cd = (iconv_t)-1;
static iconv_t u16tou8_cd = (iconv_t)-1;
+static pthread_mutex_t o_unac_mutex;
+static int unac_mutex_is_init;
+// Call this or take your chances with the auto init.
+void unac_init_mt()
+{
+ pthread_mutex_init(&o_unac_mutex, 0);
+ unac_mutex_is_init = 1;
+}
/*
* Convert buffer containing string encoded in charset into
@@ -10576,6 +10583,7 @@ static int convert(const char* from, const char* to,
const char* in, size_t in_length,
char** outp, size_t* out_lengthp)
{
+ int ret = -1;
iconv_t cd;
char* out;
size_t out_remain;
@@ -10584,6 +10592,15 @@ static int convert(const char* from, const char* to,
int from_utf16, from_utf8, to_utf16, to_utf8, u8tou16, u16tou8;
const char space[] = { 0x00, 0x20 };
+ /* Note: better call explicit unac_init_mt() before starting threads than
+ rely on this.
+ */
+ if (unac_mutex_is_init == 0) {
+ pthread_mutex_init(&o_unac_mutex, 0);
+ unac_mutex_is_init = 1;
+ }
+ pthread_mutex_lock(&o_unac_mutex);
+
if (!strcmp(utf16be, from)) {
from_utf8 = 0;
from_utf16 = 1;
@@ -10614,7 +10631,7 @@ static int convert(const char* from, const char* to,
/* *outp still valid, no freeing */
if(debug_level >= UNAC_DEBUG_LOW)
DEBUG("realloc %d bytes failed\n", out_size+1);
- return -1;
+ goto out;
}
} else {
/* +1 for null */
@@ -10622,7 +10639,7 @@ static int convert(const char* from, const char* to,
if(out == 0) {
if(debug_level >= UNAC_DEBUG_LOW)
DEBUG("malloc %d bytes failed\n", out_size+1);
- return -1;
+ goto out;
}
}
out_remain = out_size;
@@ -10631,7 +10648,7 @@ static int convert(const char* from, const char* to,
if (u8tou16) {
if (u8tou16_cd == (iconv_t)-1) {
if((u8tou16_cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
} else {
iconv(u8tou16_cd, 0, 0, 0, 0);
@@ -10640,7 +10657,7 @@ static int convert(const char* from, const char* to,
} else if (u16tou8) {
if (u16tou8_cd == (iconv_t)-1) {
if((u16tou8_cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
} else {
iconv(u16tou8_cd, 0, 0, 0, 0);
@@ -10648,7 +10665,7 @@ static int convert(const char* from, const char* to,
cd = u16tou8_cd;
} else {
if((cd = iconv_open(to, from)) == (iconv_t)-1) {
- return -1;
+ goto out;
}
}
@@ -10682,7 +10699,7 @@ static int convert(const char* from, const char* to,
if(errno == E2BIG)
/* fall thru to the E2BIG case below */;
else
- return -1;
+ goto out;
} else {
/* The offending character was replaced by a SPACE, skip it. */
in += 2;
@@ -10691,7 +10708,7 @@ static int convert(const char* from, const char* to,
break;
}
} else {
- return -1;
+ goto out;
}
case E2BIG:
{
@@ -10711,7 +10728,7 @@ static int convert(const char* from, const char* to,
DEBUG("realloc %d bytes failed\n", out_size+1);
free(saved);
*outp = 0;
- return -1;
+ goto out;
}
}
out = out_base + length;
@@ -10719,7 +10736,7 @@ static int convert(const char* from, const char* to,
}
break;
default:
- return -1;
+ goto out;
break;
}
}
@@ -10732,7 +10749,10 @@ static int convert(const char* from, const char* to,
*out_lengthp = out - out_base;
(*outp)[*out_lengthp] = '\0';
- return 0;
+ ret = 0;
+out:
+ pthread_mutex_unlock(&o_unac_mutex);
+ return ret;
}
int unacmaybefold_string(const char* charset,
diff --git a/unac/unac.h b/unac/unac.h
index c447c8a1..83fb1fea 100644
--- a/unac/unac.h
+++ b/unac/unac.h
@@ -113,6 +113,9 @@ int unacfold_string(const char* charset,
const char* in, size_t in_length,
char** out, size_t* out_length);
+/* To be called before starting threads in mt programs */
+void unac_init_mt();
+
/*
* Return unac version number.
*/
diff --git a/website/BUGS.html b/website/BUGS.html
index a07428ce..d067689e 100644
--- a/website/BUGS.html
+++ b/website/BUGS.html
@@ -38,7 +38,19 @@
-
+
+ - The recoll GUI program sometimes crashes when
+ running a query while the indexing thread is active.
+ Possible workarounds:
+
+ - (Recommended) Use the command
+ line recollindex program to perform indexing
+ (usually just type "recollindex" in a console, or see "man
+ recollindex").
+ - Do not run queries in recoll while the indexing thread
+ is running (as indicated in the bottom status line).
+
+
- Cancelling a preview in the GUI will also cancel the indexing
thread if it is running.
diff --git a/website/download.html b/website/download.html
index ea819dc8..83490d82 100644
--- a/website/download.html
+++ b/website/download.html
@@ -53,6 +53,27 @@
The current version is 1.16.0.
Release notes.
+
+
Notice for 1.16.0: the
+ recoll GUI program sometimes crashes when running a query
+ while the indexing thread is active. I can reproduce the problem
+ and I am working on a correction. Meanwhile, there are two possible
+ workarounds:
+
+ - (Recommended) Use the command
+ line recollindex program to perform indexing
+ (usually just type "recollindex" in a console, or see "man
+ recollindex").
+ - Do not run queries in recoll while the indexing thread
+ is running (as indicated in the bottom status line).
+
+ If the workaround fails or you experience other kinds of
+ crashes with either
recoll or
recollindex, and
+ want to help, please follow the instructions on
+
+ this wiki page.
+
+
The download page for Recoll 1.15 is
still available.
@@ -204,21 +225,26 @@
Xapian,
Recoll and kio-recoll. These were built from the latest versions,
- for a set of Ubuntu series. You just need to add the
- PPAs to your system software sources (the instructions are on
+ for a set of Ubuntu series.
+
+ Ubuntu 10.04 (lucid) and later versions just need the Recoll
+ PPA. Older versions also needed a backport for Xapian
+ (xapian-backports/xapian-1.2).
+
+ Just add the
+ PPA to your system software sources (the instructions are on
the PPA page or
here), and you can then use the normal package
- manager to install or update Recoll and Xapian. For Ubuntu versions
- from 9.10 (Karmic), only two commands are needed:
+ manager to install or update Recoll. For Ubuntu versions
+ after 9.10 (Karmic), only one command is needed:
-sudo add-apt-repository ppa:xapian-backports/xapian-1.2
sudo add-apt-repository ppa:recoll-backports/recoll-1.15-on
- For Ubuntu 9.04 (Jaunty) and older, to avoid
- messages about signature errors, you may have to explicitely import the
+
For Ubuntu 9.04 (Jaunty) and older,
+ you may have to explicitely import the
Recoll and Xapian public keys:
gpg --keyserver keyserver.ubuntu.com --recv 9DA85604
gpg --export --armor 9DA85604 | sudo apt-key add -
@@ -390,7 +416,11 @@ I now use the OpenSUSE build service to create Recoll OpenSUSE packages.
Updated 1.16 translations that became available after the
release:
- None for now :(
+ Czech, thanks to Pavel !
+ recoll_cs.ts
+ recoll_cs.qm
+
+