add possibility to import tags from external app, e.g. tmsu

This commit is contained in:
Jean-Francois Dockes
2013-01-22 18:30:18 +01:00
parent fe2f32fc38
commit 4272939c3d
16 changed files with 221 additions and 82 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ subtreelist : $(SUBTREELIST_OBJS)
subtreelist.o : subtreelist.cpp
$(CXX) $(ALL_CXXFLAGS) -DTEST_SUBTREELIST -c subtreelist.cpp
MIMETYPE_OBJS= trmimetype.o $(BIGLIB)
MIMETYPE_OBJS= trmimetype.o
mimetype : $(MIMETYPE_OBJS)
$(CXX) $(ALL_CXXFLAGS) -o mimetype $(MIMETYPE_OBJS) \
$(LIBRECOLL) $(LIBICONV) $(LIBSYS)
+99 -14
View File
@@ -45,6 +45,7 @@
#include "fileudi.h"
#include "cancelcheck.h"
#include "rclinit.h"
#include "execmd.h"
// When using extended attributes, we have to use the ctime.
// This is quite an expensive price to pay...
@@ -71,12 +72,13 @@ extern void *FsIndexerDbUpdWorker(void*);
class InternfileTask {
public:
InternfileTask(const std::string &f, const struct stat *i_stp,
map<string,string> lfields)
: fn(f), statbuf(*i_stp), localfields(lfields)
map<string,string> lfields, vector<MDReaper> reapers)
: fn(f), statbuf(*i_stp), localfields(lfields), mdreapers(reapers)
{}
string fn;
struct stat statbuf;
map<string,string> localfields;
vector<MDReapers> mdreapers;
};
extern void *FsIndexerInternfileWorker(void*);
#endif // IDX_THREADS
@@ -108,6 +110,7 @@ FsIndexer::FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc)
{
LOGDEB1(("FsIndexer::FsIndexer\n"));
m_havelocalfields = m_config->hasNameAnywhere("localfields");
m_havemdreapers = m_config->hasNameAnywhere("metadatacmds");
#ifdef IDX_THREADS
m_stableconfig = new RclConfig(*m_config);
@@ -311,6 +314,9 @@ bool FsIndexer::indexFiles(list<string>& files, ConfIndexer::IxFlag flag)
m_config->setKeyDir(path_getfather(*it));
if (m_havelocalfields)
localfieldsfromconf();
if (m_havemdreapers)
mdreapersfromconf();
bool follow = false;
m_config->getConfParam("followLinks", &follow);
@@ -396,21 +402,90 @@ out:
// Local fields can be set for fs subtrees in the configuration file
void FsIndexer::localfieldsfromconf()
{
LOGDEB0(("FsIndexer::localfieldsfromconf\n"));
LOGDEB1(("FsIndexer::localfieldsfromconf\n"));
string sfields;
m_config->getConfParam("localfields", sfields);
if (!sfields.compare(m_slocalfields))
return;
m_slocalfields = sfields;
m_localfields.clear();
m_config->addLocalFields(&m_localfields);
if (sfields.empty())
return;
string value;
ConfSimple attrs;
m_config->valueSplitAttributes(sfields, value, attrs);
vector<string> nmlst = attrs.getNames(cstr_null);
for (vector<string>::const_iterator it = nmlst.begin();
it != nmlst.end(); it++) {
attrs.get(*it, m_localfields[*it]);
}
}
//
void FsIndexer::setlocalfields(map<string, string> fields, Rcl::Doc& doc)
void FsIndexer::setlocalfields(const map<string, string>& fields, Rcl::Doc& doc)
{
for (map<string, string>::const_iterator it = fields.begin();
it != fields.end(); it++) {
it != fields.end(); it++) {
// Should local fields override those coming from the document
// ? I think not, but not too sure
// ? I think not, but not too sure. We could also chose to
// concatenate the values ?
if (doc.meta.find(it->second) == doc.meta.end()) {
doc.meta[it->first] = it->second;
}
}
}
}
// Metadata gathering commands
void FsIndexer::mdreapersfromconf()
{
LOGDEB1(("FsIndexer::mdreapersfromconf\n"));
string sreapers;
m_config->getConfParam("metadatacmds", sreapers);
if (!sreapers.compare(m_smdreapers))
return;
m_smdreapers = sreapers;
m_mdreapers.clear();
if (sreapers.empty())
return;
string value;
ConfSimple attrs;
m_config->valueSplitAttributes(sreapers, value, attrs);
vector<string> nmlst = attrs.getNames(cstr_null);
for (vector<string>::const_iterator it = nmlst.begin();
it != nmlst.end(); it++) {
MDReaper reaper;
reaper.fieldname = m_config->fieldCanon(*it);
string s;
attrs.get(*it, s);
stringToStrings(s, reaper.cmdv);
m_mdreapers.push_back(reaper);
}
}
void FsIndexer::reapmetadata(const vector<MDReaper>& reapers, const string& fn,
Rcl::Doc& doc)
{
map<char,string> smap = create_map<char, string>('f', fn);
for (vector<MDReaper>::const_iterator rp = reapers.begin();
rp != reapers.end(); rp++) {
vector<string> cmd;
for (vector<string>::const_iterator it = rp->cmdv.begin();
it != rp->cmdv.end(); it++) {
string s;
pcSubst(*it, s, smap);
cmd.push_back(s);
}
string output;
if (ExecCmd::backtick(cmd, output)) {
doc.meta[rp->fieldname] += string(" ") + output;
}
}
}
@@ -515,6 +590,8 @@ FsIndexer::processone(const std::string &fn, const struct stat *stp,
// Adjust local fields from config for this subtree
if (m_havelocalfields)
localfieldsfromconf();
if (m_havemdreapers)
mdreapersfromconf();
if (flg == FsTreeWalker::FtwDirReturn)
return FsTreeWalker::FtwOk;
@@ -522,7 +599,8 @@ FsIndexer::processone(const std::string &fn, const struct stat *stp,
#ifdef IDX_THREADS
if (m_haveInternQ) {
InternfileTask *tp = new InternfileTask(fn, stp, m_localfields);
InternfileTask *tp = new InternfileTask(fn, stp, m_localfields,
m_mdreapers);
if (m_iwqueue.put(tp)) {
return FsTreeWalker::FtwOk;
} else {
@@ -531,14 +609,16 @@ FsIndexer::processone(const std::string &fn, const struct stat *stp,
}
#endif
return processonefile(m_config, m_tmpdir, fn, stp, m_localfields);
return processonefile(m_config, m_tmpdir, fn, stp, m_localfields,
m_mdreapers);
}
FsTreeWalker::Status
FsIndexer::processonefile(RclConfig *config, TempDir& tmpdir,
const std::string &fn, const struct stat *stp,
map<string, string> localfields)
const map<string, string>& localfields,
const vector<MDReaper>& mdreapers)
{
////////////////////
// Check db up to date ? Doing this before file type
@@ -623,9 +703,13 @@ FsIndexer::processonefile(RclConfig *config, TempDir& tmpdir,
// We'll change the signature to ensure that the indexing will
// be retried every time.
// Internal access path for multi-document files
if (doc.ipath.empty())
// Internal access path for multi-document files. If empty, this is
// for the main file.
if (doc.ipath.empty()) {
hadNullIpath = true;
if (m_havemdreapers)
reapmetadata(mdreapers, fn, doc);
}
// Set file name, mod time and url if not done by filter
if (doc.fmtime.empty())
@@ -708,7 +792,8 @@ FsIndexer::processonefile(RclConfig *config, TempDir& tmpdir,
fileDoc.url = cstr_fileu + fn;
if (m_havelocalfields)
setlocalfields(localfields, fileDoc);
if (m_havemdreapers)
reapmetadata(mdreapers, fn, fileDoc);
char cbuf[100];
sprintf(cbuf, OFFTPC, stp->st_size);
fileDoc.pcbytes = cbuf;
+19 -4
View File
@@ -88,13 +88,24 @@ class FsIndexer : public FsTreeWalkerCB {
std::vector<std::string> m_tdl;
FIMissingStore *m_missing;
// The configuration can set attribute fields to be inherited by
// all files in a file system area. Ie: set "rclaptg = thunderbird"
// inside ~/.thunderbird. The boolean is set at init to avoid
// further wasteful processing if no local fields are set.
bool m_havelocalfields;
map<string, string> m_localfields;
string m_slocalfields;
map<string, string> m_localfields;
// Same idea with the metadata-gathering external commands,
// (e.g. used to reap tagging info: "tmsu tags %f")
/* Hold the description for an external metadata-gathering command */
struct MDReaper {
string fieldname;
vector<string> cmdv;
};
bool m_havemdreapers;
string m_smdreapers;
vector<MDReaper> m_mdreapers;
#ifdef IDX_THREADS
friend void *FsIndexerDbUpdWorker(void*);
@@ -109,11 +120,15 @@ class FsIndexer : public FsTreeWalkerCB {
bool init();
void localfieldsfromconf();
void setlocalfields(const map<string, string> flds, Rcl::Doc& doc);
void mdreapersfromconf();
void setlocalfields(const map<string, string>& flds, Rcl::Doc& doc);
void reapmetadata(const vector<MDReaper>& reapers, const string &fn,
Rcl::Doc& doc);
string getDbDir() {return m_config->getDbDir();}
FsTreeWalker::Status
processonefile(RclConfig *config, TempDir& tmpdir, const string &fn,
const struct stat *, map<string,string> localfields);
const struct stat *, const map<string,string>& localfields,
const vector<MDReaper>& mdreapers);
};
#endif /* _fsindexer_h_included_ */
+3 -14
View File
@@ -23,10 +23,7 @@
#include <ctype.h>
#include <string>
#include <list>
#ifndef NO_NAMESPACES
using namespace std;
#endif /* NO_NAMESPACES */
#include "mimetype.h"
#include "debuglog.h"
@@ -53,20 +50,13 @@ static string mimetypefromdata(const string &fn, bool usfc)
// First try the internal identifying routine
string mime = idFile(fn.c_str());
// Then exec 'file -i'
#ifdef USE_SYSTEM_FILE_COMMAND
if (usfc && mime.empty()) {
// Last resort: use "file -i"
vector<string> args;
args.push_back("-i");
args.push_back(fn);
ExecCmd ex;
vector<string> cmd = create_vector<string>(FILE_PROG) ("-i") (fn);
string result;
string cmd = FILE_PROG;
int status = ex.doexec(cmd, args, 0, &result);
if (status) {
LOGERR(("mimetypefromdata: doexec: status 0x%x\n", status));
if (!ExecCmd::backtick(cmd, result)) {
LOGERR(("mimetypefromdata: exec %s failed\n", FILE_PROG));
return string();
}
LOGDEB2(("mimetype: [%s] \"file\" output [%s]\n",
@@ -160,7 +150,6 @@ string mimetype(const string &fn, const struct stat *stp,
}
#else // TEST->
#include <stdio.h>