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 "support/filetools.h"
29 #include "support/convert.h"
30 #include "support/lyxlib.h"
31 #include "support/lstrings.h"
49 using std::istringstream;
53 namespace Alert = frontend::Alert;
55 using support::FileName;
56 using support::DocFileName;
57 using support::makeAbsPath;
58 using support::addName;
59 using support::absolutePath;
60 using support::onlyFilename;
61 using support::makeRelPath;
62 using support::changeExtension;
63 using support::bformat;
64 using support::prefixIs;
66 using support::makedir;
69 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
70 bool embed, Inset const * inset)
71 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
75 inset_list_.push_back(inset);
79 string EmbeddedFile::embeddedFile(Buffer const * buf) const
81 return addName(buf->temppath(), inzip_name_);
85 void EmbeddedFile::addInset(Inset const * inset)
87 inset_list_.push_back(inset);
91 Inset const * EmbeddedFile::inset(int idx) const
93 BOOST_ASSERT(idx < refCount());
94 // some embedded file do not have a valid par iterator
95 return inset_list_[idx];
99 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
101 Inset const * ptr = inset(idx);
102 // This might not be the most efficient method ...
103 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
105 // this is basically BufferView::saveBookmark(0)
106 LyX::ref().session().bookmarks().save(
107 FileName(buf->absFileName()),
115 // this inset can not be located. There is something wrong that needs
121 string EmbeddedFile::availableFile(Buffer const * buf) const
123 return embedded() ? embeddedFile(buf) : absFilename();
127 bool EmbeddedFile::extract(Buffer const * buf) const
129 string ext_file = absFilename();
130 string emb_file = embeddedFile(buf);
132 FileName emb(emb_file);
133 FileName ext(ext_file);
138 // if external file already exists ...
140 // no need to copy if the files are the same
141 if (sum(*this) == sum(FileName(emb_file)))
143 // otherwise, ask if overwrite
144 int ret = Alert::prompt(
145 _("Overwrite external file?"),
146 bformat(_("External file %1$s already exists, do you want to overwrite it"),
147 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
149 // if the user does not want to overwrite, we still consider it
150 // a successful operation.
155 // need to make directory?
156 FileName path = ext.onlyPath();
157 if (!path.isDirectory())
158 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
161 Alert::error(_("Copy file failure"),
162 bformat(_("Cannot copy file %1$s to %2$s.\n"
163 "Please check whether the directory exists and is writeable."),
164 from_utf8(emb_file), from_utf8(ext_file)));
165 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
170 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
172 string ext_file = absFilename();
173 string emb_file = embeddedFile(buf);
175 FileName emb(emb_file);
176 FileName ext(ext_file);
181 // if embedded file already exists ...
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.
197 // need to make directory?
198 FileName path = emb.onlyPath();
199 if (!path.isDirectory())
200 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
203 Alert::error(_("Copy file failure"),
204 bformat(_("Cannot copy file %1$s to %2$s.\n"
205 "Please check whether the directory exists and is writeable."),
206 from_utf8(ext_file), from_utf8(emb_file)));
207 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
212 void EmbeddedFile::updateInsets(Buffer const * buf) const
214 vector<Inset const *>::const_iterator it = inset_list_.begin();
215 vector<Inset const *>::const_iterator it_end = inset_list_.end();
216 for (; it != it_end; ++it)
217 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
221 bool EmbeddedFiles::enabled() const
223 return buffer_->params().embedded;
227 void EmbeddedFiles::enable(bool flag)
229 if (enabled() != flag) {
230 // if enable, copy all files to temppath()
231 // if disable, extract all files
232 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
234 // if operation is successful
235 buffer_->markDirty();
236 buffer_->params().embedded = flag;
243 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
244 bool embed, Inset const * inset, string const & inzipName)
246 // filename can be relative or absolute, translate to absolute filename
247 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
248 // try to find this file from the list
249 EmbeddedFileList::iterator it = file_list_.begin();
250 EmbeddedFileList::iterator it_end = file_list_.end();
251 for (; it != it_end; ++it)
252 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
254 // find this filename, keep the original embedding status
255 if (it != file_list_.end()) {
260 file_list_.push_back(EmbeddedFile(abs_filename,
261 getInzipName(abs_filename, inzipName), embed, inset));
262 return file_list_.back();
266 void EmbeddedFiles::update()
270 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
271 it->registerEmbeddedFiles(*buffer_, *this);
275 bool EmbeddedFiles::writeFile(DocFileName const & filename)
277 // file in the temporary path has the content
278 string const content = FileName(addName(buffer_->temppath(),
279 "content.lyx")).toFilesystemEncoding();
281 vector<pair<string, string> > filenames;
282 // add content.lyx to filenames
283 filenames.push_back(make_pair(content, "content.lyx"));
284 // prepare list of embedded file
285 EmbeddedFileList::iterator it = file_list_.begin();
286 EmbeddedFileList::iterator it_end = file_list_.end();
287 for (; it != it_end; ++it) {
288 if (it->embedded()) {
289 string file = it->availableFile(buffer_);
291 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
293 filenames.push_back(make_pair(file, it->inzipName()));
296 // write a zip file with all these files. Write to a temp file first, to
297 // avoid messing up the original file in case something goes terribly wrong.
298 DocFileName zipfile(addName(buffer_->temppath(),
299 onlyFilename(changeExtension(
300 filename.toFilesystemEncoding(), ".zip"))));
302 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
304 if (!zipfile.copyTo(filename)) {
305 Alert::error(_("Save failure"),
306 bformat(_("Cannot create file %1$s.\n"
307 "Please check whether the directory exists and is writeable."),
308 from_utf8(filename.absFilename())));
309 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
315 EmbeddedFiles::EmbeddedFileList::const_iterator
316 EmbeddedFiles::find(std::string filename) const
318 EmbeddedFileList::const_iterator it = file_list_.begin();
319 EmbeddedFileList::const_iterator it_end = file_list_.end();
320 for (; it != it_end; ++it)
321 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
323 return file_list_.end();
327 bool EmbeddedFiles::extract() const
329 EmbeddedFileList::const_iterator it = file_list_.begin();
330 EmbeddedFileList::const_iterator it_end = file_list_.end();
331 for (; it != it_end; ++it)
333 if(!it->extract(buffer_))
339 bool EmbeddedFiles::updateFromExternalFile() const
341 EmbeddedFileList::const_iterator it = file_list_.begin();
342 EmbeddedFileList::const_iterator it_end = file_list_.end();
343 for (; it != it_end; ++it)
345 if (!it->updateFromExternalFile(buffer_))
351 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
353 // register a new one, using relative file path as inzip_name
354 string inzip_name = name;
356 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
357 from_utf8(buffer_->filePath())));
358 // if inzip_name is an absolute path, use filename only to avoid
359 // leaking of filesystem information in inzip_name
360 // The second case covers cases '../path/file' and '.'
361 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
362 inzip_name = onlyFilename(abs_filename);
363 // if this name has been used...
364 // use _1_name, _2_name etc
365 string tmp = inzip_name;
366 EmbeddedFileList::iterator it;
367 EmbeddedFileList::iterator it_end = file_list_.end();
368 bool unique_name = false;
370 while (!unique_name) {
373 inzip_name = convert<string>(i) + "_" + tmp;
374 it = file_list_.begin();
375 for (; it != it_end; ++it)
376 if (it->inzipName() == inzip_name) {
386 void EmbeddedFiles::updateInsets() const
388 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
389 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
390 for (; it != it_end; ++it)
391 if (it->refCount() > 0)
392 it->updateInsets(buffer_);