#ifndef lint static char rcsid[] = "@(#$Id: indexer.cpp,v 1.1 2005-01-31 14:31:09 dockes Exp $ (C) 2004 J.F.Dockes"; #endif #include #include #include #include #include #include "pathut.h" #include "conftree.h" #include "rclconfig.h" #include "fstreewalk.h" #include "mimetype.h" #include "rcldb.h" #include "readfile.h" #include "indexer.h" #include "csguess.h" #include "transcode.h" #include "mimehandler.h" #include "debuglog.h" using namespace std; #ifndef deleteZ #define deleteZ(X) {delete X;X = 0;} #endif /** * Bunch holder for data used while indexing a directory tree */ class DbIndexer { FsTreeWalker walker; RclConfig *config; string dbdir; list *topdirs; Rcl::Db db; public: DbIndexer(RclConfig *cnf, const string &dbd, list *top) : config(cnf), dbdir(dbd), topdirs(top) { } friend FsTreeWalker::Status indexfile(void *, const std::string &, const struct stat *, FsTreeWalker::CbFlag); bool index(); }; bool DbIndexer::index() { if (!db.open(dbdir, Rcl::Db::DbUpd)) { LOGERR(("DbIndexer::index: error opening database in %s\n", dbdir.c_str())); return false; } for (list::const_iterator it = topdirs->begin(); it != topdirs->end(); it++) { LOGDEB(("DbIndexer::index: Indexing %s into %s\n", it->c_str(), dbdir.c_str())); if (walker.walk(*it, indexfile, this) != FsTreeWalker::FtwOk) { LOGERR(("DbIndexer::index: error while indexing %s\n", it->c_str())); db.close(); return false; } } db.purge(); if (!db.close()) { LOGERR(("DbIndexer::index: error closing database in %s\n", dbdir.c_str())); return false; } return true; } /** * This function gets called for every file and directory found by the * tree walker. It checks with the db if the file has changed and needs to * be reindexed. If so, it calls an appropriate handler depending on the mime * type, which is responsible for populating an Rcl::Doc. * Accent and majuscule handling are performed by the db module when doing * the actual indexing work. */ FsTreeWalker::Status indexfile(void *cdata, const std::string &fn, const struct stat *stp, FsTreeWalker::CbFlag flg) { DbIndexer *me = (DbIndexer *)cdata; // If we're changing directories, possibly adjust parameters. if (flg == FsTreeWalker::FtwDirEnter || flg == FsTreeWalker::FtwDirReturn) { me->config->setKeyDir(fn); return FsTreeWalker::FtwOk; } string mime = mimetype(fn, me->config->getMimeMap()); if (mime.empty()) { // No mime type ?? pass on. LOGDEB(("indexfile: (no mime) [%s]\n", fn.c_str())); return FsTreeWalker::FtwOk; } // Look for appropriate handler MimeHandlerFunc fun = getMimeHandler(mime, me->config->getMimeConf()); if (!fun) { // No handler for this type, for now :( LOGDEB(("indexfile: %s : no handler\n", mime.c_str())); return FsTreeWalker::FtwOk; } LOGDEB(("indexfile: %s [%s]\n", mime.c_str(), fn.c_str())); // Check db up to date ? if (!me->db.needUpdate(fn, stp)) return FsTreeWalker::FtwOk; // Turn file into a document. The document has fields for title, body // etc., all text converted to utf8 Rcl::Doc doc; if (!fun(me->config, fn, mime, doc)) return FsTreeWalker::FtwOk; // Set up common fields: doc.mimetype = mime; char ascdate[20]; sprintf(ascdate, "%ld", long(stp->st_mtime)); doc.mtime = ascdate; // Do database-specific work to update document data if (!me->db.add(fn, doc)) return FsTreeWalker::FtwError; return FsTreeWalker::FtwOk; } ConfIndexer::~ConfIndexer() { deleteZ(indexer); } bool ConfIndexer::index() { ConfTree *conf = config->getConfig(); // Retrieve the list of directories to be indexed. string topdirs; if (conf->get("topdirs", topdirs, "") == 0) { LOGERR(("ConfIndexer::index: no top directories in configuration\n")); return false; } // Group the directories by database: it is important that all // directories for a database be indexed at once so that deleted // file cleanup works vector tdl; // List of directories to be indexed if (!ConfTree::stringToStrings(topdirs, tdl)) { LOGERR(("ConfIndexer::index: parse error for directory list\n")); return false; } vector::iterator dirit; map > dbmap; map >::iterator dbit; for (dirit = tdl.begin(); dirit != tdl.end(); dirit++) { string db; string dir = path_tildexpand(*dirit); if (conf->get("dbdir", db, dir) == 0) { LOGERR(("ConfIndexer::index: no database directory in " "configuration for %s\n", dir.c_str())); return false; } db = path_tildexpand(db); dbit = dbmap.find(db); if (dbit == dbmap.end()) { list l; l.push_back(dir); dbmap[db] = l; } else { dbit->second.push_back(dir); } } for (dbit = dbmap.begin(); dbit != dbmap.end(); dbit++) { //cout << dbit->first << " -> "; //list::const_iterator dit; //for (dit = dbit->second.begin(); dit != dbit->second.end(); dit++) { // cout << *dit << " "; //} //cout << endl; indexer = new DbIndexer(config, dbit->first, &dbit->second); if (!indexer->index()) { deleteZ(indexer); return false; } deleteZ(indexer); } return true; }