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"
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;
65 using support::makedir;
68 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
69 bool embed, Inset const * inset)
70 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
74 inset_list_.push_back(inset);
78 string EmbeddedFile::embeddedFile(Buffer const * buf) const
80 return addName(buf->temppath(), inzip_name_);
84 void EmbeddedFile::addInset(Inset const * inset)
86 inset_list_.push_back(inset);
90 Inset const * EmbeddedFile::inset(int idx) const
92 BOOST_ASSERT(idx < refCount());
93 // some embedded file do not have a valid par iterator
94 return inset_list_[idx];
98 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
100 Inset const * ptr = inset(idx);
101 // This might not be the most efficient method ...
102 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
104 // this is basically BufferView::saveBookmark(0)
105 LyX::ref().session().bookmarks().save(
106 FileName(buf->absFileName()),
114 // this inset can not be located. There is something wrong that needs
120 string EmbeddedFile::availableFile(Buffer const * buf) const
122 return embedded() ? embeddedFile(buf) : absFilename();
126 bool EmbeddedFile::extract(Buffer const * buf) const
128 string ext_file = absFilename();
129 string emb_file = embeddedFile(buf);
131 FileName emb(emb_file);
132 FileName ext(ext_file);
137 // if external file already exists ...
139 // no need to copy if the files are the same
140 if (checksum() == FileName(emb_file).checksum())
142 // otherwise, ask if overwrite
143 int ret = Alert::prompt(
144 _("Overwrite external file?"),
145 bformat(_("External file %1$s already exists, do you want to overwrite it"),
146 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
148 // if the user does not want to overwrite, we still consider it
149 // a successful operation.
154 // need to make directory?
155 FileName path = ext.onlyPath();
156 if (!path.isDirectory())
157 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
160 Alert::error(_("Copy file failure"),
161 bformat(_("Cannot copy file %1$s to %2$s.\n"
162 "Please check whether the directory exists and is writeable."),
163 from_utf8(emb_file), from_utf8(ext_file)));
164 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
169 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
171 string ext_file = absFilename();
172 string emb_file = embeddedFile(buf);
174 FileName emb(emb_file);
175 FileName ext(ext_file);
180 // if embedded file already exists ...
182 // no need to copy if the files are the same
183 if (checksum() == FileName(emb_file).checksum())
185 // other wise, ask if overwrite
186 int const ret = Alert::prompt(
187 _("Update embedded file?"),
188 bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
189 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
191 // if the user does not want to overwrite, we still consider it
192 // a successful operation.
196 // need to make directory?
197 FileName path = emb.onlyPath();
198 if (!path.isDirectory())
199 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
202 Alert::error(_("Copy file failure"),
203 bformat(_("Cannot copy file %1$s to %2$s.\n"
204 "Please check whether the directory exists and is writeable."),
205 from_utf8(ext_file), from_utf8(emb_file)));
206 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
211 void EmbeddedFile::updateInsets(Buffer const * buf) const
213 vector<Inset const *>::const_iterator it = inset_list_.begin();
214 vector<Inset const *>::const_iterator it_end = inset_list_.end();
215 for (; it != it_end; ++it)
216 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
220 bool EmbeddedFiles::enabled() const
222 return buffer_->params().embedded;
226 void EmbeddedFiles::enable(bool flag)
228 if (enabled() != flag) {
229 // if enable, copy all files to temppath()
230 // if disable, extract all files
231 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
233 // if operation is successful
234 buffer_->markDirty();
235 buffer_->params().embedded = flag;
242 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
243 bool embed, Inset const * inset, string const & inzipName)
245 // filename can be relative or absolute, translate to absolute filename
246 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
247 // try to find this file from the list
248 EmbeddedFileList::iterator it = file_list_.begin();
249 EmbeddedFileList::iterator it_end = file_list_.end();
250 for (; it != it_end; ++it)
251 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
253 // find this filename, keep the original embedding status
254 if (it != file_list_.end()) {
259 file_list_.push_back(EmbeddedFile(abs_filename,
260 getInzipName(abs_filename, inzipName), embed, inset));
261 return file_list_.back();
265 void EmbeddedFiles::update()
269 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
270 it->registerEmbeddedFiles(*buffer_, *this);
274 bool EmbeddedFiles::writeFile(DocFileName const & filename)
276 // file in the temporary path has the content
277 string const content = FileName(addName(buffer_->temppath(),
278 "content.lyx")).toFilesystemEncoding();
280 vector<pair<string, string> > filenames;
281 // add content.lyx to filenames
282 filenames.push_back(make_pair(content, "content.lyx"));
283 // prepare list of embedded file
284 EmbeddedFileList::iterator it = file_list_.begin();
285 EmbeddedFileList::iterator it_end = file_list_.end();
286 for (; it != it_end; ++it) {
287 if (it->embedded()) {
288 string file = it->availableFile(buffer_);
290 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
292 filenames.push_back(make_pair(file, it->inzipName()));
295 // write a zip file with all these files. Write to a temp file first, to
296 // avoid messing up the original file in case something goes terribly wrong.
297 DocFileName zipfile(addName(buffer_->temppath(),
298 onlyFilename(changeExtension(
299 filename.toFilesystemEncoding(), ".zip"))));
301 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
303 if (!zipfile.copyTo(filename)) {
304 Alert::error(_("Save failure"),
305 bformat(_("Cannot create file %1$s.\n"
306 "Please check whether the directory exists and is writeable."),
307 from_utf8(filename.absFilename())));
308 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
314 EmbeddedFiles::EmbeddedFileList::const_iterator
315 EmbeddedFiles::find(std::string filename) const
317 EmbeddedFileList::const_iterator it = file_list_.begin();
318 EmbeddedFileList::const_iterator it_end = file_list_.end();
319 for (; it != it_end; ++it)
320 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
322 return file_list_.end();
326 bool EmbeddedFiles::extract() const
328 EmbeddedFileList::const_iterator it = file_list_.begin();
329 EmbeddedFileList::const_iterator it_end = file_list_.end();
330 for (; it != it_end; ++it)
332 if(!it->extract(buffer_))
338 bool EmbeddedFiles::updateFromExternalFile() const
340 EmbeddedFileList::const_iterator it = file_list_.begin();
341 EmbeddedFileList::const_iterator it_end = file_list_.end();
342 for (; it != it_end; ++it)
344 if (!it->updateFromExternalFile(buffer_))
350 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
352 // register a new one, using relative file path as inzip_name
353 string inzip_name = name;
355 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
356 from_utf8(buffer_->filePath())));
357 // if inzip_name is an absolute path, use filename only to avoid
358 // leaking of filesystem information in inzip_name
359 // The second case covers cases '../path/file' and '.'
360 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
361 inzip_name = onlyFilename(abs_filename);
362 // if this name has been used...
363 // use _1_name, _2_name etc
364 string tmp = inzip_name;
365 EmbeddedFileList::iterator it;
366 EmbeddedFileList::iterator it_end = file_list_.end();
367 bool unique_name = false;
369 while (!unique_name) {
372 inzip_name = convert<string>(i) + "_" + tmp;
373 it = file_list_.begin();
374 for (; it != it_end; ++it)
375 if (it->inzipName() == inzip_name) {
385 void EmbeddedFiles::updateInsets() const
387 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
388 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
389 for (; it != it_end; ++it)
390 if (it->refCount() > 0)
391 it->updateInsets(buffer_);