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"
18 #include "BufferParams.h"
19 #include "ErrorList.h"
21 #include "InsetIterator.h"
24 #include "Paragraph.h"
27 #include "frontends/alert.h"
29 #include "support/debug.h"
30 #include "support/filetools.h"
31 #include "support/gettext.h"
32 #include "support/convert.h"
33 #include "support/lyxlib.h"
34 #include "support/lstrings.h"
35 #include "support/FileZipListDir.h"
42 using namespace lyx::support;
46 namespace Alert = frontend::Alert;
49 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
50 bool embed, Inset const * inset)
51 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
55 inset_list_.push_back(inset);
59 string EmbeddedFile::embeddedFile(Buffer const * buf) const
61 return addName(buf->temppath(), inzip_name_);
65 void EmbeddedFile::addInset(Inset const * inset)
67 inset_list_.push_back(inset);
71 Inset const * EmbeddedFile::inset(int idx) const
73 BOOST_ASSERT(idx < refCount());
74 // some embedded file do not have a valid par iterator
75 return inset_list_[idx];
79 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
81 Inset const * ptr = inset(idx);
82 // This might not be the most efficient method ...
83 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
85 // this is basically BufferView::saveBookmark(0)
86 LyX::ref().session().bookmarks().save(
87 FileName(buf->absFileName()),
95 // this inset can not be located. There is something wrong that needs
101 string EmbeddedFile::availableFile(Buffer const * buf) const
103 return embedded() ? embeddedFile(buf) : absFilename();
107 bool EmbeddedFile::extract(Buffer const * buf) const
109 string ext_file = absFilename();
110 string emb_file = embeddedFile(buf);
112 FileName emb(emb_file);
113 FileName ext(ext_file);
118 // if external file already exists ...
120 // no need to copy if the files are the same
121 if (checksum() == FileName(emb_file).checksum())
123 // otherwise, ask if overwrite
124 int ret = Alert::prompt(
125 _("Overwrite external file?"),
126 bformat(_("External file %1$s already exists, do you want to overwrite it"),
127 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
129 // if the user does not want to overwrite, we still consider it
130 // a successful operation.
135 // need to make directory?
136 FileName path = ext.onlyPath();
137 if (!path.isDirectory())
138 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
141 Alert::error(_("Copy file failure"),
142 bformat(_("Cannot copy file %1$s to %2$s.\n"
143 "Please check whether the directory exists and is writeable."),
144 from_utf8(emb_file), from_utf8(ext_file)));
145 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
150 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
152 string ext_file = absFilename();
153 string emb_file = embeddedFile(buf);
155 FileName emb(emb_file);
156 FileName ext(ext_file);
161 // if embedded file already exists ...
163 // no need to copy if the files are the same
164 if (checksum() == FileName(emb_file).checksum())
166 // other wise, ask if overwrite
167 int const ret = Alert::prompt(
168 _("Update embedded file?"),
169 bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
170 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
172 // if the user does not want to overwrite, we still consider it
173 // a successful operation.
177 // need to make directory?
178 FileName path = emb.onlyPath();
179 if (!path.isDirectory())
180 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
183 Alert::error(_("Copy file failure"),
184 bformat(_("Cannot copy file %1$s to %2$s.\n"
185 "Please check whether the directory exists and is writeable."),
186 from_utf8(ext_file), from_utf8(emb_file)));
187 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
192 void EmbeddedFile::updateInsets(Buffer const * buf) const
194 vector<Inset const *>::const_iterator it = inset_list_.begin();
195 vector<Inset const *>::const_iterator it_end = inset_list_.end();
196 for (; it != it_end; ++it)
197 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
201 bool EmbeddedFiles::enabled() const
203 return buffer_->params().embedded;
207 void EmbeddedFiles::enable(bool flag)
209 if (enabled() != flag) {
210 // if enable, copy all files to temppath()
211 // if disable, extract all files
212 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
214 // if operation is successful
215 buffer_->markDirty();
216 buffer_->params().embedded = flag;
223 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
224 bool embed, Inset const * inset, string const & inzipName)
226 // filename can be relative or absolute, translate to absolute filename
227 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
228 // try to find this file from the list
229 EmbeddedFileList::iterator it = file_list_.begin();
230 EmbeddedFileList::iterator it_end = file_list_.end();
231 for (; it != it_end; ++it)
232 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
234 // find this filename, keep the original embedding status
235 if (it != file_list_.end()) {
240 file_list_.push_back(EmbeddedFile(abs_filename,
241 getInzipName(abs_filename, inzipName), embed, inset));
242 return file_list_.back();
246 void EmbeddedFiles::update()
250 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
251 it->registerEmbeddedFiles(*buffer_, *this);
255 bool EmbeddedFiles::writeFile(DocFileName const & filename)
257 // file in the temporary path has the content
258 string const content = FileName(addName(buffer_->temppath(),
259 "content.lyx")).toFilesystemEncoding();
261 vector<pair<string, string> > filenames;
262 // add content.lyx to filenames
263 filenames.push_back(make_pair(content, "content.lyx"));
264 // prepare list of embedded file
265 EmbeddedFileList::iterator it = file_list_.begin();
266 EmbeddedFileList::iterator it_end = file_list_.end();
267 for (; it != it_end; ++it) {
268 if (it->embedded()) {
269 string file = it->availableFile(buffer_);
271 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
273 filenames.push_back(make_pair(file, it->inzipName()));
276 // write a zip file with all these files. Write to a temp file first, to
277 // avoid messing up the original file in case something goes terribly wrong.
278 DocFileName zipfile(addName(buffer_->temppath(),
279 onlyFilename(changeExtension(
280 filename.toFilesystemEncoding(), ".zip"))));
282 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
284 if (!zipfile.copyTo(filename)) {
285 Alert::error(_("Save failure"),
286 bformat(_("Cannot create file %1$s.\n"
287 "Please check whether the directory exists and is writeable."),
288 from_utf8(filename.absFilename())));
289 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
295 EmbeddedFiles::EmbeddedFileList::const_iterator
296 EmbeddedFiles::find(string filename) const
298 EmbeddedFileList::const_iterator it = file_list_.begin();
299 EmbeddedFileList::const_iterator it_end = file_list_.end();
300 for (; it != it_end; ++it)
301 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
303 return file_list_.end();
307 bool EmbeddedFiles::extract() const
309 EmbeddedFileList::const_iterator it = file_list_.begin();
310 EmbeddedFileList::const_iterator it_end = file_list_.end();
311 for (; it != it_end; ++it)
313 if(!it->extract(buffer_))
319 bool EmbeddedFiles::updateFromExternalFile() const
321 EmbeddedFileList::const_iterator it = file_list_.begin();
322 EmbeddedFileList::const_iterator it_end = file_list_.end();
323 for (; it != it_end; ++it)
325 if (!it->updateFromExternalFile(buffer_))
331 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
333 // register a new one, using relative file path as inzip_name
334 string inzip_name = name;
336 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
337 from_utf8(buffer_->filePath())));
338 // if inzip_name is an absolute path, use filename only to avoid
339 // leaking of filesystem information in inzip_name
340 // The second case covers cases '../path/file' and '.'
341 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
342 inzip_name = onlyFilename(abs_filename);
343 // if this name has been used...
344 // use _1_name, _2_name etc
345 string tmp = inzip_name;
346 EmbeddedFileList::iterator it;
347 EmbeddedFileList::iterator it_end = file_list_.end();
348 bool unique_name = false;
350 while (!unique_name) {
353 inzip_name = convert<string>(i) + "_" + tmp;
354 it = file_list_.begin();
355 for (; it != it_end; ++it)
356 if (it->inzipName() == inzip_name) {
366 void EmbeddedFiles::updateInsets() const
368 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
369 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
370 for (; it != it_end; ++it)
371 if (it->refCount() > 0)
372 it->updateInsets(buffer_);