revamped history feature to be udi-based while supporting old format

This commit is contained in:
dockes
2009-11-23 16:12:20 +00:00
parent 35f32a18a8
commit b7166eb67e
12 changed files with 291 additions and 266 deletions
+85 -8
View File
@@ -25,6 +25,84 @@ static char rcsid[] = "@(#$Id: docseqhist.cpp,v 1.4 2008-09-29 08:59:20 dockes E
#include "rcldb.h"
#include "fileudi.h"
#include "internfile.h"
#include "base64.h"
#include "debuglog.h"
#include "smallut.h"
// Encode document history entry:
// U + Unix time + base64 of udi
// The U distinguishes udi-based entries from older fn+ipath ones
bool RclDHistoryEntry::encode(string& value)
{
char chartime[20];
sprintf(chartime, "%ld", unixtime);
string budi;
base64_encode(udi, budi);
value = string("U ") + string(chartime) + " " + budi;
return true;
}
// Decode. We support historical entries which were like "time b64fn [b64ipath]"
// Current entry format is "U time b64udi"
bool RclDHistoryEntry::decode(const string &value)
{
list<string> vall;
stringToStrings(value, vall);
list<string>::const_iterator it = vall.begin();
udi.erase();
string fn, ipath;
switch (vall.size()) {
case 2:
// Old fn+ipath, null ipath case
unixtime = atol((*it++).c_str());
base64_decode(*it++, fn);
break;
case 3:
if (!it->compare("U")) {
// New udi-based entry
it++;
unixtime = atol((*it++).c_str());
base64_decode(*it++, udi);
} else {
// Old fn + ipath. We happen to know how to build an udi
unixtime = atol((*it++).c_str());
base64_decode(*it++, fn);
base64_decode(*it, ipath);
}
break;
default:
return false;
}
if (!fn.empty()) {
// Old style entry found, make an udi, using the fs udi maker
make_udi(fn, ipath, udi);
}
LOGDEB(("RclDHistoryEntry::decode: udi [%s]\n", udi.c_str()));
return true;
}
bool RclDHistoryEntry::equal(const DynConfEntry& other)
{
const RclDHistoryEntry& e = dynamic_cast<const RclDHistoryEntry&>(other);
return e.udi == udi;
}
bool historyEnterDoc(RclDynConf *dncf, const string& udi)
{
LOGDEB(("historyEnterDoc: [%s] into %s\n",
udi.c_str(), dncf->getFilename().c_str()));
RclDHistoryEntry ne(time(0), udi);
RclDHistoryEntry scratch;
return dncf->insertNew(docHistSubKey, ne, scratch, 200);
}
list<RclDHistoryEntry> getDocHistory(RclDynConf* dncf)
{
return dncf->getList<RclDHistoryEntry>(docHistSubKey);
}
bool DocSequenceHistory::getDoc(int num, Rcl::Doc &doc, string *sh)
{
@@ -32,7 +110,7 @@ bool DocSequenceHistory::getDoc(int num, Rcl::Doc &doc, string *sh)
if (!m_hist)
return false;
if (m_hlist.empty())
m_hlist = m_hist->getDocHistory();
m_hlist = getDocHistory(m_hist);
if (num < 0 || num >= (int)m_hlist.size())
return false;
@@ -48,7 +126,8 @@ bool DocSequenceHistory::getDoc(int num, Rcl::Doc &doc, string *sh)
while (skip--)
m_it++;
if (sh) {
if (m_prevtime < 0 || abs (float(m_prevtime) - float(m_it->unixtime)) > 86400) {
if (m_prevtime < 0 ||
abs (float(m_prevtime) - float(m_it->unixtime)) > 86400) {
m_prevtime = m_it->unixtime;
time_t t = (time_t)(m_it->unixtime);
*sh = string(ctime(&t));
@@ -57,12 +136,10 @@ bool DocSequenceHistory::getDoc(int num, Rcl::Doc &doc, string *sh)
} else
sh->erase();
}
string udi;
make_udi(m_it->fn, m_it->ipath, udi);
bool ret = m_db->getDoc(udi, doc);
bool ret = m_db->getDoc(m_it->udi, doc);
if (!ret) {
doc.url = string("file://") + m_it->fn;
doc.ipath = m_it->ipath;
doc.url = "UNKNOWN";
doc.ipath = "";
}
return ret;
}
@@ -79,6 +156,6 @@ bool DocSequenceHistory::getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc)
int DocSequenceHistory::getResCnt()
{
if (m_hlist.empty())
m_hlist = m_hist->getDocHistory();
m_hlist = getDocHistory(m_hist);
return m_hlist.size();
}
+19 -3
View File
@@ -19,18 +19,32 @@
/* @(#$Id: docseqhist.h,v 1.3 2008-09-29 08:59:20 dockes Exp $ (C) 2004 J.F.Dockes */
#include "docseq.h"
#include "history.h"
#include "dynconf.h"
namespace Rcl {
class Db;
}
/** DynConf Document history entry */
class RclDHistoryEntry : public DynConfEntry {
public:
RclDHistoryEntry() : unixtime(0) {}
RclDHistoryEntry(long t, const string& u)
: unixtime(t), udi(u) {}
virtual ~RclDHistoryEntry() {}
virtual bool decode(const string &value);
virtual bool encode(string& value);
virtual bool equal(const DynConfEntry& other);
long unixtime;
string udi;
};
/** A DocSequence coming from the history file.
* History is kept as a list of urls. This queries the db to fetch
* metadata for an url key */
class DocSequenceHistory : public DocSequence {
public:
DocSequenceHistory(Rcl::Db *d, RclHistory *h, const string &t)
DocSequenceHistory(Rcl::Db *d, RclDynConf *h, const string &t)
: DocSequence(t), m_db(d), m_hist(h), m_prevnum(-1), m_prevtime(-1) {}
virtual ~DocSequenceHistory() {}
@@ -41,7 +55,7 @@ class DocSequenceHistory : public DocSequence {
void setDescription(const string& desc) {m_description = desc;}
private:
Rcl::Db *m_db;
RclHistory *m_hist;
RclDynConf *m_hist;
int m_prevnum;
long m_prevtime;
string m_description; // This is just an nls translated 'doc history'
@@ -49,4 +63,6 @@ class DocSequenceHistory : public DocSequence {
list<RclDHistoryEntry>::const_iterator m_it;
};
extern bool historyEnterDoc(RclDynConf *dncf, const string& udi);
#endif /* _DOCSEQ_H_INCLUDED_ */
+43 -101
View File
@@ -1,5 +1,5 @@
#ifndef lint
static char rcsid[] = "@(#$Id: history.cpp,v 1.9 2007-12-13 06:58:21 dockes Exp $ (C) 2005 J.F.Dockes";
static char rcsid[] = "@(#$Id: dynconf.cpp,v 1.9 2007-12-13 06:58:21 dockes Exp $ (C) 2005 J.F.Dockes";
#endif
/*
* This program is free software; you can redistribute it and/or modify
@@ -23,7 +23,7 @@ static char rcsid[] = "@(#$Id: history.cpp,v 1.9 2007-12-13 06:58:21 dockes Exp
#include <time.h>
#include <cstdlib>
#include "history.h"
#include "dynconf.h"
#include "base64.h"
#include "smallut.h"
#include "debuglog.h"
@@ -32,63 +32,20 @@ static char rcsid[] = "@(#$Id: history.cpp,v 1.9 2007-12-13 06:58:21 dockes Exp
using namespace std;
#endif
// Encode/decode document history entry: Unix time + base64 of fn +
// base64 of ipath separated by a space. If ipath is not set, there
// are only 2 parts
bool RclDHistoryEntry::encode(string& value)
{
char chartime[20];
sprintf(chartime, "%ld", unixtime);
string bfn, bipath;
base64_encode(fn, bfn);
base64_encode(ipath, bipath);
value = string(chartime) + " " + bfn + " " + bipath;
return true;
}
bool RclDHistoryEntry::decode(const string &value)
{
list<string> vall;
stringToStrings(value, vall);
list<string>::const_iterator it1 = vall.begin();
if (vall.size() < 2)
return false;
unixtime = atol((*it1++).c_str());
base64_decode(*it1++, fn);
if (vall.size() == 3)
base64_decode(*it1, ipath);
else
ipath.erase();
return true;
}
bool RclDHistoryEntry::equal(const HistoryEntry& other)
{
const RclDHistoryEntry& e = dynamic_cast<const RclDHistoryEntry&>(other);
return e.fn == fn && e.ipath == ipath;
}
// Well known keys for history and external indexes.
const string docHistSubKey = "docs";
const string allEdbsSk = "allExtDbs";
const string actEdbsSk = "actExtDbs";
// Encode/decode simple string. base64 used to avoid problems with
// strange chars
bool RclSListEntry::encode(string& enc)
// @param sk section this is for
// @param n new entry
// @param s a scratch entry used for decoding and comparisons.
// This avoids templating this routine for the actual entry type.
bool RclDynConf::insertNew(const string &sk, DynConfEntry &n, DynConfEntry &s,
int maxlen)
{
base64_encode(value, enc);
return true;
}
bool RclSListEntry::decode(const string &enc)
{
base64_decode(enc, value);
return true;
}
bool RclSListEntry::equal(const HistoryEntry& other)
{
const RclSListEntry& e = dynamic_cast<const RclSListEntry&>(other);
return e.value == value;
}
bool RclHistory::insertNew(const string &sk, HistoryEntry &n, HistoryEntry &s)
{
// Is this doc already in history ? If it is we remove the old entry
// Is this doc already in list ? If it is we remove the old entry
list<string> names = m_data.getNames(sk);
list<string>::const_iterator it;
bool changed = false;
@@ -111,13 +68,13 @@ bool RclHistory::insertNew(const string &sk, HistoryEntry &n, HistoryEntry &s)
if (changed)
names = m_data.getNames(sk);
// How many do we have
if (names.size() >= m_mlen) {
// Need to prune ?
if (maxlen > 0 && names.size() >= (unsigned int)maxlen) {
// Need to erase entries until we're back to size. Note that
// we don't ever reset numbers. Problems will arise when
// history is 4 billion entries old
it = names.begin();
for (unsigned int i = 0; i < names.size() - m_mlen + 1; i++, it++) {
for (unsigned int i = 0; i < names.size() - maxlen + 1; i++, it++) {
m_data.erase(*it, sk);
}
}
@@ -137,12 +94,10 @@ bool RclHistory::insertNew(const string &sk, HistoryEntry &n, HistoryEntry &s)
return false;
}
return true;
}
bool RclHistory::eraseAll(const string &sk)
bool RclDynConf::eraseAll(const string &sk)
{
// Is this doc already in history ? If it is we remove the old entry
list<string> names = m_data.getNames(sk);
list<string>::const_iterator it;
for (it = names.begin(); it != names.end(); it++) {
@@ -150,31 +105,36 @@ bool RclHistory::eraseAll(const string &sk)
}
return true;
}
bool RclHistory::truncate(const string &sk, unsigned int n)
// Generic string list specialization ///////////////////////////////////
// Encode/decode simple string. base64 used to avoid problems with
// strange chars
bool RclSListEntry::encode(string& enc)
{
// Is this doc already in history ? If it is we remove the old entry
list<string> names = m_data.getNames(sk);
if (names.size() <= n)
return true;
unsigned int i = 0;
for (list<string>::const_iterator it = names.begin();
it != names.end(); it++, i++) {
if (i >= n)
m_data.erase(*it, sk);
}
base64_encode(value, enc);
return true;
}
bool RclHistory::enterString(const string sk, const string value)
bool RclSListEntry::decode(const string &enc)
{
base64_decode(enc, value);
return true;
}
bool RclSListEntry::equal(const DynConfEntry& other)
{
const RclSListEntry& e = dynamic_cast<const RclSListEntry&>(other);
return e.value == value;
}
bool RclDynConf::enterString(const string sk, const string value, int maxlen)
{
RclSListEntry ne(value);
RclSListEntry scratch;
return insertNew(sk, ne, scratch);
return insertNew(sk, ne, scratch, maxlen);
}
list<string> RclHistory::getStringList(const string sk)
list<string> RclDynConf::getStringList(const string sk)
{
list<RclSListEntry> el = getHistory<RclSListEntry>(sk);
list<RclSListEntry> el = getList<RclSListEntry>(sk);
list<string> sl;
for (list<RclSListEntry>::const_iterator it = el.begin();
it != el.end(); it++)
@@ -182,26 +142,8 @@ list<string> RclHistory::getStringList(const string sk)
return sl;
}
string RclHistory::docSubkey = "docs";
/// *************** History entries specific methods
bool RclHistory::enterDoc(const string fn, const string ipath)
{
LOGDEB(("RclDHistory::enterDoc: [%s] [%s] into %s\n",
fn.c_str(), ipath.c_str(), m_data.getFilename().c_str()));
RclDHistoryEntry ne(time(0), fn, ipath);
RclDHistoryEntry scratch;
return insertNew(docSubkey, ne, scratch);
}
list<RclDHistoryEntry> RclHistory::getDocHistory()
{
return getHistory<RclDHistoryEntry>(docSubkey);
}
#else
#include <string>
#include <iostream>
@@ -216,7 +158,7 @@ static string thisprog;
static string usage =
"trhist [opts] <filename>\n"
" [-s <subkey>]: specify subkey (default: RclHistory::docSubkey)\n"
" [-s <subkey>]: specify subkey (default: RclDynConf::docHistSubKey)\n"
" [-e] : erase all\n"
" [-a <string>] enter string (needs -s, no good for history entries\n"
"\n"
@@ -236,7 +178,7 @@ static int op_flags;
int main(int argc, char **argv)
{
string sk = RclHistory::docSubkey;
string sk = "docs";
string value;
thisprog = argv[0];
@@ -264,7 +206,7 @@ int main(int argc, char **argv)
Usage();
string filename = *argv++;argc--;
RclHistory hist(filename, 5);
RclDynConf hist(filename, 5);
DebugLog::getdbl()->setloglevel(DEBDEB1);
DebugLog::setfilename("stderr");
+123
View File
@@ -0,0 +1,123 @@
/*
* 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 _DYNCONF_H_INCLUDED_
#define _DYNCONF_H_INCLUDED_
/* @(#$Id: dynconf.h,v 1.7 2007-06-20 13:16:11 dockes Exp $ (C) 2004 J.F.Dockes */
/**
* Dynamic configuration storage
*
* This used to be called "history" because of the initial usage.
* Used to store some parameters which would fit neither in recoll.conf,
* basically because they change a lot, nor in the QT preferences file, mostly
* because they are specific to a configuration directory.
* Examples:
* - History of documents selected for preview
* - Active and inactive external databases (depend on the
* configuration directory)
* - ...
*
* The storage is performed in a ConfSimple file, with subkeys and
* encodings which depend on the data stored. Under each section, the keys
* are sequential numeric, so this basically manages a set of lists.
*
*/
#include <string>
#include <list>
#include <utility>
#include "conftree.h"
#ifndef NO_NAMESPACES
using namespace std;
#endif
// Entry interface.
class DynConfEntry {
public:
virtual ~DynConfEntry() {}
virtual bool decode(const string &value) = 0;
virtual bool encode(string& value) = 0;
virtual bool equal(const DynConfEntry &other) = 0;
};
/** String storage generic object */
class RclSListEntry : public DynConfEntry {
public:
RclSListEntry() {}
RclSListEntry(const string& v) : value(v) {}
virtual ~RclSListEntry() {}
virtual bool decode(const string &enc);
virtual bool encode(string& enc);
virtual bool equal(const DynConfEntry& other);
string value;
};
/** The dynamic configuration class */
class RclDynConf {
public:
RclDynConf(const string &fn)
: m_data(fn.c_str()) {}
bool ok() {return m_data.getStatus() == ConfSimple::STATUS_RW;}
string getFilename() {return m_data.getFilename();}
// Generic methods
bool eraseAll(const string& sk);
bool insertNew(const string& sk, DynConfEntry &n, DynConfEntry &s,
int maxlen = -1);
template<typename Tp> list<Tp> getList(const string& sk);
// Specialized methods for simple string lists, designated by the
// subkey value
bool enterString(const string sk, const string value, int maxlen = -1);
list<string> getStringList(const string sk);
private:
unsigned int m_mlen;
ConfSimple m_data;
};
template<typename Tp> list<Tp> RclDynConf::getList(const string &sk)
{
list<Tp> mlist;
Tp entry;
list<string> names = m_data.getNames(sk);
for (list<string>::const_iterator it = names.begin();
it != names.end(); it++) {
string value;
if (m_data.get(*it, value, sk)) {
if (!entry.decode(value))
continue;
mlist.push_front(entry);
}
}
return mlist;
}
// Defined subkeys. Values in dynconf.cpp
// History
extern const string docHistSubKey;
// All external indexes
extern const string allEdbsSk;
// Active external indexes
extern const string actEdbsSk;
#endif /* _DYNCONF_H_INCLUDED_ */
-134
View File
@@ -1,134 +0,0 @@
/*
* 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 _HISTORY_H_INCLUDED_
#define _HISTORY_H_INCLUDED_
/* @(#$Id: history.h,v 1.7 2007-06-20 13:16:11 dockes Exp $ (C) 2004 J.F.Dockes */
/**
* Dynamic configuration storage
*
* The term "history" is a misnomer as this code is now used to save
* all dynamic parameters except those that are stored in the global
* recollrc QT preferences file. Examples:
* - History of documents selected for preview
* - Active and inactive external databases (these should depend on the
* configuration directory and cant be stored in recollrc).
* - ...
*
* The storage is performed in a ConSimple file, with subkeys and
* encodings which depend on the data stored.
*
*/
#include <string>
#include <list>
#include <utility>
#include "conftree.h"
#ifndef NO_NAMESPACES
using namespace std;
#endif
class HistoryEntry {
public:
virtual ~HistoryEntry() {}
virtual bool decode(const string &value) = 0;
virtual bool encode(string& value) = 0;
virtual bool equal(const HistoryEntry &other) = 0;
};
/** Document history entry */
class RclDHistoryEntry : public HistoryEntry {
public:
RclDHistoryEntry() : unixtime(0) {}
RclDHistoryEntry(long t, const string& f, const string& i)
: unixtime(t), fn(f), ipath(i) {}
virtual ~RclDHistoryEntry() {}
virtual bool decode(const string &value);
virtual bool encode(string& value);
virtual bool equal(const HistoryEntry& other);
long unixtime;
string fn;
string ipath;
};
/** String storage generic object */
class RclSListEntry : public HistoryEntry {
public:
RclSListEntry() {}
RclSListEntry(const string& v) : value(v) {}
virtual ~RclSListEntry() {}
virtual bool decode(const string &enc);
virtual bool encode(string& enc);
virtual bool equal(const HistoryEntry& other);
string value;
};
/**
* The history class. This uses a ConfSimple for storage, and should be
* renamed something like dynconf, as it is used to stored quite a few
* things beyond doc history: all dynamic configuration parameters that are
* not suitable for QT settings because they are specific to a RECOLL_CONFDIR
*/
class RclHistory {
public:
RclHistory(const string &fn, unsigned int maxsize=100)
: m_mlen(maxsize), m_data(fn.c_str()) {}
bool ok() {return m_data.getStatus() == ConfSimple::STATUS_RW;}
// Specific methods for history entries. These are for convenience, they
// just call regular methods with key RclHistory::docSubkey;
bool enterDoc(const string fn, const string ipath);
list<RclDHistoryEntry> getDocHistory();
// Generic methods used for string lists, designated by the subkey value
bool enterString(const string sk, const string value);
list<string> getStringList(const string sk);
bool eraseAll(const string& sk);
bool truncate(const string& sk, unsigned int n);
static string docSubkey;
private:
unsigned int m_mlen;
ConfSimple m_data;
bool insertNew(const string& sk, HistoryEntry &n, HistoryEntry &s);
template<typename Tp> list<Tp> getHistory(const string& sk);
};
template<typename Tp> list<Tp> RclHistory::getHistory(const string &sk)
{
list<Tp> mlist;
Tp entry;
list<string> names = m_data.getNames(sk);
for (list<string>::const_iterator it = names.begin();
it != names.end(); it++) {
string value;
if (m_data.get(*it, value, sk)) {
if (!entry.decode(value))
continue;
mlist.push_front(entry);
}
}
return mlist;
}
#endif /* _HISTORY_H_INCLUDED_ */