]> git.lyx.org Git - lyx.git/blobdiff - src/ConverterCache.cpp
Fix trailing whitespace in cpp files.
[lyx.git] / src / ConverterCache.cpp
index de97c1f27acd7071fe2a73df659fd5c3c9ba7d56..60ce245ab9671489e9aa42e35786520df9ad4370 100644 (file)
 
 #include "ConverterCache.h"
 
-#include "debug.h"
+#include "Format.h"
+#include "Lexer.h"
 #include "LyXRC.h"
 #include "Mover.h"
 
+#include "support/convert.h"
+#include "support/debug.h"
 #include "support/filetools.h"
-#include "support/lyxlib.h"
 #include "support/lyxtime.h"
-#include "support/package.h"
+#include "support/Package.h"
 
+#include "support/lassert.h"
 #include <boost/crc.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/current_function.hpp>
 
+#include <algorithm>
 #include <fstream>
 #include <iomanip>
 #include <map>
 #include <sstream>
 
-using lyx::support::addName;
-
-using std::string;
-
-namespace fs = boost::filesystem;
+using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
 
-using support::FileName;
-
 namespace {
 
 unsigned long do_crc(string const & s)
 {
        boost::crc_32_type crc;
-       crc = std::for_each(s.begin(), s.end(), crc);
+       crc = for_each(s.begin(), s.end(), crc);
        return crc.checksum();
 }
 
 
+// FIXME THREAD
+// This should be OK because it is only assigned during init()
 static FileName cache_dir;
 
 
 class CacheItem {
 public:
-       CacheItem() {}
+       CacheItem() : timestamp(0), checksum(0) {}
        CacheItem(FileName const & orig_from, string const & to_format,
-                 time_t t, unsigned long c)
+                 time_t t, unsigned long c)
                : timestamp(t), checksum(c)
        {
-               std::ostringstream os;
-               os << std::setw(10) << std::setfill('0') << do_crc(orig_from.absFilename())
+               ostringstream os;
+               os << setw(10) << setfill('0') << do_crc(orig_from.absFileName())
                   << '-' << to_format;
-               cache_name = FileName(addName(cache_dir.absFilename(), os.str()));
-               LYXERR(Debug::FILES) << "Add file cache item " << orig_from
-                                    << ' ' << to_format << ' ' << cache_name
-                                    << ' ' << timestamp << ' ' << checksum
-                                    << '.' << std::endl;
+               cache_name = FileName(addName(cache_dir.absFileName(), os.str()));
+               LYXERR(Debug::FILES, "Add file cache item " << orig_from
+                                    << ' ' << to_format << ' ' << cache_name
+                                    << ' ' << long(timestamp) << ' ' << checksum << '.');
        }
-       ~CacheItem() {}
+       ~CacheItem()
+       {}
        FileName cache_name;
        time_t timestamp;
        unsigned long checksum;
@@ -83,8 +82,15 @@ public:
 /** The cache contains one item per orig file and target format, so use a
  *  nested map to find the cache item quickly by filename and format.
  */
-typedef std::map<string, CacheItem> FormatCacheType;
-typedef std::map<FileName, FormatCacheType> CacheType;
+typedef map<string, CacheItem> FormatCacheType;
+class FormatCache {
+public:
+       /// Format of the source file
+       string from_format;
+       /// Cache target format -> item to quickly find the item by format
+       FormatCacheType cache;
+};
+typedef map<FileName, FormatCache> CacheType;
 
 
 class ConverterCache::Impl {
@@ -102,37 +108,63 @@ public:
 void ConverterCache::Impl::readIndex()
 {
        time_t const now = current_time();
-       FileName const index(addName(cache_dir.absFilename(), "index"));
-       std::ifstream is(index.toFilesystemEncoding().c_str());
-       while (is.good()) {
-               string orig_from;
-               string to_format;
-               time_t timestamp;
-               unsigned long checksum;
-               if (!(is >> orig_from >> to_format >> timestamp >> checksum))
-                       return;
+       FileName const index(addName(cache_dir.absFileName(), "index"));
+       ifstream is(index.toFilesystemEncoding().c_str());
+       Lexer lex;
+       lex.setStream(is);
+       while (lex.isOK()) {
+               if (!lex.next(true))
+                       break;
+               string const orig_from = lex.getString();
+               if (!lex.next())
+                       break;
+               string const to_format = lex.getString();
+               if (!lex.next())
+                       break;
+               time_t const timestamp =
+                       convert<unsigned long>(lex.getString());
+               if (!lex.next())
+                       break;
+               unsigned long const checksum =
+                       convert<unsigned long>(lex.getString());
                FileName const orig_from_name(orig_from);
                CacheItem item(orig_from_name, to_format, timestamp, checksum);
 
                // Don't cache files that do not exist anymore
-               if (!fs::exists(orig_from_name.toFilesystemEncoding())) {
-                       LYXERR(Debug::FILES) << "Not caching file `"
-                               << orig_from << "' (does not exist anymore)."
-                               << std::endl;
-                       support::unlink(item.cache_name);
+               if (!orig_from_name.exists()) {
+                       LYXERR(Debug::FILES, "Not caching file `"
+                               << orig_from << "' (does not exist anymore).");
+                       item.cache_name.removeFile();
+                       continue;
+               }
+
+               // Don't add items that are not in the cache anymore
+               // This can happen if two instances of LyX are running
+               // at the same time and update the index file independantly.
+               if (!item.cache_name.exists()) {
+                       LYXERR(Debug::FILES, "Not caching file `" << orig_from
+                               << "' (cached copy does not exist anymore).");
                        continue;
                }
 
                // Delete the cached file if it is too old
-               if (difftime(now, fs::last_write_time(item.cache_name.toFilesystemEncoding())) >
-                   lyxrc.converter_cache_maxage) {
-                       LYXERR(Debug::FILES) << "Not caching file `"
-                               << orig_from << "' (too old)." << std::endl;
-                       support::unlink(item.cache_name);
+               if (difftime(now, item.cache_name.lastModified())
+                               > lyxrc.converter_cache_maxage) {
+                       LYXERR(Debug::FILES, "Not caching file `"
+                               << orig_from << "' (too old).");
+                       item.cache_name.removeFile();
                        continue;
                }
 
-               cache[orig_from_name][to_format] = item;
+               FormatCache & format_cache = cache[orig_from_name];
+               if (format_cache.from_format.empty())
+                       format_cache.from_format =
+                               // FIXME perf: This very expensive function is called on all
+                               // cached files on opening. This slows LyX startup a lot. It
+                               // would be better if this information was retrieved in a
+                               // delayed fashion.
+                               theFormats().getFormatFromFile(orig_from_name);
+               format_cache.cache[to_format] = item;
        }
        is.close();
 }
@@ -140,19 +172,21 @@ void ConverterCache::Impl::readIndex()
 
 void ConverterCache::Impl::writeIndex()
 {
-       FileName const index(addName(cache_dir.absFilename(), "index"));
-       std::ofstream os(index.toFilesystemEncoding().c_str());
+       FileName const index(addName(cache_dir.absFileName(), "index"));
+       ofstream os(index.toFilesystemEncoding().c_str());
        os.close();
-       if (!lyx::support::chmod(index, 0600))
+       if (!index.changePermission(0600))
                return;
        os.open(index.toFilesystemEncoding().c_str());
        CacheType::iterator it1 = cache.begin();
        CacheType::iterator const end1 = cache.end();
        for (; it1 != end1; ++it1) {
-               FormatCacheType::iterator it2 = it1->second.begin();
-               FormatCacheType::iterator const end2 = it1->second.end();
+               FormatCacheType const & format_cache = it1->second.cache;
+               FormatCacheType::const_iterator it2 = format_cache.begin();
+               FormatCacheType::const_iterator const end2 = format_cache.end();
                for (; it2 != end2; ++it2)
-                       os << it1->first << ' ' << it2->first << ' '
+                       os << Lexer::quoteString(it1->first.absFileName())
+                          << ' ' << it2->first << ' '
                           << it2->second.timestamp << ' '
                           << it2->second.checksum << '\n';
        }
@@ -168,13 +202,31 @@ CacheItem * ConverterCache::Impl::find(FileName const & from,
        CacheType::iterator const it1 = cache.find(from);
        if (it1 == cache.end())
                return 0;
-       FormatCacheType::iterator const it2 = it1->second.find(format);
-       if (it2 == it1->second.end())
+       FormatCacheType & format_cache = it1->second.cache;
+       FormatCacheType::iterator const it2 = format_cache.find(format);
+       if (it2 == format_cache.end())
                return 0;
        return &(it2->second);
 }
 
 
+/////////////////////////////////////////////////////////////////////
+//
+// ConverterCache
+//
+/////////////////////////////////////////////////////////////////////
+
+ConverterCache::ConverterCache()
+       : pimpl_(new Impl)
+{}
+
+
+ConverterCache::~ConverterCache()
+{
+       delete pimpl_;
+}
+
+
 ConverterCache & ConverterCache::get()
 {
        // Now return the cache
@@ -189,25 +241,21 @@ void ConverterCache::init()
                return;
        // We do this here and not in the constructor because package() gets
        // initialized after all static variables.
-       cache_dir = FileName(addName(support::package().user_support().absFilename(), "cache"));
-       if (!fs::exists(cache_dir.toFilesystemEncoding()))
-               if (support::mkdir(cache_dir, 0700) != 0) {
+       cache_dir = FileName(addName(package().user_support().absFileName(), "cache"));
+       if (!cache_dir.exists())
+               if (!cache_dir.createDirectory(0700)) {
                        lyxerr << "Could not create cache directory `"
-                              << cache_dir << "'." << std::endl;
+                              << cache_dir << "'." << endl;
                        exit(EXIT_FAILURE);
                }
        get().pimpl_->readIndex();
 }
 
 
-ConverterCache::ConverterCache()
-       : pimpl_(new Impl)
-{}
-
-
-ConverterCache::~ConverterCache()
+void ConverterCache::writeIndex() const
 {
-       if (!lyxrc.use_converter_cache)
+       if (!lyxrc.use_converter_cache
+                 || cache_dir.empty())
                return;
        pimpl_->writeIndex();
 }
@@ -219,50 +267,64 @@ void ConverterCache::add(FileName const & orig_from, string const & to_format,
        if (!lyxrc.use_converter_cache || orig_from.empty() ||
            converted_file.empty())
                return;
-       LYXERR(Debug::FILES) << BOOST_CURRENT_FUNCTION << ' ' << orig_from
-                            << ' ' << to_format << ' ' << converted_file
-                            << std::endl;
+       LYXERR(Debug::FILES, ' ' << orig_from
+                            << ' ' << to_format << ' ' << converted_file);
+
+       // FIXME: Should not hardcode this (see bug 3819 for details)
+       if (to_format == "pstex") {
+               FileName const converted_eps(changeExtension(converted_file.absFileName(), "eps"));
+               add(orig_from, "eps", converted_eps);
+       } else if (to_format == "pdftex") {
+               FileName const converted_pdf(changeExtension(converted_file.absFileName(), "pdf"));
+               add(orig_from, "pdf6", converted_pdf);
+       }
 
        // Is the file in the cache already?
        CacheItem * item = pimpl_->find(orig_from, to_format);
 
-       time_t const timestamp = fs::last_write_time(orig_from.toFilesystemEncoding());
+       time_t const timestamp = orig_from.lastModified();
        Mover const & mover = getMover(to_format);
        if (item) {
-               LYXERR(Debug::FILES) << "ConverterCache::add(" << orig_from << "):\n"
-                                       "The file is already in the cache."
-                                    << std::endl;
+               LYXERR(Debug::FILES, "ConverterCache::add(" << orig_from << "):\n"
+                                       "The file is already in the cache.");
                // First test for timestamp
                if (timestamp == item->timestamp) {
-                       LYXERR(Debug::FILES) << "Same timestamp."
-                                            << std::endl;
+                       LYXERR(Debug::FILES, "Same timestamp.");
                        return;
-               } else {
-                       // Maybe the contents is still the same?
-                       item->timestamp = timestamp;
-                       unsigned long const checksum = support::sum(orig_from);
-                       if (checksum == item->checksum) {
-                               LYXERR(Debug::FILES) << "Same checksum."
-                                                    << std::endl;
-                               return;
-                       }
-                       item->checksum = checksum;
                }
-               if (!mover.copy(converted_file, item->cache_name, 0600))
-                       LYXERR(Debug::FILES) << "ConverterCache::add("
-                                            << orig_from << "):\n"
-                                               "Could not copy file."
-                                            << std::endl;
+               // Maybe the contents is still the same?
+               item->timestamp = timestamp;
+               unsigned long const checksum = orig_from.checksum();
+               if (checksum == item->checksum) {
+                       LYXERR(Debug::FILES, "Same checksum.");
+                       return;
+               }
+               item->checksum = checksum;
+               if (!mover.copy(converted_file, item->cache_name,
+                             onlyFileName(item->cache_name.absFileName()))) {
+                       LYXERR(Debug::FILES, "Could not copy file " << orig_from << " to "
+                               << item->cache_name);
+               } else if (!item->cache_name.changePermission(0600)) {
+                       LYXERR(Debug::FILES, "Could not change file mode"
+                               << item->cache_name);
+               }
        } else {
-               CacheItem new_item = CacheItem(orig_from, to_format, timestamp,
-                               support::sum(orig_from));
-               if (mover.copy(converted_file, new_item.cache_name, 0600))
-                       pimpl_->cache[orig_from][to_format] = new_item;
-               else
-                       LYXERR(Debug::FILES) << "ConverterCache::add("
-                                            << orig_from << "):\n"
-                                               "Could not copy file."
-                                            << std::endl;
+               CacheItem new_item(orig_from, to_format, timestamp,
+                               orig_from.checksum());
+               if (mover.copy(converted_file, new_item.cache_name,
+                             onlyFileName(new_item.cache_name.absFileName()))) {
+                       if (!new_item.cache_name.changePermission(0600)) {
+                               LYXERR(Debug::FILES, "Could not change file mode"
+                                       << new_item.cache_name);
+                       }
+                       FormatCache & format_cache = pimpl_->cache[orig_from];
+                       if (format_cache.from_format.empty())
+                               format_cache.from_format =
+                                       theFormats().getFormatFromFile(orig_from);
+                       format_cache.cache[to_format] = new_item;
+               } else
+                       LYXERR(Debug::FILES, "ConverterCache::add(" << orig_from << "):\n"
+                                               "Could not copy file.");
        }
 }
 
@@ -272,46 +334,98 @@ void ConverterCache::remove(FileName const & orig_from,
 {
        if (!lyxrc.use_converter_cache || orig_from.empty())
                return;
-       LYXERR(Debug::FILES) << BOOST_CURRENT_FUNCTION << ' ' << orig_from
-                            << ' ' << to_format << std::endl;
+       LYXERR(Debug::FILES, orig_from << ' ' << to_format);
 
        CacheType::iterator const it1 = pimpl_->cache.find(orig_from);
        if (it1 == pimpl_->cache.end())
                return;
-       FormatCacheType::iterator const it2 = it1->second.find(to_format);
-       if (it2 == it1->second.end())
+       FormatCacheType & format_cache = it1->second.cache;
+       FormatCacheType::iterator const it2 = format_cache.find(to_format);
+       if (it2 == format_cache.end())
                return;
 
-       it1->second.erase(it2);
-       if (it1->second.empty())
+       format_cache.erase(it2);
+       if (format_cache.empty())
                pimpl_->cache.erase(it1);
 }
 
 
+void ConverterCache::remove_all(string const & from_format,
+               string const & to_format) const
+{
+       if (!lyxrc.use_converter_cache)
+               return;
+       CacheType::iterator it1 = pimpl_->cache.begin();
+       while (it1 != pimpl_->cache.end()) {
+               if (it1->second.from_format != from_format) {
+                       ++it1;
+                       continue;
+               }
+               FormatCacheType & format_cache = it1->second.cache;
+               FormatCacheType::iterator it2 = format_cache.begin();
+               while (it2 != format_cache.end()) {
+                       if (it2->first == to_format) {
+                               LYXERR(Debug::FILES, "Removing file cache item "
+                                       << it1->first << ' ' << to_format);
+                               it2->second.cache_name.removeFile();
+                               format_cache.erase(it2);
+                               // Have to start over again since items in a
+                               // map are not ordered
+                               it2 = format_cache.begin();
+                       } else {
+                               ++it2;
+                       }
+               }
+               if (format_cache.empty()) {
+                       pimpl_->cache.erase(it1);
+                       // Have to start over again since items in a map are
+                       // not ordered
+                       it1 = pimpl_->cache.begin();
+               } else {
+                       ++it1;
+               }
+       }
+       pimpl_->writeIndex();
+}
+
+
 bool ConverterCache::inCache(FileName const & orig_from,
                string const & to_format) const
 {
        if (!lyxrc.use_converter_cache || orig_from.empty())
                return false;
-       LYXERR(Debug::FILES) << BOOST_CURRENT_FUNCTION << ' ' << orig_from
-                            << ' ' << to_format << std::endl;
+       LYXERR(Debug::FILES, orig_from << ' ' << to_format);
 
        CacheItem * const item = pimpl_->find(orig_from, to_format);
        if (!item) {
-               LYXERR(Debug::FILES) << "not in cache." << std::endl;
+               LYXERR(Debug::FILES, "not in cache.");
                return false;
        }
-       time_t const timestamp = fs::last_write_time(orig_from.toFilesystemEncoding());
+
+       // Special handling of pstex and pdftex formats: These are only
+       // considered to be in the cache if the corresponding graphics
+       // fiels are there as well. Otherwise copy() of the graphics below
+       // would fail.
+       // FIXME: Should not hardcode this (see bug 3819 for details)
+       if (to_format == "pstex") {
+               if (!inCache(orig_from, "eps"))
+                       return false;
+       } else if (to_format == "pdftex") {
+               if (!inCache(orig_from, "pdf6"))
+                       return false;
+       }
+
+       time_t const timestamp = orig_from.lastModified();
        if (item->timestamp == timestamp) {
-               LYXERR(Debug::FILES) << "identical timestamp." << std::endl;
+               LYXERR(Debug::FILES, "identical timestamp.");
                return true;
        }
-       if (item->checksum == support::sum(orig_from)) {
+       if (item->checksum == orig_from.checksum()) {
                item->timestamp = timestamp;
-               LYXERR(Debug::FILES) << "identical checksum." << std::endl;
+               LYXERR(Debug::FILES, "identical checksum.");
                return true;
        }
-       LYXERR(Debug::FILES) << "in cache, but too old." << std::endl;
+       LYXERR(Debug::FILES, "in cache, but too old.");
        return false;
 }
 
@@ -319,11 +433,10 @@ bool ConverterCache::inCache(FileName const & orig_from,
 FileName const & ConverterCache::cacheName(FileName const & orig_from,
                string const & to_format) const
 {
-       LYXERR(Debug::FILES) << BOOST_CURRENT_FUNCTION << ' ' << orig_from
-                            << ' ' << to_format << std::endl;
+       LYXERR(Debug::FILES, orig_from << ' ' << to_format);
 
        CacheItem * const item = pimpl_->find(orig_from, to_format);
-       BOOST_ASSERT(item);
+       LASSERT(item, { static const FileName fn; return fn; });
        return item->cache_name;
 }
 
@@ -333,13 +446,24 @@ bool ConverterCache::copy(FileName const & orig_from, string const & to_format,
 {
        if (!lyxrc.use_converter_cache || orig_from.empty() || dest.empty())
                return false;
-       LYXERR(Debug::FILES) << BOOST_CURRENT_FUNCTION << ' ' << orig_from
-                            << ' ' << to_format << ' ' << dest << std::endl;
+       LYXERR(Debug::FILES, orig_from << ' ' << to_format << ' ' << dest);
+
+       // FIXME: Should not hardcode this (see bug 3819 for details)
+       if (to_format == "pstex") {
+               FileName const dest_eps(changeExtension(dest.absFileName(), "eps"));
+               if (!copy(orig_from, "eps", dest_eps))
+                       return false;
+       } else if (to_format == "pdftex") {
+               FileName const dest_pdf(changeExtension(dest.absFileName(), "pdf"));
+               if (!copy(orig_from, "pdf6", dest_pdf))
+                       return false;
+       }
 
        CacheItem * const item = pimpl_->find(orig_from, to_format);
-       BOOST_ASSERT(item);
+       LASSERT(item, return false);
        Mover const & mover = getMover(to_format);
-       return mover.copy(item->cache_name, dest);
+       return mover.copy(item->cache_name, dest,
+                         onlyFileName(dest.absFileName()));
 }
 
 } // namespace lyx