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"
45 namespace Alert = frontend::Alert;
47 using support::FileName;
48 using support::DocFileName;
49 using support::makeAbsPath;
50 using support::addName;
51 using support::absolutePath;
52 using support::onlyFilename;
53 using support::makeRelPath;
54 using support::changeExtension;
55 using support::bformat;
56 using support::prefixIs;
57 using support::makedir;
60 EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name,
61 bool embed, Inset const * inset)
62 : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed),
66 inset_list_.push_back(inset);
70 string EmbeddedFile::embeddedFile(Buffer const * buf) const
72 return addName(buf->temppath(), inzip_name_);
76 void EmbeddedFile::addInset(Inset const * inset)
78 inset_list_.push_back(inset);
82 Inset const * EmbeddedFile::inset(int idx) const
84 BOOST_ASSERT(idx < refCount());
85 // some embedded file do not have a valid par iterator
86 return inset_list_[idx];
90 void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const
92 Inset const * ptr = inset(idx);
93 // This might not be the most efficient method ...
94 for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
96 // this is basically BufferView::saveBookmark(0)
97 LyX::ref().session().bookmarks().save(
98 FileName(buf->absFileName()),
106 // this inset can not be located. There is something wrong that needs
112 string EmbeddedFile::availableFile(Buffer const * buf) const
114 return embedded() ? embeddedFile(buf) : absFilename();
118 bool EmbeddedFile::extract(Buffer const * buf) const
120 string ext_file = absFilename();
121 string emb_file = embeddedFile(buf);
123 FileName emb(emb_file);
124 FileName ext(ext_file);
129 // if external file already exists ...
131 // no need to copy if the files are the same
132 if (checksum() == FileName(emb_file).checksum())
134 // otherwise, ask if overwrite
135 int ret = Alert::prompt(
136 _("Overwrite external file?"),
137 bformat(_("External file %1$s already exists, do you want to overwrite it"),
138 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
140 // if the user does not want to overwrite, we still consider it
141 // a successful operation.
146 // need to make directory?
147 FileName path = ext.onlyPath();
148 if (!path.isDirectory())
149 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
152 Alert::error(_("Copy file failure"),
153 bformat(_("Cannot copy file %1$s to %2$s.\n"
154 "Please check whether the directory exists and is writeable."),
155 from_utf8(emb_file), from_utf8(ext_file)));
156 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
161 bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const
163 string ext_file = absFilename();
164 string emb_file = embeddedFile(buf);
166 FileName emb(emb_file);
167 FileName ext(ext_file);
172 // if embedded file already exists ...
174 // no need to copy if the files are the same
175 if (checksum() == FileName(emb_file).checksum())
177 // other wise, ask if overwrite
178 int const ret = Alert::prompt(
179 _("Update embedded file?"),
180 bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
181 from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
183 // if the user does not want to overwrite, we still consider it
184 // a successful operation.
188 // need to make directory?
189 FileName path = emb.onlyPath();
190 if (!path.isDirectory())
191 makedir(const_cast<char*>(path.absFilename().c_str()), 0755);
194 Alert::error(_("Copy file failure"),
195 bformat(_("Cannot copy file %1$s to %2$s.\n"
196 "Please check whether the directory exists and is writeable."),
197 from_utf8(ext_file), from_utf8(emb_file)));
198 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
203 void EmbeddedFile::updateInsets(Buffer const * buf) const
205 vector<Inset const *>::const_iterator it = inset_list_.begin();
206 vector<Inset const *>::const_iterator it_end = inset_list_.end();
207 for (; it != it_end; ++it)
208 const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
212 bool EmbeddedFiles::enabled() const
214 return buffer_->params().embedded;
218 void EmbeddedFiles::enable(bool flag)
220 if (enabled() != flag) {
221 // if enable, copy all files to temppath()
222 // if disable, extract all files
223 if ((flag && !updateFromExternalFile()) || (!flag && !extract()))
225 // if operation is successful
226 buffer_->markDirty();
227 buffer_->params().embedded = flag;
234 EmbeddedFile & EmbeddedFiles::registerFile(string const & filename,
235 bool embed, Inset const * inset, string const & inzipName)
237 // filename can be relative or absolute, translate to absolute filename
238 string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename();
239 // try to find this file from the list
240 EmbeddedFileList::iterator it = file_list_.begin();
241 EmbeddedFileList::iterator it_end = file_list_.end();
242 for (; it != it_end; ++it)
243 if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename)
245 // find this filename, keep the original embedding status
246 if (it != file_list_.end()) {
251 file_list_.push_back(EmbeddedFile(abs_filename,
252 getInzipName(abs_filename, inzipName), embed, inset));
253 return file_list_.back();
257 void EmbeddedFiles::update()
261 for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it)
262 it->registerEmbeddedFiles(*buffer_, *this);
266 bool EmbeddedFiles::writeFile(DocFileName const & filename)
268 // file in the temporary path has the content
269 string const content = FileName(addName(buffer_->temppath(),
270 "content.lyx")).toFilesystemEncoding();
272 vector<pair<string, string> > filenames;
273 // add content.lyx to filenames
274 filenames.push_back(make_pair(content, "content.lyx"));
275 // prepare list of embedded file
276 EmbeddedFileList::iterator it = file_list_.begin();
277 EmbeddedFileList::iterator it_end = file_list_.end();
278 for (; it != it_end; ++it) {
279 if (it->embedded()) {
280 string file = it->availableFile(buffer_);
282 lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl;
284 filenames.push_back(make_pair(file, it->inzipName()));
287 // write a zip file with all these files. Write to a temp file first, to
288 // avoid messing up the original file in case something goes terribly wrong.
289 DocFileName zipfile(addName(buffer_->temppath(),
290 onlyFilename(changeExtension(
291 filename.toFilesystemEncoding(), ".zip"))));
293 ::zipFiles(zipfile.toFilesystemEncoding(), filenames);
295 if (!zipfile.copyTo(filename)) {
296 Alert::error(_("Save failure"),
297 bformat(_("Cannot create file %1$s.\n"
298 "Please check whether the directory exists and is writeable."),
299 from_utf8(filename.absFilename())));
300 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
306 EmbeddedFiles::EmbeddedFileList::const_iterator
307 EmbeddedFiles::find(std::string filename) const
309 EmbeddedFileList::const_iterator it = file_list_.begin();
310 EmbeddedFileList::const_iterator it_end = file_list_.end();
311 for (; it != it_end; ++it)
312 if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename)
314 return file_list_.end();
318 bool EmbeddedFiles::extract() const
320 EmbeddedFileList::const_iterator it = file_list_.begin();
321 EmbeddedFileList::const_iterator it_end = file_list_.end();
322 for (; it != it_end; ++it)
324 if(!it->extract(buffer_))
330 bool EmbeddedFiles::updateFromExternalFile() const
332 EmbeddedFileList::const_iterator it = file_list_.begin();
333 EmbeddedFileList::const_iterator it_end = file_list_.end();
334 for (; it != it_end; ++it)
336 if (!it->updateFromExternalFile(buffer_))
342 string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name)
344 // register a new one, using relative file path as inzip_name
345 string inzip_name = name;
347 inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename),
348 from_utf8(buffer_->filePath())));
349 // if inzip_name is an absolute path, use filename only to avoid
350 // leaking of filesystem information in inzip_name
351 // The second case covers cases '../path/file' and '.'
352 if (absolutePath(inzip_name) || prefixIs(inzip_name, "."))
353 inzip_name = onlyFilename(abs_filename);
354 // if this name has been used...
355 // use _1_name, _2_name etc
356 string tmp = inzip_name;
357 EmbeddedFileList::iterator it;
358 EmbeddedFileList::iterator it_end = file_list_.end();
359 bool unique_name = false;
361 while (!unique_name) {
364 inzip_name = convert<string>(i) + "_" + tmp;
365 it = file_list_.begin();
366 for (; it != it_end; ++it)
367 if (it->inzipName() == inzip_name) {
377 void EmbeddedFiles::updateInsets() const
379 EmbeddedFiles::EmbeddedFileList::const_iterator it = begin();
380 EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end();
381 for (; it != it_end; ++it)
382 if (it->refCount() > 0)
383 it->updateInsets(buffer_);