From ad4f24923fb74720ac74f663592517c0d9430f63 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Mon, 20 Sep 2010 10:35:26 +0200 Subject: [PATCH] uncompress file before starting external viewer except if in the nouncompforviewmts list --- src/common/rclconfig.cpp | 15 +++++ src/common/rclconfig.h | 4 ++ src/doc/user/bldloop | 2 +- src/doc/user/usermanual.sgml | 10 +++- src/internfile/internfile.cpp | 105 ++++++++++++++++++++++++++++++++++ src/internfile/internfile.h | 14 +++++ src/qtgui/rclmain_w.cpp | 27 +++++++++ src/sampleconf/mimeview | 5 ++ 8 files changed, 180 insertions(+), 2 deletions(-) diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index 5cb3c752..66f1d24d 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -721,6 +721,8 @@ string RclConfig::getMimeViewerDef(const string &mtype, const string& apptag) LOGDEB(("RclConfig::getMimeViewerDef: mtype %s apptag %s\n", mtype.c_str(), apptag.c_str())); string hs; + if (mimeview == 0) + return hs; if (apptag.empty() || !mimeview->get(mtype + string("|") + apptag, hs, "view")) mimeview->get(mtype, hs, "view"); @@ -740,6 +742,8 @@ bool RclConfig::getMimeViewerDefs(vector >& defs) bool RclConfig::setMimeViewerDef(const string& mt, const string& def) { + if (mimeview == 0) + return false; string pconfname = path_cat(m_confdir, "mimeview"); // Make sure this exists close(open(pconfname.c_str(), O_CREAT|O_WRONLY, 0600)); @@ -763,6 +767,17 @@ bool RclConfig::setMimeViewerDef(const string& mt, const string& def) return true; } +bool RclConfig::mimeViewerNeedsUncomp(const string &mimetype) +{ + string s; + vector v; + if (mimeview != 0 && mimeview->get("nouncompforviewmts", s, "") && + stringToStrings(s, v) && + find_if(v.begin(), v.end(), StringIcmpPred(mimetype)) != v.end()) + return false; + return true; +} + /** * Return icon name and path */ diff --git a/src/common/rclconfig.h b/src/common/rclconfig.h index dd925bea..4f81e794 100644 --- a/src/common/rclconfig.h +++ b/src/common/rclconfig.h @@ -209,6 +209,10 @@ class RclConfig { string getMimeViewerDef(const string &mimetype, const string& apptag); bool getMimeViewerDefs(vector >&); bool setMimeViewerDef(const string& mimetype, const string& cmd); + /** Check if mime type is designated as needing no uncompress before view + * (if a file of this type is found compressed). Default is true, + * exceptions are found in the nouncompforviewmts mimeview list */ + bool mimeViewerNeedsUncomp(const string &mimetype); /** Store/retrieve missing helpers description string */ string getMissingHelperDesc(); diff --git a/src/doc/user/bldloop b/src/doc/user/bldloop index f65ce21c..3b005359 100644 --- a/src/doc/user/bldloop +++ b/src/doc/user/bldloop @@ -1,7 +1,7 @@ #!/bin/sh while true;do - make FORMATS=html + make FORMATS="html html-split" cp *.html /usr/local/www/lesbonscomptes/recoll/usermanual/ sleep 1 done diff --git a/src/doc/user/usermanual.sgml b/src/doc/user/usermanual.sgml index 87d42b51..eff83625 100644 --- a/src/doc/user/usermanual.sgml +++ b/src/doc/user/usermanual.sgml @@ -3605,7 +3605,7 @@ x-my-tag = mailmytag which will override those from the central configuration file. Please note that these entries must be placed under a - [view] section. + [view] section. The keys in the file are normally mime types. You can add an application tag to specialize the choice for an area of the @@ -3618,6 +3618,14 @@ x-my-tag = mailmytag all mimeview entries will be ignored except the one labelled application/x-all (which is set to use xdg-open by default). + + + The nouncompforviewmts entry, (placed at + the top level, outside of the [view] section), + holds a list of mime types that should not be uncompressed before + starting the viewer (if they are found compressed, ie: + mydoc.doc.gz). + diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index 9ef4059e..1153a595 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -46,6 +46,7 @@ using namespace std; #include "fileudi.h" #include "beaglequeuecache.h" #include "cancelcheck.h" +#include "copyfile.h" #ifdef RCL_USE_XATTR #include "pxattr.h" @@ -946,6 +947,110 @@ bool FileInterner::idocToFile(TempFile& otemp, const string& tofile, return true; } +bool FileInterner::isCompressed(const string& fn, RclConfig *cnf) +{ + LOGDEB(("FileInterner::isCompressed: [%s]\n", fn.c_str())); + struct stat st; + if (stat(fn.c_str(), &st) < 0) { + LOGERR(("FileInterner::isCompressed: can't stat [%s]\n", fn.c_str())); + return false; + } + string l_mime = mimetype(fn, &st, cnf, true); + if (l_mime.empty()) { + LOGERR(("FileInterner::isUncompressed: can't get mime for [%s]\n", + fn.c_str())); + return false; + } + + listucmd; + if (cnf->getUncompressor(l_mime, ucmd)) { + return true; + } + return false; +} + +bool FileInterner::maybeUncompressToTemp(TempFile& temp, const string& fn, + RclConfig *cnf, const Rcl::Doc& doc) +{ + LOGDEB(("FileInterner::maybeUncompressToTemp: [%s]\n", fn.c_str())); + struct stat st; + if (stat(fn.c_str(), &st) < 0) { + LOGERR(("FileInterner::maybeUncompressToTemp: can't stat [%s]\n", + fn.c_str())); + return false; + } + string l_mime = mimetype(fn, &st, cnf, true); + if (l_mime.empty()) { + LOGERR(("FileInterner::maybeUncompress.: can't id. mime for [%s]\n", + fn.c_str())); + return false; + } + + listucmd; + if (!cnf->getUncompressor(l_mime, ucmd)) { + return true; + } + // Check for compressed size limit + int maxkbs = -1; + if (cnf->getConfParam("compressedfilemaxkbs", &maxkbs) && + maxkbs >= 0 && int(st.st_size / 1024) > maxkbs) { + LOGINFO(("FileInterner:: %s over size limit %d kbs\n", + fn.c_str(), maxkbs)); + return false; + } + TempDir tmpdir; + temp = + TempFile(new TempFileInternal(cnf->getSuffixFromMimeType(doc.mimetype))); + if (!tmpdir.ok() || !temp->ok()) { + LOGERR(("FileInterner: cant create temporary file/dir")); + return false; + } + + // uncompressfile choses the output file name, there is good + // reason for this, but it's not nice here. Have to copy or rename + // the uncompressed file + string uncomped; + if (!uncompressfile(cnf, fn, ucmd, tmpdir, uncomped)) { + return false; + } + + // uncompressfile choses the output file name, there is good + // reason for this, but it's not nice here. Have to copy or rename + // the uncompressed file. + // Hopefully the cross-dev case won't happen as we're + // probably choosing the temp names in the same dir. However... + // Unix really should have a rename-else-copy call... + if (stat(temp->filename(), &st) < 0) { + LOGERR(("FileInterner::maybeUncompressToTemp: can't stat [%s]\n", + temp->filename())); + return false; + } + struct stat st1; + if (stat(uncomped.c_str(), &st1) < 0) { + LOGERR(("FileInterner::maybeUncompressToTemp: can't stat [%s]\n", + uncomped.c_str())); + return false; + } + if (st.st_dev == st1.st_dev) { + if (rename(uncomped.c_str(), temp->filename()) < 0) { + LOGERR(("FileInterner::maybeUncompress: rename [%s] -> [%s]" + "failed, errno %d\n", + uncomped.c_str(), temp->filename(), errno)); + return false; + } + } else { + string reason; + bool ret = copyfile(uncomped.c_str(), temp->filename(), reason); + if (ret == false) { + LOGERR(("FileInterner::maybeUncompress: copy [%s] -> [%s]" + "failed: %s\n", + uncomped.c_str(), temp->filename(), reason.c_str())); + return false; + } + // We let the tempdir cleanup get rid of uncomped + } + return true; +} #else diff --git a/src/internfile/internfile.h b/src/internfile/internfile.h index 72057703..bdb0e73e 100644 --- a/src/internfile/internfile.h +++ b/src/internfile/internfile.h @@ -156,11 +156,25 @@ class FileInterner { static bool idocToFile(TempFile& temp, const string& tofile, RclConfig *cnf, const Rcl::Doc& doc); + /** + * Does file appear to be the compressed version of a document? + */ + static bool isCompressed(const string& fn, RclConfig *cnf); + + /** + * Check input compressed, allocate temp file and uncompress if it is. + * @return true if ok, false for error. Actual decompression is indicated + * by the TempFile status (!isNull()) + */ + static bool maybeUncompressToTemp(TempFile& temp, const string& fn, + RclConfig *cnf, const Rcl::Doc& doc); + const string& getReason() const {return m_reason;} static void getMissingExternal(string& missing); static void getMissingDescription(string& desc); bool ok() {return m_ok;} + private: static const unsigned int MAXHANDLERS = 20; RclConfig *m_cfg; diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index ad2a2b99..f44aab0a 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -1096,6 +1096,32 @@ void RclMain::startNativeViewer(Rcl::Doc doc) url = string("file://") + fn; } + // If using an actual file, check that it exists, and if it is + // compressed, we may need an uncompressed version + if (!fn.empty() && rclconfig->mimeViewerNeedsUncomp(doc.mimetype)) { + if (access(fn.c_str(), R_OK) != 0) { + QMessageBox::warning(0, "Recoll", + tr("Can't access file: ") + + QString::fromLocal8Bit(fn.c_str())); + return; + } + TempFile temp; + if (FileInterner::isCompressed(fn, rclconfig)) { + if (!FileInterner::maybeUncompressToTemp(temp, fn, rclconfig, + doc)) { + QMessageBox::warning(0, "Recoll", + tr("Can't uncompress file: ") + + QString::fromLocal8Bit(fn.c_str())); + return; + } + } + if (!temp.isNull()) { + m_tempfiles.push_back(temp); + fn = temp->filename(); + url = string("file://") + fn; + } + } + // Substitute %xx inside prototype command string efftime; if (!doc.dmtime.empty() || !doc.fmtime.empty()) { @@ -1134,6 +1160,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) if (!istempfile) historyEnterDoc(g_dynconf, doc.meta[Rcl::Doc::keyudi]); + // We should actually monitor these processes so that we can // delete the temp files when they exit LOGDEB(("Executing: [%s]\n", ncmd.c_str())); diff --git a/src/sampleconf/mimeview b/src/sampleconf/mimeview index 506d1dcf..873ce204 100644 --- a/src/sampleconf/mimeview +++ b/src/sampleconf/mimeview @@ -4,6 +4,11 @@ # External viewers, launched by the recoll GUI when you click on a result # 'edit' link +# Mime types which we should not uncompress if they are found gzipped or +# bzipped because the native viewer knows how to handle. These would be +# exceptions and the list is normally empty +#nouncompforviewmts = + [view] # Pseudo entry used if the 'use desktop' preference is set in the GUI application/x-all = xdg-open %f