X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FConverterCache.cpp;h=3842ebb419d23075580abf37e62d7b3a418571e7;hb=28be7d552f62cc02fa86d7f79201d089bfb2d7b5;hp=de97c1f27acd7071fe2a73df659fd5c3c9ba7d56;hpb=f630be890494c849981e4fb52ea4740506e92bed;p=lyx.git diff --git a/src/ConverterCache.cpp b/src/ConverterCache.cpp index de97c1f27a..3842ebb419 100644 --- a/src/ConverterCache.cpp +++ b/src/ConverterCache.cpp @@ -14,64 +14,63 @@ #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 -#include -#include +#include #include #include #include #include -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 FormatCacheType; -typedef std::map CacheType; +typedef map 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 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(lex.getString()); + if (!lex.next()) + break; + unsigned long const checksum = + convert(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