As promised I ported the image file cache to 1.5. I use it without problems 
since several weeks, and it works fine.
I added two lyxrc settings (without GUI yet, you have to write them by 
hand). On windows it is not possible yet to tighten the permissions of the 
cache files, but I understand that the directory we use cannot be read by 
other users, so this is not too important (and btw the situation is the 
same for the temporary folders).

José, can this go in?


Georg
Index: src/insets/insetgraphics.C
===================================================================
--- src/insets/insetgraphics.C	(Revision 15863)
+++ src/insets/insetgraphics.C	(Arbeitskopie)
@@ -722,7 +722,9 @@ string const InsetGraphics::prepareFile(
 
 	// 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",
Index: src/insets/ExternalSupport.C
===================================================================
--- src/insets/ExternalSupport.C	(Revision 15863)
+++ src/insets/ExternalSupport.C	(Arbeitskopie)
@@ -305,14 +305,13 @@ void updateExternal(InsetExternalParams 
 	// 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
 }
 
Index: src/exporter.C
===================================================================
--- src/exporter.C	(Revision 15863)
+++ src/exporter.C	(Arbeitskopie)
@@ -217,18 +217,20 @@ bool Exporter::Export(Buffer * buffer, s
 	}
 
 	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<ExportedFile> const files =
Index: src/graphics/GraphicsCacheItem.C
===================================================================
--- src/graphics/GraphicsCacheItem.C	(Revision 15863)
+++ src/graphics/GraphicsCacheItem.C	(Arbeitskopie)
@@ -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(boo
 		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::convertToDisplayFo
 	}
 	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::convertToDisplayFo
 		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::convertToDisplayFo
 	// 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();
 }
Index: src/converter.C
===================================================================
--- src/converter.C	(Revision 15863)
+++ src/converter.C	(Arbeitskopie)
@@ -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::getFlav
 
 
 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 to_ext = formats.extension(to_format);
-	to_file = changeExtension(to_file_base, to_ext);
+                         string const & from_file, string const & to_file,
+                         string const & orig_from,
+                         string const & from_format, string const & to_format,
+                         ErrorList & errorList, int conversionflags)
+{
+	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 * 
 			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 * 
 		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 * 
 			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();
Index: src/converter.h
===================================================================
--- src/converter.h	(Revision 15863)
+++ src/converter.h	(Arbeitskopie)
@@ -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);
 	///
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(Revision 15863)
+++ src/Makefile.am	(Arbeitskopie)
@@ -72,6 +72,8 @@ lyx_SOURCES = \
 	Chktex.h \
 	Color.C \
 	Color.h \
+	ConverterCache.C \
+	ConverterCache.h \
 	CutAndPaste.C \
 	CutAndPaste.h \
 	DepTable.C \
Index: src/support/lyxlib.h
===================================================================
--- src/support/lyxlib.h	(Revision 15863)
+++ src/support/lyxlib.h	(Arbeitskopie)
@@ -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 ?
Index: src/support/copy.C
===================================================================
--- src/support/copy.C	(Revision 15863)
+++ src/support/copy.C	(Arbeitskopie)
@@ -14,6 +14,13 @@
 
 #include "support/lyxlib.h"
 
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#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;
Index: src/support/mkdir.C
===================================================================
--- src/support/mkdir.C	(Revision 15863)
+++ src/support/mkdir.C	(Arbeitskopie)
@@ -39,6 +39,9 @@ int lyx::support::mkdir(std::string cons
 # 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 cons
 #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
Index: src/lyxrc.C
===================================================================
--- src/lyxrc.C	(Revision 15863)
+++ src/lyxrc.C	(Arbeitskopie)
@@ -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<unsigned int>(lexrc.getString());
+			break;
+
 		case RC_LAST: break; // this is just a dummy
 		}
 	}
@@ -1463,6 +1478,20 @@ void LyXRC::write(ostream & os, bool ign
 			   << 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<string>(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";
Index: src/ConverterCache.C
===================================================================
--- src/ConverterCache.C	(Revision 0)
+++ src/ConverterCache.C	(Revision 0)
@@ -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 <config.h>
+
+#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 <boost/crc.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <fstream>
+#include <iomanip>
+#include <map>
+#include <sstream>
+
+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<string, CacheItem> FormatCacheType;
+typedef std::map<string, FormatCacheType> 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
Index: src/importer.C
===================================================================
--- src/importer.C	(Revision 15863)
+++ src/importer.C	(Arbeitskopie)
@@ -53,8 +53,11 @@ bool Importer::Import(LyXView * lv, stri
 		for (vector<string>::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;
Index: src/lyxrc.h
===================================================================
--- src/lyxrc.h	(Revision 15863)
+++ src/lyxrc.h	(Arbeitskopie)
@@ -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;
 };
 
 
Index: src/ConverterCache.h
===================================================================
--- src/ConverterCache.h	(Revision 0)
+++ src/ConverterCache.h	(Revision 0)
@@ -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 <boost/utility.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <string>
+
+
+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<Impl> const pimpl_;
+};
+
+} // namespace lyx
+
+#endif
Index: src/lyx_main.C
===================================================================
--- src/lyx_main.C	(Revision 15863)
+++ src/lyx_main.C	(Arbeitskopie)
@@ -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;
 }
 
Index: src/mover.C
===================================================================
--- src/mover.C	(Revision 15863)
+++ src/mover.C	(Arbeitskopie)
@@ -17,11 +17,13 @@
 #include "support/lyxlib.h"
 #include "support/systemcall.h"
 
+#include <fstream>
 #include <sstream>
 
 
 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 & fro
 
 
 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 
 	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;
 }
Index: src/mover.h
===================================================================
--- src/mover.h	(Revision 15863)
+++ src/mover.h	(Arbeitskopie)
@@ -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,
Index: configure.ac
===================================================================
--- configure.ac	(Revision 15863)
+++ configure.ac	(Arbeitskopie)
@@ -260,7 +260,7 @@ dnl work correctly because of some confl
 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
Index: development/scons/scons_manifest.py
===================================================================
--- development/scons/scons_manifest.py	(Revision 15863)
+++ development/scons/scons_manifest.py	(Arbeitskopie)
@@ -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

Reply via email to