Fix problems that occurred when multiple threads were trying to read/convert files at the same time (ie: indexing and previewing threads in the GUI calling internfile()). Either get rid of or lock-protect all shared data, eliminate misc initialization possible conflicts by using static initializers. Hopefuly closes issue #51

This commit is contained in:
Jean-Francois Dockes 2011-04-28 10:58:33 +02:00
parent 01f24fa5fd
commit 55f124725f
33 changed files with 318 additions and 231 deletions

View File

@ -320,7 +320,12 @@ list<string> RclConfig::getTopdirs()
// (only the locale). // (only the locale).
const string& RclConfig::getDefCharset(bool filename) const string& RclConfig::getDefCharset(bool filename)
{ {
// This can't change once computed inside a process. // This can't change once computed inside a process. It would be
// nicer to move this to a static class initializer to avoid
// possible threading issues but this doesn't work (tried) as
// things would not be ready. In practise we make sure that this
// is called from the main thread at once, by calling
// getDefCharset from recollinit
static string localecharset; static string localecharset;
if (localecharset.empty()) { if (localecharset.empty()) {
const char *cp; const char *cp;

View File

@ -92,6 +92,10 @@ RclConfig *recollinit(RclInitFlags flags,
// to utf8 for indexing. // to utf8 for indexing.
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
// Make sure the locale charset is initialized (so that multiple
// threads don't try to do it at once).
config->getDefCharset();
return config; return config;
} }

View File

@ -60,49 +60,47 @@ static int charclasses[charclasses_size];
static set<unsigned int> unicign; static set<unsigned int> unicign;
static set<unsigned int> visiblewhite; static set<unsigned int> visiblewhite;
// Set up character classes array and the additional unicode sets class CharClassInit {
static void setcharclasses() public:
{ CharClassInit()
static int init = 0; {
if (init) unsigned int i;
return;
unsigned int i;
// Set default value for all: SPACE // Set default value for all: SPACE
for (i = 0 ; i < 256 ; i ++) for (i = 0 ; i < 256 ; i ++)
charclasses[i] = SPACE; charclasses[i] = SPACE;
char digits[] = "0123456789"; char digits[] = "0123456789";
for (i = 0; i < strlen(digits); i++) for (i = 0; i < strlen(digits); i++)
charclasses[int(digits[i])] = DIGIT; charclasses[int(digits[i])] = DIGIT;
char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (i = 0; i < strlen(upper); i++) for (i = 0; i < strlen(upper); i++)
charclasses[int(upper[i])] = A_ULETTER; charclasses[int(upper[i])] = A_ULETTER;
char lower[] = "abcdefghijklmnopqrstuvwxyz"; char lower[] = "abcdefghijklmnopqrstuvwxyz";
for (i = 0; i < strlen(lower); i++) for (i = 0; i < strlen(lower); i++)
charclasses[int(lower[i])] = A_LLETTER; charclasses[int(lower[i])] = A_LLETTER;
char wild[] = "*?[]"; char wild[] = "*?[]";
for (i = 0; i < strlen(wild); i++) for (i = 0; i < strlen(wild); i++)
charclasses[int(wild[i])] = WILD; charclasses[int(wild[i])] = WILD;
char special[] = ".@+-,#'_\n\r"; char special[] = ".@+-,#'_\n\r";
for (i = 0; i < strlen(special); i++) for (i = 0; i < strlen(special); i++)
charclasses[int(special[i])] = special[i]; charclasses[int(special[i])] = special[i];
for (i = 0; i < sizeof(uniign) / sizeof(int); i++) { for (i = 0; i < sizeof(uniign) / sizeof(int); i++) {
unicign.insert(uniign[i]); unicign.insert(uniign[i]);
}
unicign.insert((unsigned int)-1);
for (i = 0; i < sizeof(avsbwht) / sizeof(int); i++) {
visiblewhite.insert(avsbwht[i]);
}
} }
unicign.insert((unsigned int)-1); };
static const CharClassInit charClassInitInstance;
for (i = 0; i < sizeof(avsbwht) / sizeof(int); i++) {
visiblewhite.insert(avsbwht[i]);
}
init = 1;
}
static inline int whatcc(unsigned int c) static inline int whatcc(unsigned int c)
{ {
@ -280,8 +278,6 @@ bool TextSplit::text_to_words(const string &in)
m_flags & TXTS_KEEPWILD ? " keepwild" : "", m_flags & TXTS_KEEPWILD ? " keepwild" : "",
in.substr(0,50).c_str())); in.substr(0,50).c_str()));
setcharclasses();
m_span.erase(); m_span.erase();
m_inNumber = false; m_inNumber = false;
m_wordStart = m_wordLen = m_prevpos = m_prevlen = m_wordpos = m_spanpos = 0; m_wordStart = m_wordLen = m_prevpos = m_prevlen = m_wordpos = m_spanpos = 0;
@ -633,7 +629,6 @@ int TextSplit::countWords(const string& s, TextSplit::Flags flgs)
bool TextSplit::hasVisibleWhite(const string &in) bool TextSplit::hasVisibleWhite(const string &in)
{ {
setcharclasses();
Utf8Iter it(in); Utf8Iter it(in);
for (; !it.eof(); it++) { for (; !it.eof(); it++) {
unsigned int c = (unsigned char)*it; unsigned int c = (unsigned char)*it;
@ -650,7 +645,6 @@ bool TextSplit::hasVisibleWhite(const string &in)
template <class T> bool u8stringToStrings(const string &s, T &tokens) template <class T> bool u8stringToStrings(const string &s, T &tokens)
{ {
setcharclasses();
Utf8Iter it(s); Utf8Iter it(s);
string current; string current;

View File

@ -63,7 +63,13 @@ using namespace std;
#define deleteZ(X) {delete X;X = 0;} #define deleteZ(X) {delete X;X = 0;}
#endif #endif
FsIndexer::FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc)
: m_config(cnf), m_db(db), m_updater(updfunc), m_missing(new FIMissingStore)
{
m_havelocalfields = m_config->hasNameAnywhere("localfields");
}
FsIndexer::~FsIndexer() { FsIndexer::~FsIndexer() {
delete m_missing;
} }
bool FsIndexer::init() bool FsIndexer::init()
@ -121,7 +127,7 @@ bool FsIndexer::index()
} }
string missing; string missing;
FileInterner::getMissingDescription(missing); FileInterner::getMissingDescription(m_missing, missing);
if (!missing.empty()) { if (!missing.empty()) {
LOGINFO(("FsIndexer::index missing helper program(s):\n%s\n", LOGINFO(("FsIndexer::index missing helper program(s):\n%s\n",
missing.c_str())); missing.c_str()));
@ -359,6 +365,7 @@ FsIndexer::processone(const std::string &fn, const struct stat *stp,
// indexallfilenames is not set // indexallfilenames is not set
return FsTreeWalker::FtwOk; return FsTreeWalker::FtwOk;
} }
interner.setMissingStore(m_missing);
// File name transcoded to utf8 for indexing. // File name transcoded to utf8 for indexing.
string charset = m_config->getDefCharset(true); string charset = m_config->getDefCharset(true);

View File

@ -26,6 +26,7 @@ using std::list;
#include "rcldb.h" #include "rcldb.h"
class DbIxStatusUpdater; class DbIxStatusUpdater;
class FIMissingStore;
/** Index selected parts of the file system /** Index selected parts of the file system
@ -45,12 +46,7 @@ class FsIndexer : public FsTreeWalkerCB {
* @param cnf Configuration data * @param cnf Configuration data
* @param updfunc Status updater callback * @param updfunc Status updater callback
*/ */
FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc = 0) FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc = 0);
: m_config(cnf), m_db(db), m_updater(updfunc)
{
m_havelocalfields = m_config->hasNameAnywhere("localfields");
}
virtual ~FsIndexer(); virtual ~FsIndexer();
/** /**
@ -79,6 +75,7 @@ class FsIndexer : public FsTreeWalkerCB {
string m_reason; string m_reason;
DbIxStatusUpdater *m_updater; DbIxStatusUpdater *m_updater;
list<string> m_tdl; list<string> m_tdl;
FIMissingStore *m_missing;
// The configuration can set attribute fields to be inherited by // The configuration can set attribute fields to be inherited by
// all files in a file system area. Ie: set "rclaptg = thunderbird" // all files in a file system area. Ie: set "rclaptg = thunderbird"

View File

@ -45,6 +45,7 @@ using namespace std;
#include "beaglequeuecache.h" #include "beaglequeuecache.h"
#include "cancelcheck.h" #include "cancelcheck.h"
#include "copyfile.h" #include "copyfile.h"
#include "ptmutex.h"
#ifdef RCL_USE_XATTR #ifdef RCL_USE_XATTR
#include "pxattr.h" #include "pxattr.h"
@ -76,9 +77,6 @@ static string colon_restore(const string& in)
return out; return out;
} }
set<string> FileInterner::o_missingExternal;
map<string, set<string> > FileInterner::o_typesForMissing;
#ifdef RCL_USE_XATTR #ifdef RCL_USE_XATTR
void FileInterner::reapXAttrs(const string& path) void FileInterner::reapXAttrs(const string& path)
{ {
@ -192,7 +190,7 @@ void FileInterner::tmpcleanup()
FileInterner::FileInterner(const string &f, const struct stat *stp, FileInterner::FileInterner(const string &f, const struct stat *stp,
RclConfig *cnf, RclConfig *cnf,
TempDir& td, int flags, const string *imime) TempDir& td, int flags, const string *imime)
: m_tdir(td), m_ok(false) : m_tdir(td), m_ok(false), m_missingdatap(0)
{ {
initcommon(cnf, flags); initcommon(cnf, flags);
init(f, stp, cnf, flags, imime); init(f, stp, cnf, flags, imime);
@ -304,7 +302,7 @@ void FileInterner::init(const string &f, const struct stat *stp, RclConfig *cnf,
// Setup from memory data (ie: out of the web cache). imime needs to be set. // Setup from memory data (ie: out of the web cache). imime needs to be set.
FileInterner::FileInterner(const string &data, RclConfig *cnf, FileInterner::FileInterner(const string &data, RclConfig *cnf,
TempDir& td, int flags, const string& imime) TempDir& td, int flags, const string& imime)
: m_tdir(td), m_ok(false) : m_tdir(td), m_ok(false), m_missingdatap(0)
{ {
initcommon(cnf, flags); initcommon(cnf, flags);
init(data, cnf, flags, imime); init(data, cnf, flags, imime);
@ -366,9 +364,13 @@ void FileInterner::initcommon(RclConfig *cnf, int flags)
m_targetMType = stxtplain; m_targetMType = stxtplain;
} }
// We used a single beagle cache object to access beagle data. We protect it
// against multiple thread access.
static PTMutexInit o_lock;
FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf,
TempDir& td, int flags) TempDir& td, int flags)
: m_tdir(td), m_ok(false) : m_tdir(td), m_ok(false), m_missingdatap(0)
{ {
LOGDEB(("FileInterner::FileInterner(idoc)\n")); LOGDEB(("FileInterner::FileInterner(idoc)\n"));
initcommon(cnf, flags); initcommon(cnf, flags);
@ -405,10 +407,6 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf,
} }
init(fn, &st, cnf, flags, &idoc.mimetype); init(fn, &st, cnf, flags, &idoc.mimetype);
} else if (!backend.compare("BGL")) { } else if (!backend.compare("BGL")) {
// 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);
string data; string data;
Rcl::Doc dotdoc; Rcl::Doc dotdoc;
map<string,string>::const_iterator it = map<string,string>::const_iterator it =
@ -418,11 +416,19 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf,
return; return;
} }
string udi = it->second; string udi = it->second;
if (!beagler.getFromCache(udi, dotdoc, data)) {
LOGINFO(("FileInterner:: failed fetch from Beagle cache for [%s]\n", {
udi.c_str())); PTMutexLocker locker(o_lock);
return; // 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)) {
LOGINFO(("FileInterner:: failed fetch from Beagle cache for [%s]\n",
udi.c_str()));
return;
}
}
if (dotdoc.mimetype.compare(idoc.mimetype)) { if (dotdoc.mimetype.compare(idoc.mimetype)) {
LOGINFO(("FileInterner:: udi [%s], mimetp mismatch: in: [%s], bgl " LOGINFO(("FileInterner:: udi [%s], mimetp mismatch: in: [%s], bgl "
"[%s]\n", idoc.mimetype.c_str(), dotdoc.mimetype.c_str())); "[%s]\n", idoc.mimetype.c_str(), dotdoc.mimetype.c_str()));
@ -485,7 +491,7 @@ bool FileInterner::dataToTempFile(const string& dt, const string& mt,
// accumulate helper name if it is // accumulate helper name if it is
void FileInterner::checkExternalMissing(const string& msg, const string& mt) void FileInterner::checkExternalMissing(const string& msg, const string& mt)
{ {
if (msg.find("RECFILTERROR") == 0) { if (m_missingdatap && msg.find("RECFILTERROR") == 0) {
list<string> lerr; list<string> lerr;
stringToStrings(msg, lerr); stringToStrings(msg, lerr);
if (lerr.size() > 2) { if (lerr.size() > 2) {
@ -495,28 +501,33 @@ void FileInterner::checkExternalMissing(const string& msg, const string& mt)
lerr.erase(it++); lerr.erase(it++);
string s; string s;
stringsToString(lerr, s); stringsToString(lerr, s);
o_missingExternal.insert(s); m_missingdatap->m_missingExternal.insert(s);
o_typesForMissing[s].insert(mt); m_missingdatap->m_typesForMissing[s].insert(mt);
} }
} }
} }
} }
void FileInterner::getMissingExternal(string& out) void FileInterner::getMissingExternal(FIMissingStore *st, string& out)
{ {
stringsToString(o_missingExternal, out); if (st)
stringsToString(st->m_missingExternal, out);
} }
void FileInterner::getMissingDescription(string& out) void FileInterner::getMissingDescription(FIMissingStore *st, string& out)
{ {
if (st == 0)
return;
out.erase(); out.erase();
for (set<string>::const_iterator it = o_missingExternal.begin(); for (set<string>::const_iterator it =
it != o_missingExternal.end(); it++) { st->m_missingExternal.begin();
it != st->m_missingExternal.end(); it++) {
out += *it; out += *it;
map<string, set<string> >::const_iterator it2; map<string, set<string> >::const_iterator it2;
it2 = o_typesForMissing.find(*it); it2 = st->m_typesForMissing.find(*it);
if (it2 != o_typesForMissing.end()) { if (it2 != st->m_typesForMissing.end()) {
out += " ("; out += " (";
set<string>::const_iterator it3; set<string>::const_iterator it3;
for (it3 = it2->second.begin(); for (it3 = it2->second.begin();

View File

@ -39,6 +39,19 @@ class Doc;
struct stat; struct stat;
/** Storage for missing helper program info. We want to keep this out of the
* FileInterner class, because the data will typically be accumulated by several
* FileInterner objects. Can't use static member either (because there
* may be several separate usages of the class which shouldn't mix
* their data).
*/
class FIMissingStore {
public:
// Missing external programs
set<string> m_missingExternal;
map<string, set<string> > m_typesForMissing;
};
/** /**
* A class to convert data from a datastore (file-system, firefox * A class to convert data from a datastore (file-system, firefox
* history, etc.) into possibly one or severaldocuments in internal * history, etc.) into possibly one or severaldocuments in internal
@ -110,6 +123,11 @@ class FileInterner {
~FileInterner(); ~FileInterner();
void setMissingStore(FIMissingStore *st)
{
m_missingdatap = st;
}
/** /**
* Turn file or file part into Recoll document. * Turn file or file part into Recoll document.
* *
@ -169,11 +187,10 @@ class FileInterner {
RclConfig *cnf, const Rcl::Doc& doc); RclConfig *cnf, const Rcl::Doc& doc);
const string& getReason() const {return m_reason;} const string& getReason() const {return m_reason;}
static void getMissingExternal(string& missing); static void getMissingExternal(FIMissingStore *st, string& missing);
static void getMissingDescription(string& desc); static void getMissingDescription(FIMissingStore *st, string& desc);
bool ok() {return m_ok;} bool ok() {return m_ok;}
private: private:
static const unsigned int MAXHANDLERS = 20; static const unsigned int MAXHANDLERS = 20;
RclConfig *m_cfg; RclConfig *m_cfg;
@ -203,9 +220,7 @@ class FileInterner {
vector<TempFile> m_tempfiles; vector<TempFile> m_tempfiles;
// Error data if any // Error data if any
string m_reason; string m_reason;
// Missing external programs FIMissingStore *m_missingdatap;
static set<string> o_missingExternal;
static map<string, set<string> > o_typesForMissing;
// Pseudo-constructors // Pseudo-constructors
void init(const string &fn, const struct stat *stp, void init(const string &fn, const struct stat *stp,

View File

@ -37,6 +37,7 @@
#include "rclconfig.h" #include "rclconfig.h"
#include "md5.h" #include "md5.h"
#include "conftree.h" #include "conftree.h"
#include "ptmutex.h"
using namespace std; using namespace std;
class FpKeeper { class FpKeeper {
@ -52,6 +53,7 @@ public:
private: FILE **m_fpp; private: FILE **m_fpp;
}; };
static PTMutexInit o_mutex;
/** /**
* Handles a cache for message numbers to offset translations. Permits direct * Handles a cache for message numbers to offset translations. Permits direct
@ -78,6 +80,7 @@ public:
~MboxCache() {} ~MboxCache() {}
mbhoff_type get_offset(RclConfig *config, const string& udi, int msgnum) mbhoff_type get_offset(RclConfig *config, const string& udi, int msgnum)
{ {
PTMutexLocker locker(o_mutex);
LOGDEB0(("MboxCache::get_offsets: udi [%s] msgnum %d\n", udi.c_str(), LOGDEB0(("MboxCache::get_offsets: udi [%s] msgnum %d\n", udi.c_str(),
msgnum)); msgnum));
if (!ok(config)) { if (!ok(config)) {
@ -125,6 +128,7 @@ public:
void put_offsets(RclConfig *config, const string& udi, mbhoff_type fsize, void put_offsets(RclConfig *config, const string& udi, mbhoff_type fsize,
vector<mbhoff_type>& offs) vector<mbhoff_type>& offs)
{ {
PTMutexLocker locker(o_mutex);
LOGDEB0(("MboxCache::put_offsets: %u offsets\n", offs.size())); LOGDEB0(("MboxCache::put_offsets: %u offsets\n", offs.size()));
if (!ok(config) || !maybemakedir()) if (!ok(config) || !maybemakedir())
return; return;
@ -159,6 +163,7 @@ public:
// Check state, possibly initialize // Check state, possibly initialize
bool ok(RclConfig *config) { bool ok(RclConfig *config) {
PTMutexLocker locker(o_mutex);
if (m_minfsize == -1) if (m_minfsize == -1)
return false; return false;
if (!m_ok) { if (!m_ok) {
@ -218,7 +223,9 @@ private:
}; };
const size_t MboxCache::o_b1size = 1024; const size_t MboxCache::o_b1size = 1024;
static class MboxCache mcache; static class MboxCache mcache;
static const string keyquirks("mhmboxquirks"); static const string keyquirks("mhmboxquirks");
MimeHandlerMbox::~MimeHandlerMbox() MimeHandlerMbox::~MimeHandlerMbox()
@ -389,6 +396,7 @@ bool MimeHandlerMbox::next_document()
// avoid rereading the whole thing in this case. But I'm not sure // avoid rereading the whole thing in this case. But I'm not sure
// we're ever used in this way (multiple retrieves on same // we're ever used in this way (multiple retrieves on same
// object). So: // object). So:
bool storeoffsets = true;
if (mtarg > 0) { if (mtarg > 0) {
mbhoff_type off; mbhoff_type off;
line_type line; line_type line;
@ -404,6 +412,7 @@ bool MimeHandlerMbox::next_document()
LOGDEB0(("MimeHandlerMbox: Cache: From_ Ok\n")); LOGDEB0(("MimeHandlerMbox: Cache: From_ Ok\n"));
fseeko(fp, (off_t)off, SEEK_SET); fseeko(fp, (off_t)off, SEEK_SET);
m_msgnum = mtarg -1; m_msgnum = mtarg -1;
storeoffsets = false;
} else { } else {
fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
m_msgnum = 0; m_msgnum = 0;
@ -444,7 +453,8 @@ bool MimeHandlerMbox::next_document()
!regexec(&minifromregex, line, 0, 0, 0)) ) { !regexec(&minifromregex, line, 0, 0, 0)) ) {
LOGDEB1(("MimeHandlerMbox: msgnum %d, " LOGDEB1(("MimeHandlerMbox: msgnum %d, "
"From_ at line %d: [%s]\n", m_msgnum, m_lineno, line)); "From_ at line %d: [%s]\n", m_msgnum, m_lineno, line));
m_offsets.push_back(message_end); if (storeoffsets)
m_offsets.push_back(message_end);
m_msgnum++; m_msgnum++;
if ((mtarg <= 0 && m_msgnum > 1) || if ((mtarg <= 0 && m_msgnum > 1) ||
(mtarg > 0 && m_msgnum > mtarg)) { (mtarg > 0 && m_msgnum > mtarg)) {
@ -477,7 +487,7 @@ bool MimeHandlerMbox::next_document()
if (iseof) { if (iseof) {
LOGDEB2(("MimeHandlerMbox::next: eof hit\n")); LOGDEB2(("MimeHandlerMbox::next: eof hit\n"));
m_havedoc = false; m_havedoc = false;
if (!m_udi.empty()) { if (!m_udi.empty() && storeoffsets) {
mcache.put_offsets(m_config, m_udi, m_fsize, m_offsets); mcache.put_offsets(m_config, m_udi, m_fsize, m_offsets);
} }
} }

View File

@ -28,7 +28,6 @@ using namespace std;
#include "debuglog.h" #include "debuglog.h"
#include "rclconfig.h" #include "rclconfig.h"
#include "smallut.h" #include "smallut.h"
#include "pthread.h"
#include "mh_exec.h" #include "mh_exec.h"
#include "mh_execm.h" #include "mh_execm.h"
@ -37,6 +36,7 @@ using namespace std;
#include "mh_mbox.h" #include "mh_mbox.h"
#include "mh_text.h" #include "mh_text.h"
#include "mh_unknown.h" #include "mh_unknown.h"
#include "ptmutex.h"
// Performance help: we use a pool of already known and created // Performance help: we use a pool of already known and created
// handlers. There can be several instances for a given mime type // handlers. There can be several instances for a given mime type
@ -48,26 +48,7 @@ using namespace std;
// simple lock should be enough as handlers are removed from the cache // simple lock should be enough as handlers are removed from the cache
// while in use and multiple copies are allowed // while in use and multiple copies are allowed
static multimap<string, Dijon::Filter*> o_handlers; static multimap<string, Dijon::Filter*> o_handlers;
pthread_mutex_t o_handlers_mutex; static PTMutexInit o_handlers_mutex;
class HandlersLocker {
public:
HandlersLocker()
{
pthread_mutex_lock(&o_handlers_mutex);
}
~HandlersLocker()
{
pthread_mutex_unlock(&o_handlers_mutex);
}
};
class HandlersLockerInit {
public:
HandlersLockerInit()
{
pthread_mutex_init(&o_handlers_mutex, 0);
}
};
static HandlersLockerInit o_hli;
/** For mime types set as "internal" in mimeconf: /** For mime types set as "internal" in mimeconf:
* create appropriate handler object. */ * create appropriate handler object. */
@ -163,7 +144,7 @@ void returnMimeHandler(Dijon::Filter *handler)
typedef multimap<string, Dijon::Filter*>::value_type value_type; typedef multimap<string, Dijon::Filter*>::value_type value_type;
if (handler) { if (handler) {
handler->clear(); handler->clear();
HandlersLocker locker; PTMutexLocker locker(o_handlers_mutex);
o_handlers.insert(value_type(handler->get_mime_type(), handler)); o_handlers.insert(value_type(handler->get_mime_type(), handler));
} }
} }
@ -172,7 +153,7 @@ void clearMimeHandlerCache()
{ {
typedef multimap<string, Dijon::Filter*>::value_type value_type; typedef multimap<string, Dijon::Filter*>::value_type value_type;
map<string, Dijon::Filter *>::iterator it; map<string, Dijon::Filter *>::iterator it;
HandlersLocker locker; PTMutexLocker locker(o_handlers_mutex);
for (it = o_handlers.begin(); it != o_handlers.end(); it++) { for (it = o_handlers.begin(); it != o_handlers.end(); it++) {
delete it->second; delete it->second;
} }
@ -187,7 +168,7 @@ Dijon::Filter *getMimeHandler(const string &mtype, RclConfig *cfg,
LOGDEB2(("getMimeHandler: mtype [%s] filtertypes %d\n", LOGDEB2(("getMimeHandler: mtype [%s] filtertypes %d\n",
mtype.c_str(), filtertypes)); mtype.c_str(), filtertypes));
Dijon::Filter *h = 0; Dijon::Filter *h = 0;
HandlersLocker locker; PTMutexLocker locker(o_handlers_mutex);
// Get handler definition for mime type. We do this even if an // Get handler definition for mime type. We do this even if an
// appropriate handler object may be in the cache (indexed by mime // appropriate handler object may be in the cache (indexed by mime

View File

@ -35,8 +35,6 @@
#include "debuglog.h" #include "debuglog.h"
#include "transcode.h" #include "transcode.h"
map<string, string> MyHtmlParser::my_named_ents;
inline static bool inline static bool
p_notdigit(char c) p_notdigit(char c)
{ {
@ -151,20 +149,11 @@ static const char *epairs[] = {
"rsaquo", "\xe2\x80\xba", "euro", "\xe2\x82\xac", "rsaquo", "\xe2\x80\xba", "euro", "\xe2\x82\xac",
NULL, NULL NULL, NULL
}; };
map<string, string> my_named_ents;
MyHtmlParser::MyHtmlParser() class NamedEntsInitializer {
: in_script_tag(false), public:
in_style_tag(false), NamedEntsInitializer()
in_body_tag(false), {
in_pre_tag(false),
pending_space(false),
indexing_allowed(true)
{
// The default html document charset is iso-8859-1. We'll update
// this value from the encoding tag if found.
charset = "iso-8859-1";
if (my_named_ents.empty()) {
for (int i = 0;;) { for (int i = 0;;) {
const char *ent; const char *ent;
const char *val; const char *val;
@ -177,6 +166,20 @@ MyHtmlParser::MyHtmlParser()
my_named_ents[string(ent)] = val; my_named_ents[string(ent)] = val;
} }
} }
};
static NamedEntsInitializer namedEntsInitializerInstance;
MyHtmlParser::MyHtmlParser()
: in_script_tag(false),
in_style_tag(false),
in_body_tag(false),
in_pre_tag(false),
pending_space(false),
indexing_allowed(true)
{
// The default html document charset is iso-8859-1. We'll update
// this value from the encoding tag if found.
charset = "iso-8859-1";
} }
void MyHtmlParser::decode_entities(string &s) void MyHtmlParser::decode_entities(string &s)

View File

@ -40,7 +40,6 @@ class MyHtmlParser : public HtmlParser {
bool in_pre_tag; bool in_pre_tag;
bool pending_space; bool pending_space;
map<string,string> meta; map<string,string> meta;
static map<string, string> my_named_ents;
string dump, dmtime; string dump, dmtime;
// This is the charset our caller thinks the doc used (initially // This is the charset our caller thinks the doc used (initially
// comes from the environment/configuration, used as source for // comes from the environment/configuration, used as source for

View File

@ -48,8 +48,6 @@ using std::unique;
#include "guiutils.h" #include "guiutils.h"
#include "rclhelp.h" #include "rclhelp.h"
extern RclConfig *rclconfig;
static const int initclausetypes[] = {1, 3, 0, 2, 5}; static const int initclausetypes[] = {1, 3, 0, 2, 5};
static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int); static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int);
static map<QString,QString> cat_translations; static map<QString,QString> cat_translations;
@ -268,7 +266,7 @@ void AdvSearch::fillFileTypes()
QStringList ql; QStringList ql;
if (m_ignByCats == false) { if (m_ignByCats == false) {
list<string> types = rclconfig->getAllMimeTypes(); list<string> types = theconfig->getAllMimeTypes();
for (list<string>::iterator it = types.begin(); for (list<string>::iterator it = types.begin();
it != types.end(); it++) { it != types.end(); it++) {
QString qs = QString::fromUtf8(it->c_str()); QString qs = QString::fromUtf8(it->c_str());
@ -277,7 +275,7 @@ void AdvSearch::fillFileTypes()
} }
} else { } else {
list<string> cats; list<string> cats;
rclconfig->getMimeCategories(cats); theconfig->getMimeCategories(cats);
for (list<string>::const_iterator it = cats.begin(); for (list<string>::const_iterator it = cats.begin();
it != cats.end(); it++) { it != cats.end(); it++) {
map<QString, QString>::const_iterator it1; map<QString, QString>::const_iterator it1;
@ -334,7 +332,7 @@ void AdvSearch::runSearch()
cat = (const char *)qcat.toUtf8(); cat = (const char *)qcat.toUtf8();
} }
list<string> types; list<string> types;
rclconfig->getMimeCatTypes(cat, types); theconfig->getMimeCatTypes(cat, types);
for (list<string>::const_iterator it = types.begin(); for (list<string>::const_iterator it = types.begin();
it != types.end(); it++) { it != types.end(); it++) {
sdata->addFiletype(*it); sdata->addFiletype(*it);

View File

@ -49,8 +49,8 @@ using std::list;
namespace confgui { namespace confgui {
const static int spacing = 2; static const int spacing = 2;
const static int margin = 2; static const int margin = 2;
void ConfParamW::setValue(const QString& value) void ConfParamW::setValue(const QString& value)
{ {

View File

@ -44,8 +44,8 @@ using std::list;
#include "rclconfig.h" #include "rclconfig.h"
namespace confgui { namespace confgui {
const static int spacing = 3; static const int spacing = 3;
const static int margin = 3; static const int margin = 3;
ConfIndexW::ConfIndexW(QWidget *parent, RclConfig *config) ConfIndexW::ConfIndexW(QWidget *parent, RclConfig *config)
: QDialog(parent), m_rclconf(config) : QDialog(parent), m_rclconf(config)
@ -84,6 +84,7 @@ void ConfIndexW::acceptChanges()
delete m_conf; delete m_conf;
m_conf = 0; m_conf = 0;
m_rclconf->updateMainConfig(); m_rclconf->updateMainConfig();
snapshotConfig();
if (startIndexingAfterConfig) { if (startIndexingAfterConfig) {
startIndexingAfterConfig = 0; startIndexingAfterConfig = 0;

View File

@ -29,6 +29,7 @@
#include "smallut.h" #include "smallut.h"
#include "rclinit.h" #include "rclinit.h"
#include "pathut.h" #include "pathut.h"
#include "recoll.h"
static int stopindexing; static int stopindexing;
static int startindexing; static int startindexing;
@ -60,7 +61,6 @@ class IdxThread : public QThread , public DbIxStatusUpdater {
// Maintain a copy/snapshot of idx status // Maintain a copy/snapshot of idx status
DbIxStatus m_statusSnap; DbIxStatus m_statusSnap;
bool m_interrupted; bool m_interrupted;
const RclConfig *cnf;
}; };
void IdxThread::run() void IdxThread::run()
@ -76,7 +76,11 @@ void IdxThread::run()
indexingstatus = IDXTS_NULL; indexingstatus = IDXTS_NULL;
// We make a private snapshot of the config: setKeydir changes // We make a private snapshot of the config: setKeydir changes
// it during indexing and it may be updated by the main thread. // it during indexing and it may be updated by the main thread.
RclConfig *myconf = new RclConfig(*cnf); RclConfig *myconf;
{
PTMutexLocker locker(thestableconfiglock);
myconf = new RclConfig(*thestableconfig);
}
int loglevel; int loglevel;
myconf->setKeyDir(""); myconf->setKeyDir("");
myconf->getConfParam("loglevel", &loglevel); myconf->getConfParam("loglevel", &loglevel);
@ -117,9 +121,8 @@ static IdxThread idxthread;
// Functions called by the main thread // Functions called by the main thread
void start_idxthread(const RclConfig& cnf) void start_idxthread()
{ {
idxthread.cnf = &cnf;
idxthread.start(); idxthread.start();
} }

View File

@ -19,11 +19,11 @@
#include <string> #include <string>
#include "indexer.h" #include "indexer.h"
class RclConfig;
// These two deal with starting / stopping the thread itself, not // These two deal with starting / stopping the thread itself, not
// indexing sessions. // indexing sessions.
extern void start_idxthread(const RclConfig& cnf); // cnf will be cloned each time we start an indexing pass. The pointer must
// stay valid for the whole program duration.
extern void start_idxthread();
extern void stop_idxthread(); extern void stop_idxthread();
// Use these to to request action from thread // Use these to to request action from thread

View File

@ -47,7 +47,16 @@
#include "smallut.h" #include "smallut.h"
#include "recollq.h" #include "recollq.h"
RclConfig *rclconfig; RclConfig *theconfig;
RclConfig *thestableconfig;
PTMutexInit thestableconfiglock;
void snapshotConfig()
{
PTMutexLocker locker(thestableconfiglock);
thestableconfig = new RclConfig(*theconfig);
}
Rcl::Db *rcldb; Rcl::Db *rcldb;
#ifdef RCL_USE_ASPELL #ifdef RCL_USE_ASPELL
Aspell *aspell; Aspell *aspell;
@ -80,7 +89,7 @@ bool maybeOpenDb(string &reason, bool force)
} }
if (!rcldb->isopen() && !rcldb->open(Rcl::Db::DbRO)) { if (!rcldb->isopen() && !rcldb->open(Rcl::Db::DbRO)) {
reason = "Could not open database in " + reason = "Could not open database in " +
rclconfig->getDbDir() + " wait for indexing to complete?"; theconfig->getDbDir() + " wait for indexing to complete?";
return false; return false;
} }
rcldb->setAbstractParams(-1, prefs.syntAbsLen, prefs.syntAbsCtx); rcldb->setAbstractParams(-1, prefs.syntAbsLen, prefs.syntAbsCtx);
@ -104,7 +113,8 @@ static void recollCleanup()
rwSettings(true); rwSettings(true);
LOGDEB2(("recollCleanup: closing database\n")); LOGDEB2(("recollCleanup: closing database\n"));
deleteZ(rcldb); deleteZ(rcldb);
deleteZ(rclconfig); deleteZ(theconfig);
deleteZ(thestableconfig);
#ifdef RCL_USE_ASPELL #ifdef RCL_USE_ASPELL
deleteZ(aspell); deleteZ(aspell);
#endif #endif
@ -165,7 +175,7 @@ int main(int argc, char **argv)
{ {
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "-t")) { if (!strcmp(argv[i], "-t")) {
exit(recollq(&rclconfig, argc, argv)); exit(recollq(&theconfig, argc, argv));
} }
} }
@ -218,17 +228,18 @@ int main(int argc, char **argv)
app.installTranslator( &qt ); app.installTranslator( &qt );
string reason; string reason;
rclconfig = recollinit(recollCleanup, sigcleanup, reason, &a_config); theconfig = recollinit(recollCleanup, sigcleanup, reason, &a_config);
if (!rclconfig || !rclconfig->ok()) { if (!theconfig || !theconfig->ok()) {
QString msg = "Configuration problem: "; QString msg = "Configuration problem: ";
msg += QString::fromUtf8(reason.c_str()); msg += QString::fromUtf8(reason.c_str());
QMessageBox::critical(0, "Recoll", msg); QMessageBox::critical(0, "Recoll", msg);
exit(1); exit(1);
} }
snapshotConfig();
// fprintf(stderr, "recollinit done\n"); // fprintf(stderr, "recollinit done\n");
// Translations for Recoll // Translations for Recoll
string translatdir = path_cat(rclconfig->getDatadir(), "translations"); string translatdir = path_cat(theconfig->getDatadir(), "translations");
QTranslator translator(0); QTranslator translator(0);
translator.load( QString("recoll_") + slang, translatdir.c_str() ); translator.load( QString("recoll_") + slang, translatdir.c_str() );
app.installTranslator( &translator ); app.installTranslator( &translator );
@ -236,7 +247,7 @@ int main(int argc, char **argv)
// fprintf(stderr, "Translations installed\n"); // fprintf(stderr, "Translations installed\n");
#ifdef RCL_USE_ASPELL #ifdef RCL_USE_ASPELL
aspell = new Aspell(rclconfig); aspell = new Aspell(theconfig);
aspell->init(reason); aspell->init(reason);
if (!aspell || !aspell->ok()) { if (!aspell || !aspell->ok()) {
LOGDEB(("Aspell speller creation failed %s\n", reason.c_str())); LOGDEB(("Aspell speller creation failed %s\n", reason.c_str()));
@ -244,7 +255,7 @@ int main(int argc, char **argv)
} }
#endif #endif
string historyfile = path_cat(rclconfig->getConfDir(), "history"); string historyfile = path_cat(theconfig->getConfDir(), "history");
g_dynconf = new RclDynConf(historyfile); g_dynconf = new RclDynConf(historyfile);
if (!g_dynconf || !g_dynconf->ok()) { if (!g_dynconf || !g_dynconf->ok()) {
QString msg = app.translate("Main", "Configuration problem (dynconf"); QString msg = app.translate("Main", "Configuration problem (dynconf");
@ -266,7 +277,7 @@ int main(int argc, char **argv)
mainWindow->resize(s); mainWindow->resize(s);
} }
string dbdir = rclconfig->getDbDir(); string dbdir = theconfig->getDbDir();
if (dbdir.empty()) { if (dbdir.empty()) {
QMessageBox::critical(0, "Recoll", QMessageBox::critical(0, "Recoll",
app.translate("Main", app.translate("Main",
@ -274,7 +285,7 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
rcldb = new Rcl::Db(rclconfig); rcldb = new Rcl::Db(theconfig);
mainWindow->show(); mainWindow->show();
QTimer::singleShot(0, mainWindow, SLOT(initDbOpen())); QTimer::singleShot(0, mainWindow, SLOT(initDbOpen()));
@ -286,7 +297,7 @@ int main(int argc, char **argv)
// Start the indexing thread. It will immediately go to sleep waiting for // Start the indexing thread. It will immediately go to sleep waiting for
// something to do. // something to do.
start_idxthread(*rclconfig); start_idxthread();
mainWindow->sSearch->searchTypCMB->setCurrentIndex(prefs.ssearchTyp); mainWindow->sSearch->searchTypCMB->setCurrentIndex(prefs.ssearchTyp);
mainWindow->sSearch->searchTypeChanged(prefs.ssearchTyp); mainWindow->sSearch->searchTypeChanged(prefs.ssearchTyp);

View File

@ -518,7 +518,7 @@ void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum)
} }
LOGDEB(("Doc.url: [%s]\n", doc.url.c_str())); LOGDEB(("Doc.url: [%s]\n", doc.url.c_str()));
string url; string url;
printableUrl(rclconfig->getDefCharset(), doc.url, url); printableUrl(theconfig->getDefCharset(), doc.url, url);
string tiptxt = url + string("\n"); string tiptxt = url + string("\n");
tiptxt += doc.mimetype + " " + string(datebuf) + "\n"; tiptxt += doc.mimetype + " " + string(datebuf) + "\n";
if (meta_it != doc.meta.end() && !meta_it->second.empty()) if (meta_it != doc.meta.end() && !meta_it->second.empty())
@ -538,6 +538,11 @@ bool Preview::makeDocCurrent(const Rcl::Doc& doc, int docnum, bool sametab)
{ {
LOGDEB(("Preview::makeDocCurrent: %s\n", doc.url.c_str())); LOGDEB(("Preview::makeDocCurrent: %s\n", doc.url.c_str()));
if (m_loading) {
LOGERR(("Already loading\n"));
return false;
}
/* Check if we already have this page */ /* Check if we already have this page */
for (int i = 0; i < pvTab->count(); i++) { for (int i = 0; i < pvTab->count(); i++) {
QWidget *tw = pvTab->widget(i); QWidget *tw = pvTab->widget(i);
@ -621,9 +626,10 @@ class LoadThread : public QThread {
// QMessageBox::critical(0, "Recoll", Preview::tr("File does not exist")); // QMessageBox::critical(0, "Recoll", Preview::tr("File does not exist"));
FileInterner interner(idoc, rclconfig, tmpdir, FileInterner interner(idoc, theconfig, tmpdir,
FileInterner::FIF_forPreview); FileInterner::FIF_forPreview);
FIMissingStore mst;
interner.setMissingStore(&mst);
// We don't set the interner's target mtype to html because we // We don't set the interner's target mtype to html because we
// do want the html filter to do its work: we won't use the // do want the html filter to do its work: we won't use the
// text, but we need the conversion to utf-8 // text, but we need the conversion to utf-8
@ -647,7 +653,7 @@ class LoadThread : public QThread {
} }
} else { } else {
out.mimetype = interner.getMimetype(); out.mimetype = interner.getMimetype();
interner.getMissingExternal(missing); interner.getMissingExternal(&mst, missing);
*statusp = -1; *statusp = -1;
} }
} catch (CancelExcept) { } catch (CancelExcept) {
@ -706,10 +712,6 @@ public:
bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum) bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
{ {
LOGDEB1(("PreviewTextEdit::loadDocInCurrentTab()\n")); LOGDEB1(("PreviewTextEdit::loadDocInCurrentTab()\n"));
if (m_loading) {
LOGERR(("ALready loading\n"));
return false;
}
LoadGuard guard(&m_loading); LoadGuard guard(&m_loading);
CancelCheck::instance().setCancel(false); CancelCheck::instance().setCancel(false);
@ -757,14 +759,20 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
if (CancelCheck::instance().cancelState()) if (CancelCheck::instance().cancelState())
return false; return false;
if (status != 0) { if (status != 0) {
QMessageBox::warning(0, "Recoll", QString explain;
tr("Can't turn doc into internal " if (!lthr.missing.empty()) {
"representation for ") + explain = QString::fromAscii("<br>") +
fdoc.mimetype.c_str()); tr("Missing helper program: ") +
QString::fromLocal8Bit(lthr.missing.c_str());
}
QMessageBox::warning(0, "Recoll",
tr("Can't turn doc into internal "
"representation for ") +
fdoc.mimetype.c_str() + explain);
return false; return false;
} }
// Reset config just in case. // Reset config just in case.
rclconfig->setKeyDir(""); theconfig->setKeyDir("");
// Create preview text: highlight search terms // Create preview text: highlight search terms
// We don't do the highlighting for very big texts: too long. We // We don't do the highlighting for very big texts: too long. We

View File

@ -97,6 +97,7 @@ void RclMain::init()
spellform = 0; spellform = 0;
m_idxStatusAck = false; m_idxStatusAck = false;
periodictimer = new QTimer(this); periodictimer = new QTimer(this);
m_periodicToggle = 0;
// At least some versions of qt4 don't display the status bar if // At least some versions of qt4 don't display the status bar if
// it's not created here. // it's not created here.
@ -124,7 +125,7 @@ void RclMain::init()
// instead // instead
string slangs; string slangs;
list<string> langs; list<string> langs;
if (rclconfig->getConfParam("indexstemminglanguages", slangs)) { if (theconfig->getConfParam("indexstemminglanguages", slangs)) {
stringToStrings(slangs, langs); stringToStrings(slangs, langs);
} else { } else {
QMessageBox::warning(0, "Recoll", QMessageBox::warning(0, "Recoll",
@ -166,7 +167,7 @@ void RclMain::init()
connect(bgrp, SIGNAL(buttonClicked(int)), this, SLOT(catgFilter(int))); connect(bgrp, SIGNAL(buttonClicked(int)), this, SLOT(catgFilter(int)));
allRDB->setChecked(true); allRDB->setChecked(true);
list<string> cats; list<string> cats;
rclconfig->getMimeCategories(cats); theconfig->getMimeCategories(cats);
// Text for button 0 is not used. Next statement just avoids unused // Text for button 0 is not used. Next statement just avoids unused
// variable compiler warning for catg_strings // variable compiler warning for catg_strings
m_catgbutvec.push_back(catg_strings[0]); m_catgbutvec.push_back(catg_strings[0]);
@ -350,7 +351,7 @@ void RclMain::initDbOpen()
question question
(this, "Recoll", (this, "Recoll",
qApp->translate("Main", "Could not open database in ") + qApp->translate("Main", "Could not open database in ") +
QString::fromLocal8Bit(rclconfig->getDbDir().c_str()) + QString::fromLocal8Bit(theconfig->getDbDir().c_str()) +
qApp->translate("Main", qApp->translate("Main",
".\n" ".\n"
"Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed."), "Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed."),
@ -492,7 +493,6 @@ void RclMain::fileExit()
// indexing thread and a possible need to exit // indexing thread and a possible need to exit
void RclMain::periodic100() void RclMain::periodic100()
{ {
static int toggle = 0;
// Check if indexing thread done // Check if indexing thread done
if (idxthread_getStatus() != IDXTS_NULL) { if (idxthread_getStatus() != IDXTS_NULL) {
// Indexing is stopped // Indexing is stopped
@ -527,7 +527,7 @@ void RclMain::periodic100()
fileToggleIndexingAction->setEnabled(TRUE); fileToggleIndexingAction->setEnabled(TRUE);
periodictimer->setInterval(100); periodictimer->setInterval(100);
// The toggle thing is for the status to flash // The toggle thing is for the status to flash
if (toggle < 9) { if (m_periodicToggle < 9) {
QString msg = tr("Indexing in progress: "); QString msg = tr("Indexing in progress: ");
DbIxStatus status = idxthread_idxStatus(); DbIxStatus status = idxthread_idxStatus();
QString phs; QString phs;
@ -548,17 +548,17 @@ void RclMain::periodic100()
msg += QString::fromAscii(cnts) + " "; msg += QString::fromAscii(cnts) + " ";
} }
string mf;int ecnt = 0; string mf;int ecnt = 0;
string fcharset = rclconfig->getDefCharset(true); string fcharset = theconfig->getDefCharset(true);
if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) { if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) {
mf = url_encode(status.fn, 0); mf = url_encode(status.fn, 0);
} }
msg += QString::fromUtf8(mf.c_str()); msg += QString::fromUtf8(mf.c_str());
statusBar()->showMessage(msg, 4000); statusBar()->showMessage(msg, 4000);
} else if (toggle == 9) { } else if (m_periodicToggle == 9) {
statusBar()->showMessage(""); statusBar()->showMessage("");
} }
if (++toggle >= 10) if (++m_periodicToggle >= 10)
toggle = 0; m_periodicToggle = 0;
} }
if (recollNeedsExit) if (recollNeedsExit)
fileExit(); fileExit();
@ -602,7 +602,7 @@ void RclMain::startSearch(RefCntr<Rcl::SearchData> sdata)
string stemLang = (const char *)prefs.queryStemLang.toAscii(); string stemLang = (const char *)prefs.queryStemLang.toAscii();
if (stemLang == "ALL") { if (stemLang == "ALL") {
rclconfig->getConfParam("indexstemminglanguages", stemLang); theconfig->getConfParam("indexstemminglanguages", stemLang);
} }
sdata->setStemlang(stemLang); sdata->setStemlang(stemLang);
@ -692,7 +692,7 @@ void RclMain::showIndexConfig()
{ {
LOGDEB(("showIndexConfig()\n")); LOGDEB(("showIndexConfig()\n"));
if (indexConfig == 0) { if (indexConfig == 0) {
indexConfig = new ConfIndexW(0, rclconfig); indexConfig = new ConfIndexW(0, theconfig);
connect(new QShortcut(quitKeySeq, indexConfig), SIGNAL (activated()), connect(new QShortcut(quitKeySeq, indexConfig), SIGNAL (activated()),
this, SLOT (fileExit())); this, SLOT (fileExit()));
} else { } else {
@ -744,7 +744,7 @@ void RclMain::showAboutDialog()
void RclMain::showMissingHelpers() void RclMain::showMissingHelpers()
{ {
string miss = rclconfig->getMissingHelperDesc(); string miss = theconfig->getMissingHelperDesc();
QString msg = tr("External applications/commands needed and not found " QString msg = tr("External applications/commands needed and not found "
"for indexing your file types:\n\n"); "for indexing your file types:\n\n");
if (!miss.empty()) { if (!miss.empty()) {
@ -984,7 +984,7 @@ void RclMain::saveDocToFile(Rcl::Doc doc)
); );
string tofile((const char *)s.toLocal8Bit()); string tofile((const char *)s.toLocal8Bit());
TempFile temp; // not used TempFile temp; // not used
if (!FileInterner::idocToFile(temp, tofile, rclconfig, doc)) { if (!FileInterner::idocToFile(temp, tofile, theconfig, doc)) {
QMessageBox::warning(0, "Recoll", QMessageBox::warning(0, "Recoll",
tr("Cannot extract document or create " tr("Cannot extract document or create "
"temporary file")); "temporary file"));
@ -1034,13 +1034,13 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
// Look for appropriate viewer // Look for appropriate viewer
string cmdplusattr; string cmdplusattr;
if (prefs.useDesktopOpen) { if (prefs.useDesktopOpen) {
cmdplusattr = rclconfig->getMimeViewerDef("application/x-all", ""); cmdplusattr = theconfig->getMimeViewerDef("application/x-all", "");
} else { } else {
string apptag; string apptag;
map<string,string>::const_iterator it; map<string,string>::const_iterator it;
if ((it = doc.meta.find(Rcl::Doc::keyapptg)) != doc.meta.end()) if ((it = doc.meta.find(Rcl::Doc::keyapptg)) != doc.meta.end())
apptag = it->second; apptag = it->second;
cmdplusattr = rclconfig->getMimeViewerDef(doc.mimetype, apptag); cmdplusattr = theconfig->getMimeViewerDef(doc.mimetype, apptag);
} }
if (cmdplusattr.empty()) { if (cmdplusattr.empty()) {
@ -1053,7 +1053,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
// Extract possible viewer attributes // Extract possible viewer attributes
ConfSimple attrs; ConfSimple attrs;
string cmd; string cmd;
rclconfig->valueSplitAttributes(cmdplusattr, cmd, attrs); theconfig->valueSplitAttributes(cmdplusattr, cmd, attrs);
bool ignoreipath = false; bool ignoreipath = false;
if (attrs.get("ignoreipath", cmdplusattr)) if (attrs.get("ignoreipath", cmdplusattr))
ignoreipath = stringToBool(cmdplusattr); ignoreipath = stringToBool(cmdplusattr);
@ -1073,7 +1073,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
// directory // directory
string cmdpath; string cmdpath;
if (!ExecCmd::which(lcmd.front(), cmdpath)) { if (!ExecCmd::which(lcmd.front(), cmdpath)) {
cmdpath = rclconfig->findFilter(lcmd.front()); cmdpath = theconfig->findFilter(lcmd.front());
// findFilter returns its input param if the filter is not in // findFilter returns its input param if the filter is not in
// the normal places. As we already looked in the path, we // the normal places. As we already looked in the path, we
// have no use for a simple command name here (as opposed to // have no use for a simple command name here (as opposed to
@ -1128,10 +1128,10 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
// If the command wants a file but this is not a file url, or // If the command wants a file but this is not a file url, or
// there is an ipath that it won't understand, we need a temp file: // there is an ipath that it won't understand, we need a temp file:
rclconfig->setKeyDir(path_getfather(fn)); theconfig->setKeyDir(path_getfather(fn));
if ((wantsfile && fn.empty()) || (!wantsipath && !doc.ipath.empty())) { if ((wantsfile && fn.empty()) || (!wantsipath && !doc.ipath.empty())) {
TempFile temp; TempFile temp;
if (!FileInterner::idocToFile(temp, string(), rclconfig, doc)) { if (!FileInterner::idocToFile(temp, string(), theconfig, doc)) {
QMessageBox::warning(0, "Recoll", QMessageBox::warning(0, "Recoll",
tr("Cannot extract document or create " tr("Cannot extract document or create "
"temporary file")); "temporary file"));
@ -1145,7 +1145,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
// If using an actual file, check that it exists, and if it is // If using an actual file, check that it exists, and if it is
// compressed, we may need an uncompressed version // compressed, we may need an uncompressed version
if (!fn.empty() && rclconfig->mimeViewerNeedsUncomp(doc.mimetype)) { if (!fn.empty() && theconfig->mimeViewerNeedsUncomp(doc.mimetype)) {
if (access(fn.c_str(), R_OK) != 0) { if (access(fn.c_str(), R_OK) != 0) {
QMessageBox::warning(0, "Recoll", QMessageBox::warning(0, "Recoll",
tr("Can't access file: ") + tr("Can't access file: ") +
@ -1153,8 +1153,8 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
return; return;
} }
TempFile temp; TempFile temp;
if (FileInterner::isCompressed(fn, rclconfig)) { if (FileInterner::isCompressed(fn, theconfig)) {
if (!FileInterner::maybeUncompressToTemp(temp, fn, rclconfig, if (!FileInterner::maybeUncompressToTemp(temp, fn, theconfig,
doc)) { doc)) {
QMessageBox::warning(0, "Recoll", QMessageBox::warning(0, "Recoll",
tr("Can't uncompress file: ") + tr("Can't uncompress file: ") +
@ -1208,7 +1208,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc)
ncmd += " &"; ncmd += " &";
QStatusBar *stb = statusBar(); QStatusBar *stb = statusBar();
if (stb) { if (stb) {
string fcharset = rclconfig->getDefCharset(true); string fcharset = theconfig->getDefCharset(true);
string prcmd; string prcmd;
transcode(ncmd, prcmd, fcharset, "UTF-8"); transcode(ncmd, prcmd, fcharset, "UTF-8");
QString msg = tr("Executing: [") + QString msg = tr("Executing: [") +
@ -1234,7 +1234,7 @@ void RclMain::startManual(const string& index)
{ {
Rcl::Doc doc; Rcl::Doc doc;
doc.url = "file://"; doc.url = "file://";
doc.url = path_cat(doc.url, rclconfig->getDatadir()); doc.url = path_cat(doc.url, theconfig->getDatadir());
doc.url = path_cat(doc.url, "doc"); doc.url = path_cat(doc.url, "doc");
doc.url = path_cat(doc.url, "usermanual.html"); doc.url = path_cat(doc.url, "usermanual.html");
LOGDEB(("RclMain::startManual: help index is %s\n", LOGDEB(("RclMain::startManual: help index is %s\n",
@ -1382,7 +1382,7 @@ void RclMain::catgFilter(int id)
if (id != 0) { if (id != 0) {
string catg = m_catgbutvec[id]; string catg = m_catgbutvec[id];
list<string> tps; list<string> tps;
rclconfig->getMimeCatTypes(catg, tps); theconfig->getMimeCatTypes(catg, tps);
for (list<string>::const_iterator it = tps.begin(); for (list<string>::const_iterator it = tps.begin();
it != tps.end(); it++) it != tps.end(); it++)
m_filtspec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, *it); m_filtspec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, *it);

View File

@ -133,7 +133,8 @@ private:
bool m_sortspecnochange; bool m_sortspecnochange;
DocSeqSortSpec m_sortspec; DocSeqSortSpec m_sortspec;
RefCntr<DocSequence> m_source; RefCntr<DocSequence> m_source;
int m_periodicToggle;
virtual void init(); virtual void init();
virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum, virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum,
bool next); bool next);

View File

@ -21,6 +21,7 @@
#include "rclconfig.h" #include "rclconfig.h"
#include "rcldb.h" #include "rcldb.h"
#include "idxthread.h" #include "idxthread.h"
#include "ptmutex.h"
// Misc declarations in need of sharing between the UI files // Misc declarations in need of sharing between the UI files
@ -30,7 +31,11 @@ extern bool maybeOpenDb(std::string &reason, bool force = true);
/** Retrieve configured stemming languages */ /** Retrieve configured stemming languages */
bool getStemLangs(list<string>& langs); bool getStemLangs(list<string>& langs);
extern RclConfig *rclconfig; extern RclConfig *theconfig;
extern RclConfig *thestableconfig;
extern PTMutexInit thestableconfiglock;
extern void snapshotConfig();
extern Rcl::Db *rcldb; extern Rcl::Db *rcldb;
extern int recollNeedsExit; extern int recollNeedsExit;
extern int startIndexingAfterConfig; // 1st startup extern int startIndexingAfterConfig; // 1st startup

View File

@ -158,7 +158,7 @@ string QtGuiResListPager::pageTop()
string QtGuiResListPager::iconPath(const string& mtype) string QtGuiResListPager::iconPath(const string& mtype)
{ {
string iconpath; string iconpath;
rclconfig->getMimeIconName(mtype, &iconpath); theconfig->getMimeIconName(mtype, &iconpath);
return iconpath; return iconpath;
} }
@ -167,7 +167,7 @@ void QtGuiResListPager::suggest(const vector<string>uterms, vector<string>&sugg)
sugg.clear(); sugg.clear();
#ifdef RCL_USE_ASPELL #ifdef RCL_USE_ASPELL
bool noaspell = false; bool noaspell = false;
rclconfig->getConfParam("noaspell", &noaspell); theconfig->getConfParam("noaspell", &noaspell);
if (noaspell) if (noaspell)
return; return;
if (!aspell) { if (!aspell) {
@ -524,7 +524,7 @@ void ResList::displayPage()
{ {
m_pageParaToReldocnums.clear(); m_pageParaToReldocnums.clear();
clear(); clear();
m_pager->displayPage(rclconfig); m_pager->displayPage(theconfig);
LOGDEB0(("ResList::resultPageNext: hasNext %d hasPrev %d\n", LOGDEB0(("ResList::resultPageNext: hasNext %d hasPrev %d\n",
m_pager->hasPrev(), m_pager->hasNext())); m_pager->hasPrev(), m_pager->hasNext()));
emit prevPageAvailable(m_pager->hasPrev()); emit prevPageAvailable(m_pager->hasPrev());

View File

@ -102,7 +102,7 @@ const string& ResTablePager::parFormat()
string ResTablePager::iconPath(const string& mtype) string ResTablePager::iconPath(const string& mtype)
{ {
string iconpath; string iconpath;
rclconfig->getMimeIconName(mtype, &iconpath); theconfig->getMimeIconName(mtype, &iconpath);
return iconpath; return iconpath;
} }
@ -229,8 +229,8 @@ RecollModel::RecollModel(const QStringList fields, QObject *parent)
// Add dynamic "stored" fields to the full column list. This // Add dynamic "stored" fields to the full column list. This
// could be protected to be done only once, but it's no real // could be protected to be done only once, but it's no real
// problem // problem
if (rclconfig) { if (theconfig) {
const set<string>& stored = rclconfig->getStoredFields(); const set<string>& stored = theconfig->getStoredFields();
for (set<string>::const_iterator it = stored.begin(); for (set<string>::const_iterator it = stored.begin();
it != stored.end(); it++) { it != stored.end(); it++) {
if (o_displayableFields.find(*it) == o_displayableFields.end()) { if (o_displayableFields.find(*it) == o_displayableFields.end()) {
@ -535,7 +535,7 @@ void ResTable::onTableView_currentChanged(const QModelIndex& index)
m_detail->clear(); m_detail->clear();
m_detaildocnum = index.row(); m_detaildocnum = index.row();
m_detaildoc = doc; m_detaildoc = doc;
m_pager->displayDoc(rclconfig, index.row(), doc, m_model->m_hdata); m_pager->displayDoc(theconfig, index.row(), doc, m_model->m_hdata);
} else { } else {
m_detaildocnum = -1; m_detaildocnum = -1;
} }

View File

@ -82,8 +82,8 @@ void SearchClauseW::languageChange()
// sTpCMB->insertItem(tr("Complex clause"));//6 // sTpCMB->insertItem(tr("Complex clause"));//6
fldCMB->addItem(tr("In field")); fldCMB->addItem(tr("In field"));
if (rclconfig) { if (theconfig) {
set<string> fields = rclconfig->getIndexedFields(); set<string> fields = theconfig->getIndexedFields();
for (set<string>::const_iterator it = fields.begin(); for (set<string>::const_iterator it = fields.begin();
it != fields.end(); it++) { it != fields.end(); it++) {
// Some fields don't make sense here // Some fields don't make sense here

View File

@ -50,7 +50,7 @@ void SpellW::init()
/*2*/expTypeCMB->addItem(tr("Stem expansion")); /*2*/expTypeCMB->addItem(tr("Stem expansion"));
#ifdef RCL_USE_ASPELL #ifdef RCL_USE_ASPELL
bool noaspell = false; bool noaspell = false;
rclconfig->getConfParam("noaspell", &noaspell); theconfig->getConfParam("noaspell", &noaspell);
if (!noaspell) if (!noaspell)
/*3*/expTypeCMB->addItem(tr("Spelling/Phonetic")); /*3*/expTypeCMB->addItem(tr("Spelling/Phonetic"));
#endif #endif

View File

@ -132,9 +132,9 @@ void SSearch::startSimpleSearch()
if (tp == SST_LANG) { if (tp == SST_LANG) {
string reason; string reason;
if (prefs.autoSuffsEnable) if (prefs.autoSuffsEnable)
sdata = wasaStringToRcl(rclconfig, u8, reason, (const char *)prefs.autoSuffs.toUtf8()); sdata = wasaStringToRcl(theconfig, u8, reason, (const char *)prefs.autoSuffs.toUtf8());
else else
sdata = wasaStringToRcl(rclconfig, u8, reason); sdata = wasaStringToRcl(theconfig, u8, reason);
if (sdata == 0) { if (sdata == 0) {
QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " + QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " +
QString::fromAscii(reason.c_str())); QString::fromAscii(reason.c_str()));
@ -280,7 +280,7 @@ void SSearch::completion()
Rcl::TermMatchResult tmres; Rcl::TermMatchResult tmres;
string stemLang = (const char *)prefs.queryStemLang.toAscii(); string stemLang = (const char *)prefs.queryStemLang.toAscii();
if (stemLang == "ALL") { if (stemLang == "ALL") {
rclconfig->getConfParam("indexstemminglanguages", stemLang); theconfig->getConfParam("indexstemminglanguages", stemLang);
} }
if (!rcldb->termMatch(Rcl::Db::ET_WILD, stemLang, s, tmres, max) || if (!rcldb->termMatch(Rcl::Db::ET_WILD, stemLang, s, tmres, max) ||
tmres.entries.size() == 0) { tmres.entries.size() == 0) {

View File

@ -372,7 +372,7 @@ void UIPrefsDialog::addExtraDbPB_clicked()
} }
struct stat st1, st2; struct stat st1, st2;
stat(dbdir.c_str(), &st1); stat(dbdir.c_str(), &st1);
string rcldbdir = rclconfig->getDbDir(); string rcldbdir = theconfig->getDbDir();
stat(rcldbdir.c_str(), &st2); stat(rcldbdir.c_str(), &st2);
path_catslash(rcldbdir); path_catslash(rcldbdir);

View File

@ -51,7 +51,7 @@ void ViewAction::fillLists()
actionsLV->clear(); actionsLV->clear();
actionsLV->verticalHeader()->setDefaultSectionSize(20); actionsLV->verticalHeader()->setDefaultSectionSize(20);
vector<pair<string, string> > defs; vector<pair<string, string> > defs;
rclconfig->getMimeViewerDefs(defs); theconfig->getMimeViewerDefs(defs);
actionsLV->setRowCount(defs.size()); actionsLV->setRowCount(defs.size());
int row = 0; int row = 0;
for (vector<pair<string, string> >::const_iterator it = defs.begin(); for (vector<pair<string, string> >::const_iterator it = defs.begin();
@ -128,7 +128,7 @@ void ViewAction::editActions()
string sact = (const char *)newaction.toLocal8Bit(); string sact = (const char *)newaction.toLocal8Bit();
for (list<string>::const_iterator it = mtypes.begin(); for (list<string>::const_iterator it = mtypes.begin();
it != mtypes.end(); it++) { it != mtypes.end(); it++) {
rclconfig->setMimeViewerDef(*it, sact); theconfig->setMimeViewerDef(*it, sact);
} }
fillLists(); fillLists();
} }

View File

@ -372,7 +372,7 @@ string ResListPager::detailsLink()
const string &ResListPager::parFormat() const string &ResListPager::parFormat()
{ {
const static string format("<img src=\"%I\" align=\"left\">" static const string format("<img src=\"%I\" align=\"left\">"
"%R %S %L &nbsp;&nbsp;<b>%T</b><br>" "%R %S %L &nbsp;&nbsp;<b>%T</b><br>"
"%M&nbsp;%D&nbsp;&nbsp;&nbsp;<i>%U</i><br>" "%M&nbsp;%D&nbsp;&nbsp;&nbsp;<i>%U</i><br>"
"%A %K"); "%A %K");

View File

@ -80,7 +80,7 @@ string version_string(){
// Synthetic abstract marker (to discriminate from abstract actually // Synthetic abstract marker (to discriminate from abstract actually
// found in document) // found in document)
const static string rclSyntAbs("?!#@"); static const string rclSyntAbs("?!#@");
// Compute the unique term used to link documents to their origin. // Compute the unique term used to link documents to their origin.
// "Q" + external udi // "Q" + external udi

View File

@ -38,7 +38,7 @@ namespace Rcl {
namespace StemDb { namespace StemDb {
const static string stemdirstem = "stem_"; static const string stemdirstem = "stem_";
/// Compute name of stem db for given base database and language /// Compute name of stem db for given base database and language
static string stemdbname(const string& dbdir, const string& lang) static string stemdbname(const string& dbdir, const string& lang)

View File

@ -19,7 +19,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -44,6 +43,7 @@
#include "smallut.h" #include "smallut.h"
#include "netcon.h" #include "netcon.h"
#include "closefrom.h" #include "closefrom.h"
#include "ptmutex.h"
#ifndef NO_NAMESPACES #ifndef NO_NAMESPACES
using namespace std; using namespace std;
@ -316,38 +316,20 @@ private:
// The netcon selectloop that doexec() uses for reading/writing would // The netcon selectloop that doexec() uses for reading/writing would
// be complicated to render thread-safe. Use locking to ensure only // be complicated to render thread-safe. Use locking to ensure only
// one thread in there // one thread in there
class ExecLocking { static PTMutexInit o_lock;
public:
pthread_mutex_t m_mutex;
ExecLocking()
{
pthread_mutex_init(&m_mutex, 0);
}
};
ExecLocking o_lock;
class ExecLocker {
public:
ExecLocker()
{
pthread_mutex_lock(&o_lock.m_mutex);
}
~ExecLocker()
{
pthread_mutex_unlock(&o_lock.m_mutex);
}
};
int ExecCmd::doexec(const string &cmd, const list<string>& args, int ExecCmd::doexec(const string &cmd, const list<string>& args,
const string *input, string *output) const string *input, string *output)
{ {
// Only one thread allowed in here...
PTMutexLocker locker(o_lock);
if (startExec(cmd, args, input != 0, output != 0) < 0) { if (startExec(cmd, args, input != 0, output != 0) < 0) {
return -1; return -1;
} }
// Cleanup in case we return early // Cleanup in case we return early
ExecCmdRsrc e(this); ExecCmdRsrc e(this);
// Only one thread allowed in here...
ExecLocker locker;
int ret = 0; int ret = 0;
if (input || output) { if (input || output) {

52
src/utils/ptmutex.h Normal file
View File

@ -0,0 +1,52 @@
/* Copyright (C) 2011 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.
*/
#ifndef _PTMUTEX_H_INCLUDED_
#define _PTMUTEX_H_INCLUDED_
#include <pthread.h>
/// A trivial wrapper/helper for pthread mutex locks
/// Init lock. Used as a single PTMutexInit static object.
class PTMutexInit {
public:
pthread_mutex_t m_mutex;
PTMutexInit()
{
pthread_mutex_init(&m_mutex, 0);
}
};
/// Take the lock when constructed, release when deleted
class PTMutexLocker {
public:
PTMutexLocker(PTMutexInit& l) : m_lock(l)
{
m_status = pthread_mutex_lock(&m_lock.m_mutex);
}
~PTMutexLocker()
{
pthread_mutex_unlock(&m_lock.m_mutex);
}
int ok() {return m_status == 0;}
private:
PTMutexInit& m_lock;
int m_status;
};
#endif /* _PTMUTEX_H_INCLUDED_ */