]> git.lyx.org Git - lyx.git/blob - src/ConverterCache.C
fix pch error
[lyx.git] / src / ConverterCache.C
1 /**
2  * \file ConverterCache.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Baruch Even
7  * \author Angus Leeming
8  * \author Georg Baum
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "ConverterCache.h"
16
17 #include "debug.h"
18 #include "lyxrc.h"
19 #include "mover.h"
20
21 #include "support/filetools.h"
22 #include "support/lyxlib.h"
23 #include "support/lyxtime.h"
24 #include "support/package.h"
25
26 #include <boost/crc.hpp>
27 #include <boost/filesystem/operations.hpp>
28 #include <boost/current_function.hpp>
29
30 #include <fstream>
31 #include <iomanip>
32 #include <map>
33 #include <sstream>
34
35 using lyx::support::absolutePath;
36 using lyx::support::addName;
37
38 using std::string;
39
40 namespace fs = boost::filesystem;
41
42 namespace lyx {
43
44 namespace {
45
46 unsigned long do_crc(string const & s)
47 {
48         boost::crc_32_type crc;
49         crc = std::for_each(s.begin(), s.end(), crc);
50         return crc.checksum();
51 }
52
53
54 static string cache_dir;
55
56
57 class CacheItem {
58 public:
59         CacheItem() {}
60         CacheItem(string const & orig_from, string const & to_format,
61                   time_t t, unsigned long c)
62                 : timestamp(t), checksum(c)
63         {
64                 BOOST_ASSERT(absolutePath(orig_from));
65                 std::ostringstream os;
66                 os << std::setw(10) << std::setfill('0') << do_crc(orig_from)
67                    << '-' << to_format;
68                 cache_name = addName(cache_dir, os.str());
69                 lyxerr[Debug::FILES] << "Add file cache item " << orig_from
70                                      << ' ' << to_format << ' ' << cache_name
71                                      << ' ' << timestamp << ' ' << checksum
72                                      << '.' << std::endl;
73         }
74         ~CacheItem() {}
75         string cache_name;
76         time_t timestamp;
77         unsigned long checksum;
78 };
79
80 }
81
82
83 /** The cache contains one item per orig file and target format, so use a
84  *  nested map to find the cache item quickly by filename and format.
85  */
86 typedef std::map<string, CacheItem> FormatCacheType;
87 typedef std::map<string, FormatCacheType> CacheType;
88
89
90 class ConverterCache::Impl {
91 public:
92         ///
93         void readIndex();
94         ///
95         void writeIndex();
96         ///
97         CacheItem * find(string const & from, string const & format);
98         CacheType cache;
99 };
100
101
102 void ConverterCache::Impl::readIndex()
103 {
104         time_t const now = current_time();
105         string const index = addName(cache_dir, "index");
106         std::ifstream is(index.c_str());
107         while (is.good()) {
108                 string orig_from;
109                 string to_format;
110                 time_t timestamp;
111                 unsigned long checksum;
112                 if (!(is >> orig_from >> to_format >> timestamp >> checksum))
113                         return;
114                 CacheItem item(orig_from, to_format, timestamp, checksum);
115
116                 // Don't cache files that do not exist anymore
117                 if (!fs::exists(orig_from)) {
118                         lyxerr[Debug::FILES] << "Not caching file `"
119                                 << orig_from << "' (does not exist anymore)."
120                                 << std::endl;
121                         support::unlink(item.cache_name);
122                         continue;
123                 }
124
125                 // Delete the cached file if it is too old
126                 if (difftime(now, fs::last_write_time(item.cache_name)) >
127                     lyxrc.converter_cache_maxage) {
128                         lyxerr[Debug::FILES] << "Not caching file `"
129                                 << orig_from << "' (too old)." << std::endl;
130                         support::unlink(item.cache_name);
131                         continue;
132                 }
133
134                 cache[orig_from][to_format] = item;
135         }
136         is.close();
137 }
138
139
140 void ConverterCache::Impl::writeIndex()
141 {
142         string const index = addName(cache_dir, "index");
143         std::ofstream os(index.c_str());
144         os.close();
145         if (!lyx::support::chmod(index.c_str(), 0600))
146                 return;
147         os.open(index.c_str());
148         CacheType::iterator it1 = cache.begin();
149         CacheType::iterator const end1 = cache.end();
150         for (; it1 != end1; ++it1) {
151                 FormatCacheType::iterator it2 = it1->second.begin();
152                 FormatCacheType::iterator const end2 = it1->second.end();
153                 for (; it2 != end2; ++it2)
154                         os << it1->first << ' ' << it2->first << ' '
155                            << it2->second.timestamp << ' '
156                            << it2->second.checksum << '\n';
157         }
158         os.close();
159 }
160
161
162 CacheItem * ConverterCache::Impl::find(string const & from,
163                 string const & format)
164 {
165         if (!lyxrc.use_converter_cache)
166                 return 0;
167         CacheType::iterator const it1 = cache.find(from);
168         if (it1 == cache.end())
169                 return 0;
170         FormatCacheType::iterator const it2 = it1->second.find(format);
171         if (it2 == it1->second.end())
172                 return 0;
173         return &(it2->second);
174 }
175
176
177 ConverterCache & ConverterCache::get()
178 {
179         // Now return the cache
180         static ConverterCache singleton;
181         return singleton;
182 }
183
184
185 void ConverterCache::init()
186 {
187         if (!lyxrc.use_converter_cache)
188                 return;
189         // We do this here and not in the constructor because package() gets
190         // initialized after all static variables.
191         cache_dir = addName(support::package().user_support(), "cache");
192         if (!fs::exists(cache_dir))
193                 if (support::mkdir(cache_dir, 0700) != 0) {
194                         lyxerr << "Could not create cache directory `"
195                                << cache_dir << "'." << std::endl;
196                         exit(EXIT_FAILURE);
197                 }
198         get().pimpl_->readIndex();
199 }
200
201
202 ConverterCache::ConverterCache()
203         : pimpl_(new Impl)
204 {}
205
206
207 ConverterCache::~ConverterCache()
208 {
209         if (!lyxrc.use_converter_cache)
210                 return;
211         pimpl_->writeIndex();
212 }
213
214
215 void ConverterCache::add(string const & orig_from, string const & to_format,
216                 string const & converted_file) const
217 {
218         if (!lyxrc.use_converter_cache)
219                 return;
220         lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from
221                              << ' ' << to_format << ' ' << converted_file
222                              << std::endl;
223         BOOST_ASSERT(absolutePath(orig_from));
224         BOOST_ASSERT(absolutePath(converted_file));
225
226         // Is the file in the cache already?
227         CacheItem * item = pimpl_->find(orig_from, to_format);
228
229         time_t const timestamp = fs::last_write_time(orig_from);
230         Mover const & mover = movers(to_format);
231         if (item) {
232                 lyxerr[Debug::FILES] << "ConverterCache::add(" << orig_from << "):\n"
233                                         "The file is already in the cache."
234                                      << std::endl;
235                 // First test for timestamp
236                 if (timestamp == item->timestamp) {
237                         lyxerr[Debug::FILES] << "Same timestamp."
238                                              << std::endl;
239                         return;
240                 } else {
241                         // Maybe the contents is still the same?
242                         item->timestamp = timestamp;
243                         unsigned long const checksum = support::sum(orig_from);
244                         if (checksum == item->checksum) {
245                                 lyxerr[Debug::FILES] << "Same checksum."
246                                                      << std::endl;
247                                 return;
248                         }
249                         item->checksum = checksum;
250                 }
251                 if (!mover.copy(converted_file, item->cache_name, 0600))
252                         lyxerr[Debug::FILES] << "ConverterCache::add("
253                                              << orig_from << "):\n"
254                                                 "Could not copy file."
255                                              << std::endl;
256         } else {
257                 CacheItem new_item = CacheItem(orig_from, to_format, timestamp,
258                                 support::sum(orig_from));
259                 if (mover.copy(converted_file, new_item.cache_name, 0600))
260                         pimpl_->cache[orig_from][to_format] = new_item;
261                 else
262                         lyxerr[Debug::FILES] << "ConverterCache::add("
263                                              << orig_from << "):\n"
264                                                 "Could not copy file."
265                                              << std::endl;
266         }
267 }
268
269
270 void ConverterCache::remove(string const & orig_from,
271                 string const & to_format) const
272 {
273         if (!lyxrc.use_converter_cache)
274                 return;
275         lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from
276                              << ' ' << to_format << std::endl;
277         BOOST_ASSERT(absolutePath(orig_from));
278
279         CacheType::iterator const it1 = pimpl_->cache.find(orig_from);
280         if (it1 == pimpl_->cache.end())
281                 return;
282         FormatCacheType::iterator const it2 = it1->second.find(to_format);
283         if (it2 == it1->second.end())
284                 return;
285
286         it1->second.erase(it2);
287         if (it1->second.empty())
288                 pimpl_->cache.erase(it1);
289 }
290
291
292 bool ConverterCache::inCache(string const & orig_from,
293                 string const & to_format) const
294 {
295         if (!lyxrc.use_converter_cache)
296                 return false;
297         lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from
298                              << ' ' << to_format << std::endl;
299         BOOST_ASSERT(absolutePath(orig_from));
300
301         CacheItem * const item = pimpl_->find(orig_from, to_format);
302         if (!item) {
303                 lyxerr[Debug::FILES] << "not in cache." << std::endl;
304                 return false;
305         }
306         time_t const timestamp = fs::last_write_time(orig_from);
307         if (item->timestamp == timestamp) {
308                 lyxerr[Debug::FILES] << "identical timestamp." << std::endl;
309                 return true;
310         }
311         if (item->checksum == support::sum(orig_from)) {
312                 item->timestamp = timestamp;
313                 lyxerr[Debug::FILES] << "identical checksum." << std::endl;
314                 return true;
315         }
316         lyxerr[Debug::FILES] << "in cache, but too old." << std::endl;
317         return false;
318 }
319
320
321 string const ConverterCache::cacheName(string const & orig_from,
322                 string const & to_format) const
323 {
324         lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from
325                              << ' ' << to_format << std::endl;
326         BOOST_ASSERT(absolutePath(orig_from));
327
328         CacheItem * const item = pimpl_->find(orig_from, to_format);
329         BOOST_ASSERT(item);
330         return item->cache_name;
331 }
332
333
334 bool ConverterCache::copy(string const & orig_from, string const & to_format,
335                 string const & dest) const
336 {
337         if (!lyxrc.use_converter_cache)
338                 return false;
339         lyxerr[Debug::FILES] << BOOST_CURRENT_FUNCTION << ' ' << orig_from
340                              << ' ' << to_format << ' ' << dest << std::endl;
341         BOOST_ASSERT(absolutePath(orig_from));
342         BOOST_ASSERT(absolutePath(dest));
343
344         CacheItem * const item = pimpl_->find(orig_from, to_format);
345         BOOST_ASSERT(item);
346         Mover const & mover = movers(to_format);
347         return mover.copy(item->cache_name, dest);
348 }
349
350 } // namespace lyx