From 5ed606f9c5ae2ee31cd665e7d4408f858b6ef9f2 Mon Sep 17 00:00:00 2001 From: Georg Baum Date: Mon, 13 Nov 2006 10:27:57 +0000 Subject: [PATCH] Add a cache for converted image files. This needs to be enabled in the preferences file with \use_converter_cache true. It is disabled by default, and no GUI support for changing the preferences is yet implemented. * src/insets/insetgraphics.C (InsetGraphics::prepareFile): Use image file cache * src/insets/ExternalSupport.C (updateExternal): Use image file cache * src/exporter.C (Exporter::Export): Do not use image file cache * src/graphics/GraphicsCacheItem.C (CacheItem::Impl::imageConverted): Add the converted file to the image file cache (CacheItem::Impl::convertToDisplayFo): Use image file cache * src/converter.C (Converters::convert): Use image file cache if the caller allowed that * src/converter.h (Converters::convert): Adjust arguments * src/Makefile.am: Add new files * src/support/lyxlib.h (chmod): new function (copy): add mode argument * src/support/copy.C (chmod): new function (copy): implement mode argument * src/support/mkdir.C (lyx::support::mkdir): Add warning if permissions are ignored * src/lyxrc.[Ch]: Add new settings \converter_cache_maxage and \use_converter_cache * src/ConverterCache.[Ch]: New image file cache * src/importer.C (Importer::Import): Do nut use the image file cache * src/lyx_main.C (LyX::init): Initialize the image file cache * src/mover.[Ch] (Mover::do_copy): Add mode argument (SpecialisedMover::do_copy): ditto * configure.ac: Check for chmod * development/cmake/ConfigureChecks.cmake: ditto * development/cmake/config.h.cmake: ditto * development/scons/SConstruct: ditto * development/scons/scons_manifest.py: Add new files git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@15897 a592a061-630c-0410-9148-cb99ea01b6c8 --- configure.ac | 2 +- development/cmake/ConfigureChecks.cmake | 1 + development/cmake/config.h.cmake | 1 + development/scons/SConstruct | 1 + development/scons/scons_manifest.py | 2 + src/ConverterCache.C | 349 ++++++++++++++++++++++++ src/ConverterCache.h | 101 +++++++ src/Makefile.am | 2 + src/converter.C | 47 ++-- src/converter.h | 22 +- src/exporter.C | 14 +- src/graphics/GraphicsCacheItem.C | 24 +- src/importer.C | 7 +- src/insets/ExternalSupport.C | 7 +- src/insets/insetgraphics.C | 4 +- src/lyx_main.C | 6 + src/lyxrc.C | 29 ++ src/lyxrc.h | 6 + src/mover.C | 21 +- src/mover.h | 14 +- src/support/copy.C | 32 ++- src/support/lyxlib.h | 5 +- src/support/mkdir.C | 9 + 23 files changed, 643 insertions(+), 63 deletions(-) create mode 100644 src/ConverterCache.C create mode 100644 src/ConverterCache.h diff --git a/configure.ac b/configure.ac index a8679d4ff5..2e4c60f489 100644 --- a/configure.ac +++ b/configure.ac @@ -260,7 +260,7 @@ dnl work correctly because of some conflict with stdlib.h with g++ 2.96 dnl We aim to remove this eventually, since we should test as much as dnl possible with the compiler which will use the functions (JMarc) AC_LANG_PUSH(C) -AC_CHECK_FUNCS(close _close getpid _getpid lstat mkfifo mkstemp mktemp open _open pclose _pclose popen _popen readlink) +AC_CHECK_FUNCS(chmod close _close getpid _getpid lstat mkfifo mkstemp mktemp open _open pclose _pclose popen _popen readlink) AC_LANG_POP(C) LYX_CHECK_SPELL_ENGINES diff --git a/development/cmake/ConfigureChecks.cmake b/development/cmake/ConfigureChecks.cmake index c7158edf39..144ddab075 100644 --- a/development/cmake/ConfigureChecks.cmake +++ b/development/cmake/ConfigureChecks.cmake @@ -41,6 +41,7 @@ check_include_files(argz.h HAVE_ARGZ_H) check_function_exists(open HAVE_OPEN) +check_function_exists(chmod HAVE_CHMOD) check_function_exists(close HAVE_CLOSE) check_function_exists(popen HAVE_POPEN) check_function_exists(pclose HAVE_PCLOSE) diff --git a/development/cmake/config.h.cmake b/development/cmake/config.h.cmake index d7f3886f12..c82092c9a4 100644 --- a/development/cmake/config.h.cmake +++ b/development/cmake/config.h.cmake @@ -33,6 +33,7 @@ #cmakedefine HAVE_IOS 1 #cmakedefine HAVE_LOCALE 1 #cmakedefine HAVE_OPEN 1 +#cmakedefine HAVE_CHMOD 1 #cmakedefine HAVE_CLOSE 1 #cmakedefine HAVE_POPEN 1 #cmakedefine HAVE_PCLOSE 1 diff --git a/development/scons/SConstruct b/development/scons/SConstruct index 8e23e71b86..63229e6bea 100644 --- a/development/scons/SConstruct +++ b/development/scons/SConstruct @@ -986,6 +986,7 @@ result = utils.createConfigFile(conf, ], functions = [ ('open', 'HAVE_OPEN', None), + ('chmod', 'HAVE_CHMOD', None), ('close', 'HAVE_CLOSE', None), ('popen', 'HAVE_POPEN', None), ('pclose', 'HAVE_PCLOSE', None), diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index 4b8cbbe778..9c789ade40 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -1034,6 +1034,7 @@ src_header_files = Split(''' Bullet.h Chktex.h Color.h + ConverterCache.h CutAndPaste.h DepTable.h FloatList.h @@ -1155,6 +1156,7 @@ src_pre_files = Split(''' Bullet.C Chktex.C Color.C + ConverterCache.C CutAndPaste.C DepTable.C FloatList.C diff --git a/src/ConverterCache.C b/src/ConverterCache.C new file mode 100644 index 0000000000..fee76c0afb --- /dev/null +++ b/src/ConverterCache.C @@ -0,0 +1,349 @@ +/** + * \file ConverterCache.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Baruch Even + * \author Angus Leeming + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "ConverterCache.h" + +#include "debug.h" +#include "lyxrc.h" +#include "mover.h" + +#include "support/filetools.h" +#include "support/lyxlib.h" +#include "support/lyxtime.h" +#include "support/package.h" + +#include +#include + +#include +#include +#include +#include + +using lyx::support::absolutePath; +using lyx::support::addName; + +using std::string; + +namespace fs = boost::filesystem; + +namespace lyx { + +namespace { + +unsigned long do_crc(string const & s) +{ + boost::crc_32_type crc; + crc = std::for_each(s.begin(), s.end(), crc); + return crc.checksum(); +} + + +static string cache_dir; + + +class CacheItem { +public: + CacheItem() {} + CacheItem(string const & orig_from, string const & to_format, + time_t t, unsigned long c) + : timestamp(t), checksum(c) + { + BOOST_ASSERT(absolutePath(orig_from)); + std::ostringstream os; + os << std::setw(10) << std::setfill('0') << do_crc(orig_from) + << '-' << to_format; + cache_name = addName(cache_dir, os.str()); + lyxerr[Debug::FILES] << "Add file cache item " << orig_from + << ' ' << to_format << ' ' << cache_name + << ' ' << timestamp << ' ' << checksum + << '.' << std::endl; + } + ~CacheItem() {} + string cache_name; + time_t timestamp; + unsigned long checksum; +}; + +} + + +/** 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; + + +class ConverterCache::Impl { +public: + /// + void readIndex(); + /// + void writeIndex(); + /// + CacheItem * find(string const & from, string const & format); + CacheType cache; +}; + + +void ConverterCache::Impl::readIndex() +{ + time_t const now = current_time(); + string const index = addName(cache_dir, "index"); + std::ifstream is(index.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; + CacheItem item(orig_from, to_format, timestamp, checksum); + + // Don't cache files that do not exist anymore + if (!fs::exists(orig_from)) { + lyxerr[Debug::FILES] << "Not caching file `" + << orig_from << "' (does not exist anymore)." + << std::endl; + support::unlink(item.cache_name); + continue; + } + + // Delete the cached file if it is too old + if (difftime(now, fs::last_write_time(item.cache_name)) > + lyxrc.converter_cache_maxage) { + lyxerr[Debug::FILES] << "Not caching file `" + << orig_from << "' (too old)." << std::endl; + support::unlink(item.cache_name); + continue; + } + + cache[orig_from][to_format] = item; + } + is.close(); +} + + +void ConverterCache::Impl::writeIndex() +{ + string const index = addName(cache_dir, "index"); + std::ofstream os(index.c_str()); + os.close(); + if (!lyx::support::chmod(index.c_str(), 0600)) + return; + os.open(index.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(); + for (; it2 != end2; ++it2) + os << it1->first << ' ' << it2->first << ' ' + << it2->second.timestamp << ' ' + << it2->second.checksum << '\n'; + } + os.close(); +} + + +CacheItem * ConverterCache::Impl::find(string const & from, + string const & format) +{ + if (!lyxrc.use_converter_cache) + return 0; + 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()) + return 0; + return &(it2->second); +} + + +ConverterCache & ConverterCache::get() +{ + // Now return the cache + static ConverterCache singleton; + return singleton; +} + + +void ConverterCache::init() +{ + if (!lyxrc.use_converter_cache) + return; + // We do this here and not in the constructor because package() gets + // initialized after all static variables. + cache_dir = addName(support::package().user_support(), "cache"); + if (!fs::exists(cache_dir)) + if (support::mkdir(cache_dir, 0700) != 0) { + lyxerr << "Could not create cache directory `" + << cache_dir << "'." << std::endl; + exit(EXIT_FAILURE); + } + get().pimpl_->readIndex(); +} + + +ConverterCache::ConverterCache() + : pimpl_(new Impl) +{} + + +ConverterCache::~ConverterCache() +{ + if (!lyxrc.use_converter_cache) + return; + pimpl_->writeIndex(); +} + + +void ConverterCache::add(string const & orig_from, string const & to_format, + string const & converted_file) const +{ + if (!lyxrc.use_converter_cache) + return; + lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from + << ' ' << to_format << ' ' << converted_file + << std::endl; + BOOST_ASSERT(absolutePath(orig_from)); + BOOST_ASSERT(absolutePath(converted_file)); + + // 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); + Mover const & mover = movers(to_format); + if (item) { + lyxerr[Debug::FILES] << "ConverterCache::add(" << orig_from << "):\n" + "The file is already in the cache." + << std::endl; + // First test for timestamp + if (timestamp == item->timestamp) { + lyxerr[Debug::FILES] << "Same timestamp." + << std::endl; + 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; + } 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; + } +} + + +void ConverterCache::remove(string const & orig_from, + string const & to_format) const +{ + if (!lyxrc.use_converter_cache) + return; + lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from + << ' ' << to_format << std::endl; + BOOST_ASSERT(absolutePath(orig_from)); + + 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()) + return; + + it1->second.erase(it2); + if (it1->second.empty()) + pimpl_->cache.erase(it1); +} + + +bool ConverterCache::inCache(string const & orig_from, + string const & to_format) const +{ + if (!lyxrc.use_converter_cache) + return false; + lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from + << ' ' << to_format << std::endl; + BOOST_ASSERT(absolutePath(orig_from)); + + CacheItem * const item = pimpl_->find(orig_from, to_format); + if (!item) { + lyxerr[Debug::FILES] << "not in cache." << std::endl; + return false; + } + time_t const timestamp = fs::last_write_time(orig_from); + if (item->timestamp == timestamp) { + lyxerr[Debug::FILES] << "identical timestamp." << std::endl; + return true; + } + if (item->checksum == support::sum(orig_from)) { + item->timestamp = timestamp; + lyxerr[Debug::FILES] << "identical checksum." << std::endl; + return true; + } + lyxerr[Debug::FILES] << "in cache, but too old." << std::endl; + return false; +} + + +string const ConverterCache::cacheName(string const & orig_from, + string const & to_format) const +{ + lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from + << ' ' << to_format << std::endl; + BOOST_ASSERT(absolutePath(orig_from)); + + CacheItem * const item = pimpl_->find(orig_from, to_format); + BOOST_ASSERT(item); + return item->cache_name; +} + + +bool ConverterCache::copy(string const & orig_from, string const & to_format, + string const & dest) const +{ + if (!lyxrc.use_converter_cache) + return false; + lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from + << ' ' << to_format << ' ' << dest << std::endl; + BOOST_ASSERT(absolutePath(orig_from)); + BOOST_ASSERT(absolutePath(dest)); + + CacheItem * const item = pimpl_->find(orig_from, to_format); + BOOST_ASSERT(item); + Mover const & mover = movers(to_format); + return mover.copy(item->cache_name, dest); +} + +} // namespace lyx diff --git a/src/ConverterCache.h b/src/ConverterCache.h new file mode 100644 index 0000000000..162f0f3be5 --- /dev/null +++ b/src/ConverterCache.h @@ -0,0 +1,101 @@ +// -*- C++ -*- +/** + * \file ConverterCache.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Baruch Even + * \author Angus Leeming + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + * + * lyx::ConverterCache is the manager of the file cache. + * It is responsible for creating the lyx::ConverterCacheItem's + * and maintaining them. + * + * lyx::ConverterCache is a singleton class. It is possible to have + * only one instance of it at any moment. + */ + +#ifndef CONVERTERCACHE_H +#define CONVERTERCACHE_H + +#include +#include + +#include + + +namespace lyx { + +/** + * Cache for converted files. The cache works as follows: + * + * The key for a cache item consists of the absolute name of the original + * file and the format name of the target format. The original file in the + * user directory is named \c orig_from in the code, the format name is named + * \c to_format. Example: + * \c orig_from = "/home/me/myfigure.fig" + * \c to_format = "eps" + * A cache item is considered up to date (inCache() returns \c true) if + * - The cache contains an item with key (\c orig_to, \c to_format) + * - The stored timestamp of the item is identical with the actual timestamp + * of \c orig_from, or, if that is not the case, the stored checksum is + * identical with the actual checksum of \c orig_from. + * Otherwise the item is not considered up to date, and add() will refresh it. + * + * There is no cache maintenance yet (max size, max age etc.) + */ +class ConverterCache : boost::noncopyable { +public: + + /// This is a singleton class. Get the instance. + static ConverterCache & get(); + + /// Init the cache. This must be done after package initialization. + static void init(); + + /** + * Add \c converted_file (\c orig_from converted to \c to_format) to + * the cache if it is not already in or not up to date. + */ + void add(std::string const & orig_from, std::string const & to_format, + std::string const & converted_file) const; + + /// Remove a file from the cache. + void remove(std::string const & orig_from, + std::string const & to_format) const; + + /** + * Returns \c true if \c orig_from converted to \c to_format is in + * the cache and up to date. + */ + bool inCache(std::string const & orig_from, + std::string const & to_format) const; + + /// Get the name of the cached file + std::string const cacheName(std::string const & orig_from, + std::string const & to_format) const; + + /// Copy the file from the cache to \p dest + bool copy(std::string const & orig_from, std::string const & to_format, + std::string const & dest) const; + +private: + /** Make the c-tor, d-tor private so we can control how many objects + * are instantiated. + */ + ConverterCache(); + /// + ~ConverterCache(); + + /// Use the Pimpl idiom to hide the internals. + class Impl; + /// The pointer never changes although *pimpl_'s contents may. + boost::scoped_ptr const pimpl_; +}; + +} // namespace lyx + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index f7be2c0266..dbe1fcee82 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,8 @@ lyx_SOURCES = \ Chktex.h \ Color.C \ Color.h \ + ConverterCache.C \ + ConverterCache.h \ CutAndPaste.C \ CutAndPaste.h \ DepTable.C \ diff --git a/src/converter.C b/src/converter.C index 8d2390c1fe..0ffdc49179 100644 --- a/src/converter.C +++ b/src/converter.C @@ -12,6 +12,7 @@ #include "converter.h" +#include "ConverterCache.h" #include "buffer.h" #include "buffer_funcs.h" #include "bufferparams.h" @@ -33,6 +34,7 @@ namespace lyx { +using support::absolutePath; using support::addName; using support::bformat; using support::changeExtension; @@ -285,24 +287,31 @@ OutputParams::FLAVOR Converters::getFlavor(Graph::EdgePath const & path) bool Converters::convert(Buffer const * buffer, - string const & from_file, string const & to_file_base, - string const & from_format, string const & to_format, - string & to_file, ErrorList & errorList, bool try_default) + string const & from_file, string const & to_file, + string const & orig_from, + string const & from_format, string const & to_format, + ErrorList & errorList, int conversionflags) { - string const to_ext = formats.extension(to_format); - to_file = changeExtension(to_file_base, to_ext); + BOOST_ASSERT(absolutePath(from_file)); + BOOST_ASSERT(absolutePath(to_file)); + BOOST_ASSERT(absolutePath(orig_from)); if (from_format == to_format) return move(from_format, from_file, to_file, false); + if ((conversionflags & try_cache) && + ConverterCache::get().inCache(orig_from, to_format)) + return ConverterCache::get().copy(orig_from, to_format, to_file); + Graph::EdgePath edgepath = getPath(from_format, to_format); if (edgepath.empty()) { - if (try_default) { + if (conversionflags & try_default) { // if no special converter defined, then we take the // default one from ImageMagic. string const from_ext = from_format.empty() ? getExtension(from_file) : formats.extension(from_format); + string const to_ext = formats.extension(to_format); string const command = support::os::python() + ' ' + quoteName(libFileSearch("scripts", "convertDefault.py")) + @@ -317,6 +326,9 @@ bool Converters::convert(Buffer const * buffer, Systemcall one; one.startscript(Systemcall::Wait, command); if (isFileReadable(to_file)) { + if (conversionflags & try_cache) + ConverterCache::get().add(orig_from, + to_format, to_file); return true; } } @@ -466,9 +478,8 @@ bool Converters::convert(Buffer const * buffer, return true; if (!conv.result_dir.empty()) { - to_file = addName(subst(conv.result_dir, token_base, to_base), - subst(conv.result_file, - token_base, onlyFilename(to_base))); + // The converter has put the file(s) in a directory. + // In this case we ignore the given to_file. if (from_base != to_base) { string const from = subst(conv.result_dir, token_base, from_base); @@ -477,14 +488,17 @@ bool Converters::convert(Buffer const * buffer, Mover const & mover = movers(conv.from); if (!mover.rename(from, to)) { Alert::error(_("Cannot convert file"), - bformat(_("Could not move a temporary file from %1$s to %2$s."), + bformat(_("Could not move a temporary directory from %1$s to %2$s."), from_ascii(from), from_ascii(to))); return false; } } return true; - } else + } else { + if (conversionflags & try_cache) + ConverterCache::get().add(orig_from, to_format, outfile); return move(conv.to, outfile, to_file, conv.latex); + } } @@ -527,17 +541,6 @@ bool Converters::move(string const & fmt, } -bool Converters::convert(Buffer const * buffer, - string const & from_file, string const & to_file_base, - string const & from_format, string const & to_format, - ErrorList & errorList, bool try_default) -{ - string to_file; - return convert(buffer, from_file, to_file_base, from_format, to_format, - to_file, errorList, try_default); -} - - bool Converters::formatIsUsed(string const & format) { ConverterList::const_iterator cit = converterlist_.begin(); diff --git a/src/converter.h b/src/converter.h index 7f941187d6..9cec2c122b 100644 --- a/src/converter.h +++ b/src/converter.h @@ -107,17 +107,21 @@ public: Graph::EdgePath const getPath(std::string const & from, std::string const & to); /// OutputParams::FLAVOR getFlavor(Graph::EdgePath const & path); + /// Flags for converting files + enum ConversionFlags { + /// No special flags + none = 0, + /// Use the default converter if no converter is defined + try_default = 1 << 0, + /// Get the converted file from cache if possible + try_cache = 1 << 1 + }; /// bool convert(Buffer const * buffer, - std::string const & from_file, std::string const & to_file_base, - std::string const & from_format, std::string const & to_format, - std::string & to_file, ErrorList & errorList, - bool try_default = false); - /// - bool convert(Buffer const * buffer, - std::string const & from_file, std::string const & to_file_base, - std::string const & from_format, std::string const & to_format, - ErrorList & errorList, bool try_default = false); + std::string const & from_file, std::string const & to_file, + std::string const & orig_from, + std::string const & from_format, std::string const & to_format, + ErrorList & errorList, int conversionflags = none); /// void update(Formats const & formats); /// diff --git a/src/exporter.C b/src/exporter.C index fa32fc1e5b..eff1172ae8 100644 --- a/src/exporter.C +++ b/src/exporter.C @@ -217,18 +217,20 @@ bool Exporter::Export(Buffer * buffer, string const & format, } string const error_type = (format == "program")? "Build" : bufferFormat(*buffer); - bool const success = converters.convert(buffer, filename, filename, - backend_format, format, result_file, + string const ext = formats.extension(format); + string const tmp_result_file = changeExtension(filename, ext); + bool const success = converters.convert(buffer, filename, + tmp_result_file, buffer->fileName(), backend_format, format, buffer->errorList(error_type)); // Emit the signal to show the error list. buffer->errors(error_type); if (!success) return false; - if (!put_in_tempdir) { - string const tmp_result_file = result_file; - result_file = changeExtension(buffer->fileName(), - formats.extension(format)); + if (put_in_tempdir) + result_file = tmp_result_file; + else { + result_file = changeExtension(buffer->fileName(), ext); // We need to copy referenced files (e. g. included graphics // if format == "dvi") to the result dir. vector const files = diff --git a/src/graphics/GraphicsCacheItem.C b/src/graphics/GraphicsCacheItem.C index 6ef2e65ff8..8caa6f766f 100644 --- a/src/graphics/GraphicsCacheItem.C +++ b/src/graphics/GraphicsCacheItem.C @@ -16,6 +16,7 @@ #include "GraphicsConverter.h" #include "GraphicsImage.h" +#include "ConverterCache.h" #include "debug.h" #include "format.h" @@ -28,7 +29,6 @@ namespace lyx { -using support::changeExtension; using support::FileMonitor; using support::isFileReadable; using support::makeDisplayPath; @@ -108,6 +108,8 @@ public: bool zipped_; /// If so, store the uncompressed file in this temporary file. string unzipped_filename_; + /// The target format + string to_; /// What file are we trying to load? string file_to_load_; /** Should we delete the file after loading? True if the file is @@ -228,6 +230,7 @@ void CacheItem::Impl::reset() unlink(file_to_load_); remove_loaded_file_ = false; file_to_load_.erase(); + to_.erase(); if (image_.get()) image_.reset(); @@ -278,6 +281,9 @@ void CacheItem::Impl::imageConverted(bool success) return; } + // Add the converted file to the file cache + ConverterCache::get().add(filename_, to_, file_to_load_); + loadImage(); } @@ -403,9 +409,9 @@ void CacheItem::Impl::convertToDisplayFormat() } lyxerr[Debug::GRAPHICS] << "\n\tThe file contains " << from << " format data." << endl; - string const to = findTargetFormat(from); + to_ = findTargetFormat(from); - if (from == to) { + if (from == to_) { // No conversion needed! lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (from == to)!" << endl; file_to_load_ = filename; @@ -413,7 +419,15 @@ void CacheItem::Impl::convertToDisplayFormat() return; } - lyxerr[Debug::GRAPHICS] << "\tConverting it to " << to << " format." << endl; + if (ConverterCache::get().inCache(filename, to_)) { + lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (file in file cache)!" + << endl; + file_to_load_ = ConverterCache::get().cacheName(filename, to_); + loadImage(); + return; + } + + lyxerr[Debug::GRAPHICS] << "\tConverting it to " << to_ << " format." << endl; // Add some stuff to create a uniquely named temporary file. // This file is deleted in loadImage after it is loaded into memory. @@ -427,7 +441,7 @@ void CacheItem::Impl::convertToDisplayFormat() // Connect a signal to this->imageConverted and pass this signal to // the graphics converter so that we can load the modified file // on completion of the conversion process. - converter_.reset(new Converter(filename, to_file_base, from, to)); + converter_.reset(new Converter(filename, to_file_base, from, to_)); converter_->connect(boost::bind(&Impl::imageConverted, this, _1)); converter_->startConversion(); } diff --git a/src/importer.C b/src/importer.C index d00b05c4c8..b37c9f0cfa 100644 --- a/src/importer.C +++ b/src/importer.C @@ -53,8 +53,11 @@ bool Importer::Import(LyXView * lv, string const & filename, for (vector::const_iterator it = loaders.begin(); it != loaders.end(); ++it) { if (converters.isReachable(format, *it)) { - if (!converters.convert(0, filename, filename, - format, *it, errorList)) + string const tofile = + changeExtension(filename, + formats.extension(*it)); + if (!converters.convert(0, filename, tofile, + filename, format, *it, errorList)) return false; loader_format = *it; break; diff --git a/src/insets/ExternalSupport.C b/src/insets/ExternalSupport.C index 30ddda86c4..087ac5d9f3 100644 --- a/src/insets/ExternalSupport.C +++ b/src/insets/ExternalSupport.C @@ -305,14 +305,13 @@ void updateExternal(InsetExternalParams const & params, // Yes if to_file does not exist or if from_file is newer than to_file if (support::compare_timestamps(temp_file, abs_to_file) < 0) return; // SUCCESS - string const to_file_base = - support::changeExtension(to_file, string()); // FIXME (Abdel 12/08/06): Is there a need to show these errors? ErrorList el; /* bool const success = */ - converters.convert(&buffer, temp_file, to_file_base, - from_format, to_format, el, true); + converters.convert(&buffer, temp_file, abs_to_file, + abs_from_file, from_format, to_format, el, + Converters::try_default | Converters::try_cache); // return success } diff --git a/src/insets/insetgraphics.C b/src/insets/insetgraphics.C index da3ff8dbe9..16d12a2094 100644 --- a/src/insets/insetgraphics.C +++ b/src/insets/insetgraphics.C @@ -722,7 +722,9 @@ string const InsetGraphics::prepareFile(Buffer const & buf, // FIXME (Abdel 12/08/06): Is there a need to show these errors? ErrorList el; - if (converters.convert(&buf, temp_file, temp_file, from, to, el, true)) { + if (converters.convert(&buf, temp_file, to_file, orig_file, + from, to, el, + Converters::try_default | Converters::try_cache)) { runparams.exportdata->addExternalFile(tex_format, to_file, output_to_file); runparams.exportdata->addExternalFile("dvi", diff --git a/src/lyx_main.C b/src/lyx_main.C index cc856fb70a..20fce9319c 100644 --- a/src/lyx_main.C +++ b/src/lyx_main.C @@ -17,6 +17,7 @@ #include "lyx_main.h" +#include "ConverterCache.h" #include "buffer.h" #include "buffer_funcs.h" #include "bufferlist.h" @@ -796,6 +797,11 @@ bool LyX::init() lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl; pimpl_->session_.reset(new Session(lyxrc.num_lastfiles)); + + // This must happen after package initialization and after lyxrc is + // read, therefore it can't be done by a static object. + ConverterCache::init(); + return true; } diff --git a/src/lyxrc.C b/src/lyxrc.C index a25bac9dbd..03c9143eb0 100644 --- a/src/lyxrc.C +++ b/src/lyxrc.C @@ -79,6 +79,7 @@ keyword_item lyxrcTags[] = { { "\\check_lastfiles", LyXRC::RC_CHECKLASTFILES }, { "\\chktex_command", LyXRC::RC_CHKTEX_COMMAND }, { "\\converter", LyXRC::RC_CONVERTER }, + { "\\converter_cache_maxage", LyXRC::RC_CONVERTER_CACHE_MAXAGE }, { "\\copier", LyXRC::RC_COPIER }, { "\\cursor_follows_scrollbar", LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR }, { "\\custom_export_command", LyXRC::RC_CUSTOM_EXPORT_COMMAND }, @@ -167,6 +168,7 @@ keyword_item lyxrcTags[] = { { "\\tex_expects_windows_paths", LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS }, { "\\ui_file", LyXRC::RC_UIFILE }, { "\\use_alt_language", LyXRC::RC_USE_ALT_LANG }, + { "\\use_converter_cache", LyXRC::RC_USE_CONVERTER_CACHE }, { "\\use_escape_chars", LyXRC::RC_USE_ESC_CHARS }, { "\\use_input_encoding", LyXRC::RC_USE_INP_ENC }, { "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS }, @@ -289,6 +291,8 @@ void LyXRC::setDefaults() { preview = PREVIEW_OFF; preview_hashed_labels = false; preview_scale_factor = "0.9"; + use_converter_cache = false; + converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months user_name = support::user_name(); @@ -1187,6 +1191,17 @@ int LyXRC::read(LyXLex & lexrc) path_prefix = lexrc.getString(); break; + case RC_USE_CONVERTER_CACHE: + if (lexrc.next()) + use_converter_cache = lexrc.getBool(); + break; + + case RC_CONVERTER_CACHE_MAXAGE: + if (lexrc.next()) + converter_cache_maxage = + convert(lexrc.getString()); + break; + case RC_LAST: break; // this is just a dummy } } @@ -1463,6 +1478,20 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc) const << preview_scale_factor << '\n'; } + case RC_USE_CONVERTER_CACHE: + if (ignore_system_lyxrc || + use_converter_cache != system_lyxrc.use_converter_cache) { + os << "\\use_converter_cache " + << convert(use_converter_cache) << '\n'; + } + + case RC_CONVERTER_CACHE_MAXAGE: + if (ignore_system_lyxrc || + converter_cache_maxage != system_lyxrc.converter_cache_maxage) { + os << "\\converter_cache_maxage" + << converter_cache_maxage << '\n'; + } + os << "\n#\n" << "# SCREEN & FONTS SECTION ############################\n" << "#\n\n"; diff --git a/src/lyxrc.h b/src/lyxrc.h index 138481cb67..8282c21a4e 100644 --- a/src/lyxrc.h +++ b/src/lyxrc.h @@ -50,6 +50,7 @@ public: RC_CHECKLASTFILES, RC_CHKTEX_COMMAND, RC_CONVERTER, + RC_CONVERTER_CACHE_MAXAGE, RC_COPIER, RC_CURSOR_FOLLOWS_SCROLLBAR, RC_CUSTOM_EXPORT_COMMAND, @@ -136,6 +137,7 @@ public: RC_USER_NAME, RC_USETEMPDIR, RC_USE_ALT_LANG, + RC_USE_CONVERTER_CACHE, RC_USE_ESC_CHARS, RC_USE_INP_ENC, RC_USE_PERS_DICT, @@ -396,6 +398,10 @@ public: * The string is input, stored and output in native format. */ std::string path_prefix; + /// Use the cache for file converters? + bool use_converter_cache; + /// The maximum age of cache files in seconds + unsigned int converter_cache_maxage; }; diff --git a/src/mover.C b/src/mover.C index 24950a8706..a88cca859d 100644 --- a/src/mover.C +++ b/src/mover.C @@ -17,11 +17,13 @@ #include "support/lyxlib.h" #include "support/systemcall.h" +#include #include namespace lyx { +using std::ios; using std::string; Movers movers; @@ -29,9 +31,9 @@ Movers system_movers; bool Mover::do_copy(string const & from, string const & to, - string const &) const + string const &, unsigned long int mode) const { - return support::copy(from, to); + return support::copy(from, to, mode); } @@ -43,10 +45,19 @@ bool Mover::do_rename(string const & from, string const & to, bool SpecialisedMover::do_copy(string const & from, string const & to, - string const & latex) const + string const & latex, unsigned long int mode) const { if (command_.empty()) - return Mover::do_copy(from, to, latex); + return Mover::do_copy(from, to, latex, mode); + + if (mode != (unsigned long int)-1) { + std::ofstream ofs(to.c_str(), ios::binary | ios::out | ios::trunc); + if (!ofs) + return false; + ofs.close(); + if (!support::chmod(to.c_str(), mode_t(mode))) + return false; + } string command = support::libScriptSearch(command_); command = support::subst(command, "$$i", from); @@ -64,7 +75,7 @@ bool SpecialisedMover::do_rename(string const & from, string const & to, if (command_.empty()) return Mover::do_rename(from, to, latex); - if (!do_copy(from, to, latex)) + if (!do_copy(from, to, latex, (unsigned long int)-1)) return false; return support::unlink(from) == 0; } diff --git a/src/mover.h b/src/mover.h index 951636bfdc..1aae717766 100644 --- a/src/mover.h +++ b/src/mover.h @@ -34,9 +34,10 @@ public: * \returns true if successful. */ bool - copy(std::string const & from, std::string const & to) const + copy(std::string const & from, std::string const & to, + unsigned long int mode = (unsigned long int)-1) const { - return do_copy(from, to, to); + return do_copy(from, to, to, mode); } /** Copy file @c from to @c to. @@ -49,9 +50,10 @@ public: */ bool copy(std::string const & from, std::string const & to, - std::string const & latex) const + std::string const & latex, + unsigned long int mode = (unsigned long int)-1) const { - return do_copy(from, to, latex); + return do_copy(from, to, latex, mode); } /** Rename file @c from as @c to. @@ -84,7 +86,7 @@ public: protected: virtual bool do_copy(std::string const & from, std::string const & to, - std::string const &) const; + std::string const &, unsigned long int mode) const; virtual bool do_rename(std::string const & from, std::string const & to, @@ -131,7 +133,7 @@ public: private: virtual bool do_copy(std::string const & from, std::string const & to, - std::string const & latex) const; + std::string const & latex, unsigned long int mode) const; virtual bool do_rename(std::string const & from, std::string const & to, diff --git a/src/support/copy.C b/src/support/copy.C index 2d994b8450..869a8db3c4 100644 --- a/src/support/copy.C +++ b/src/support/copy.C @@ -14,6 +14,13 @@ #include "support/lyxlib.h" +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + namespace lyx { @@ -24,12 +31,35 @@ using std::ios; using std::string; -bool lyx::support::copy(string const & from, string const & to) +bool lyx::support::chmod(string const & file, unsigned long int mode) +{ +#ifdef HAVE_CHMOD + if (::chmod(file.c_str(), mode_t(mode)) != 0) + return false; +#else +# ifdef WITH_WARNINGS +# warning "File permissions are ignored on this system." +# endif +#endif + return true; +} + + +bool lyx::support::copy(string const & from, string const & to, unsigned long int mode) { ifstream ifs(from.c_str(), ios::binary | ios::in); if (!ifs) return false; + if (mode != (unsigned long int)-1) { + ofstream ofs(to.c_str(), ios::binary | ios::out | ios::trunc); + if (!ofs) + return false; + ofs.close(); + if (!chmod(to, mode_t(mode))) + return false; + } + ofstream ofs(to.c_str(), ios::binary | ios::out | ios::trunc); if (!ofs) return false; diff --git a/src/support/lyxlib.h b/src/support/lyxlib.h index fa06e1de11..582dacca19 100644 --- a/src/support/lyxlib.h +++ b/src/support/lyxlib.h @@ -25,13 +25,16 @@ namespace support { std::string const getcwd(); /// change to a directory, 0 is returned on success. int chdir(std::string const & name); +/// Change file permissions +bool chmod(std::string const & file, unsigned long int mode); /** * rename a file, returns false if it fails. * It can handle renames across partitions. */ bool rename(std::string const & from, std::string const & to); /// copy a file, returns false it it fails -bool copy(std::string const & from, std::string const & to); +bool copy(std::string const & from, std::string const & to, + unsigned long int mode = (unsigned long int)-1); /// generates a checksum of a file unsigned long sum(std::string const & file); /// FIXME: some point to this hmm ? diff --git a/src/support/mkdir.C b/src/support/mkdir.C index a667e4fc91..3ced971eb0 100644 --- a/src/support/mkdir.C +++ b/src/support/mkdir.C @@ -39,6 +39,9 @@ int lyx::support::mkdir(std::string const & pathname, unsigned long int mode) # if MKDIR_TAKES_ONE_ARG // MinGW32 return ::mkdir(pathname.c_str()); +# ifdef WITH_WARNINGS +# warning "Permissions of created directories are ignored on this system." +# endif # else // POSIX return ::mkdir(pathname.c_str(), mode_t(mode)); @@ -46,8 +49,14 @@ int lyx::support::mkdir(std::string const & pathname, unsigned long int mode) #elif defined(_WIN32) // plain Windows 32 return CreateDirectory(pathname.c_str(), 0) != 0 ? 0 : -1; +# ifdef WITH_WARNINGS +# warning "Permissions of created directories are ignored on this system." +# endif #elif HAVE__MKDIR return ::_mkdir(pathname.c_str()); +# ifdef WITH_WARNINGS +# warning "Permissions of created directories are ignored on this system." +# endif #else # error "Don't know how to create a directory on this system." #endif