3 * \file EmbeddedFiles.cpp
4 * This file is part of LyX, the document processor.
5 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
15 #include "EmbeddedFiles.h"
17 #include "BufferParams.h"
18 #include "Paragraph.h"
19 #include "InsetIterator.h"
24 #include "ErrorList.h"
26 #include "frontends/alert.h"
28 #include <boost/filesystem/operations.hpp>
30 #include "support/filetools.h"
31 #include "support/fs_extras.h"
32 #include "support/convert.h"
33 #include "support/lyxlib.h"
34 #include "support/lstrings.h"
52 using std::istringstream;
56 namespace fs = boost::filesystem;
57 namespace Alert = frontend::Alert;
59 using support::FileName;
60 using support::DocFileName;
61 using support::makeAbsPath;
62 using support::addName;
63 using support::absolutePath;
64 using support::onlyFilename;
65 using support::makeRelPath;
66 using support::changeExtension;
67 using support::bformat;
68 using support::prefixIs;
70 using support::makedir;
73 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
74 bool embed, Inset const * inset)
75 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
79 inset_list_.push_back(inset);
83 string EmbeddedFile::embeddedFile(Buffer const * buf) const
85 return addName(buf->temppath(), inzip_name_);
89 void EmbeddedFile::addInset(Inset const * inset)
91 inset_list_.push_back(inset);
95 Inset const * EmbeddedFile::inset(int idx) const
97 BOOST_ASSERT(idx < refCount());
98 // some embedded file do not have a valid par iterator
99 return inset_list_[idx];
103 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
105 Inset const * ptr = inset(idx);
106 // This might not be the most efficient method ...
107 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
109 // this is basically BufferView::saveBookmark(0)
110 LyX::ref().session().bookmarks().save(
111 FileName(buf->absFileName()),
119 // this inset can not be located. There is something wrong that needs
125 string EmbeddedFile::availableFile(Buffer const * buf) const
127 return embedded() ? embeddedFile(buf) : absFilename();
131 bool EmbeddedFile::extract(Buffer const * buf) const
133 string ext_file = absFilename();
134 string emb_file = embeddedFile(buf);
136 if (!fs::exists(emb_file))
139 // if external file already exists ...
140 if (fs::exists(ext_file)) {
141 // no need to copy if the files are the same
142 if (sum(*this) == sum(FileName(emb_file)))
144 // otherwise, ask if overwrite
145 int ret = Alert::prompt(
146 _("Overwrite external file?"),
147 bformat(_("External file %1$s already exists, do you want to overwrite it"),
148 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
150 // if the user does not want to overwrite, we still consider it
151 // a successful operation.
156 // need to make directory?
157 string path = support::onlyPath(ext_file);
158 if (!fs::is_directory(path))
159 makedir(const_cast<char*>(path.c_str()), 0755);
160 fs::copy_file(emb_file, ext_file, false);
162 } catch (fs::filesystem_error const & fe) {
163 Alert::error(_("Copy file failure"),
164 bformat(_("Cannot copy file %1$s to %2$s.\n"
165 "Please check whether the directory exists and is writeable."),
166 from_utf8(emb_file), from_utf8(ext_file)));
167 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
173 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
175 string ext_file = absFilename();
176 string emb_file = embeddedFile(buf);
178 if (!fs::exists(ext_file))
181 // if embedded file already exists ...
182 if (fs::exists(emb_file)) {
183 // no need to copy if the files are the same
184 if (sum(*this) == sum(FileName(emb_file)))
186 // other wise, ask if overwrite
187 int const ret = Alert::prompt(
188 _("Update embedded file?"),
189 bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
190 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
192 // if the user does not want to overwrite, we still consider it
193 // a successful operation.
198 // need to make directory?
199 string path = support::onlyPath(emb_file);
200 if (!fs::is_directory(path))
201 makedir(const_cast<char*>(path.c_str()), 0755);
202 fs::copy_file(ext_file, emb_file, false);
204 } catch (fs::filesystem_error const & fe) {
205 Alert::error(_("Copy file failure"),
206 bformat(_("Cannot copy file %1$s to %2$s.\n"
207 "Please check whether the directory exists and is writeable."),
208 from_utf8(ext_file), from_utf8(emb_file)));
209 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
215 void EmbeddedFile::updateInsets(Buffer const * buf) const
217 vector<Inset const *>::const_iterator it = inset_list_.begin();
218 vector<Inset const *>::const_iterator it_end = inset_list_.end();
219 for (; it != it_end; ++it)
220 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
224 bool EmbeddedFiles::enabled() const
226 return buffer_->params().embedded;
230 void EmbeddedFiles::enable(bool flag)
232 if (enabled() != flag) {
233 // if enable, copy all files to temppath()
234 // if disable, extract all files
235 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
237 // if operation is successful
238 buffer_->markDirty();
239 buffer_->params().embedded = flag;
246 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
247 bool embed, Inset const * inset, string const & inzipName)
249 // filename can be relative or absolute, translate to absolute filename
250 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
251 // try to find this file from the list
252 EmbeddedFileList::iterator it = file_list_.begin();
253 EmbeddedFileList::iterator it_end = file_list_.end();
254 for (; it != it_end; ++it)
255 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
257 // find this filename, keep the original embedding status
258 if (it != file_list_.end()) {
263 file_list_.push_back(EmbeddedFile(abs_filename,
264 getInzipName(abs_filename, inzipName), embed, inset));
265 return file_list_.back();
269 void EmbeddedFiles::update()
273 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
274 it->registerEmbeddedFiles(*buffer_, *this);
278 bool EmbeddedFiles::writeFile(DocFileName const & filename)
280 // file in the temporary path has the content
281 string const content = FileName(addName(buffer_->temppath(),
282 "content.lyx")).toFilesystemEncoding();
284 vector<pair<string, string> > filenames;
285 // add content.lyx to filenames
286 filenames.push_back(make_pair(content, "content.lyx"));
287 // prepare list of embedded file
288 EmbeddedFileList::iterator it = file_list_.begin();
289 EmbeddedFileList::iterator it_end = file_list_.end();
290 for (; it != it_end; ++it) {
291 if (it->embedded()) {
292 string file = it->availableFile(buffer_);
294 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
296 filenames.push_back(make_pair(file, it->inzipName()));
299 // write a zip file with all these files. Write to a temp file first, to
300 // avoid messing up the original file in case something goes terribly wrong.
301 DocFileName zipfile(addName(buffer_->temppath(),
302 onlyFilename(changeExtension(
303 filename.toFilesystemEncoding(), ".zip"))));
305 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
308 fs::copy_file(zipfile.toFilesystemEncoding(), filename.toFilesystemEncoding(), false);
309 } catch (fs::filesystem_error const & fe) {
310 Alert::error(_("Save failure"),
311 bformat(_("Cannot create file %1$s.\n"
312 "Please check whether the directory exists and is writeable."),
313 from_utf8(filename.absFilename())));
314 LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl;
320 EmbeddedFiles::EmbeddedFileList::const_iterator EmbeddedFiles::find(std::string filename) const
322 EmbeddedFileList::const_iterator it = file_list_.begin();
323 EmbeddedFileList::const_iterator it_end = file_list_.end();
324 for (; it != it_end; ++it)
325 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
327 return file_list_.end();
331 bool EmbeddedFiles::extract() const
333 EmbeddedFileList::const_iterator it = file_list_.begin();
334 EmbeddedFileList::const_iterator it_end = file_list_.end();
335 for (; it != it_end; ++it)
337 if(!it->extract(buffer_))
343 bool EmbeddedFiles::updateFromExternalFile() const
345 EmbeddedFileList::const_iterator it = file_list_.begin();
346 EmbeddedFileList::const_iterator it_end = file_list_.end();
347 for (; it != it_end; ++it)
349 if (!it->updateFromExternalFile(buffer_))
355 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
357 // register a new one, using relative file path as inzip_name
358 string inzip_name = name;
360 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
361 from_utf8(buffer_->filePath())));
362 // if inzip_name is an absolute path, use filename only to avoid
363 // leaking of filesystem information in inzip_name
364 // The second case covers cases '../path/file' and '.'
365 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
366 inzip_name = onlyFilename(abs_filename);
367 // if this name has been used...
368 // use _1_name, _2_name etc
369 string tmp = inzip_name;
370 EmbeddedFileList::iterator it;
371 EmbeddedFileList::iterator it_end = file_list_.end();
372 bool unique_name = false;
374 while (!unique_name) {
377 inzip_name = convert<string>(i) + "_" + tmp;
378 it = file_list_.begin();
379 for (; it != it_end; ++it)
380 if (it->inzipName() == inzip_name) {
390 void EmbeddedFiles::updateInsets() const
392 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
393 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
394 for (; it != it_end; ++it)
395 if (it->refCount() > 0)
396 it->updateInsets(buffer_);