#include "support/ExceptionMessage.h"
#include "support/FileZipListDir.h"
+#include "support/assert.h"
+
#include <sstream>
#include <fstream>
#include <utility>
namespace Alert = frontend::Alert;
EmbeddedFile::EmbeddedFile(string const & file, std::string const & buffer_path)
- : DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_(),
- temp_path_("")
+ : DocFileName("", false), embedded_(false), inset_list_()
{
set(file, buffer_path);
}
if (filename.empty())
return;
- inzip_name_ = calcInzipName(buffer_path);
+ if (!buffer_path.empty())
+ inzip_name_ = calcInzipName(buffer_path);
}
return;
// an enabled EmbeededFile should have this problem handled
- BOOST_ASSERT(!enabled());
+ LASSERT(!isEnabled(), /**/);
// file will be synced when it is enabled
inzip_name_ = name;
}
string EmbeddedFile::embeddedFile() const
{
- BOOST_ASSERT(enabled());
+ LASSERT(isEnabled(), /**/);
return temp_path_ + inzip_name_;
}
FileName EmbeddedFile::availableFile() const
{
- if (enabled() && embedded())
+ if (isEnabled() && embedded())
return FileName(embeddedFile());
- else
- return *this;
+ return *this;
}
string EmbeddedFile::latexFilename(std::string const & buffer_path) const
{
- return (enabled() && embedded()) ? inzip_name_ : relFilename(buffer_path);
+ return (isEnabled() && embedded()) ? inzip_name_ : relFilename(buffer_path);
}
void EmbeddedFile::addInset(Inset const * inset)
{
- if (inset != NULL)
+ if (inset)
inset_list_.push_back(inset);
}
}
-void EmbeddedFile::enable(bool flag, Buffer const * buf)
+void EmbeddedFile::enable(bool enabled, Buffer const & buf, bool updateFile)
{
- if (enabled() == flag)
- return;
-
- if (flag) {
- temp_path_ = buf->temppath();
+ // This function will be called when
+ // 1. through EmbeddedFiles::enable() when a file is read. Files
+ // should be in place so no updateFromExternalFile or extract()
+ // should be called. (updateFile should be false in this case).
+ // 2. through menu item enable/disable. updateFile should be true.
+ // 3. A single embedded file is added or modified. updateFile
+ // can be true or false.
+ LYXERR(Debug::FILES, (enabled ? "Enable" : "Disable")
+ << " " << absFilename()
+ << (updateFile ? " (update file)." : " (no update)."));
+
+ if (enabled) {
+ temp_path_ = buf.temppath();
if (!suffixIs(temp_path_, '/'))
temp_path_ += '/';
- if (embedded()) {
- if (inzip_name_ != calcInzipName(buf->filePath()))
- syncInzipFile(buf->filePath());
+ if (embedded() && updateFile)
updateFromExternalFile();
- } else
- extract();
} else {
- extract();
+ // when a new embeddeed file is created, it is not enabled, and
+ // there is no need to extract.
+ if (isEnabled() && embedded() && updateFile)
+ extract();
temp_path_ = "";
}
}
bool EmbeddedFile::extract() const
{
- BOOST_ASSERT(enabled());
+ LASSERT(isEnabled(), /**/);
string ext_file = absFilename();
string emb_file = embeddedFile();
if (!emb.exists()) {
if (ext.exists())
return true;
- else
- throw ExceptionMessage(ErrorException, _("Failed to extract file"),
- bformat(_("Cannot extract file '%1$s'.\n"
- "Source file %2$s does not exist"),
- from_utf8(outputFilename()), from_utf8(emb_file)));
+ throw ExceptionMessage(ErrorException, _("Failed to extract file"),
+ bformat(_("Cannot extract file '%1$s'.\n"
+ "Source file %2$s does not exist"),
+ from_utf8(outputFilename()), from_utf8(emb_file)));
}
// if external file already exists ...
// otherwise, ask if overwrite
int ret = Alert::prompt(
_("Overwrite external file?"),
- bformat(_("External file %1$s already exists, do you want to overwrite it"),
+ bformat(_("External file %1$s already exists, do you want to overwrite it?"),
from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel"));
if (ret != 0)
// if the user does not want to overwrite, we still consider it
bool EmbeddedFile::updateFromExternalFile() const
{
- BOOST_ASSERT(enabled());
+ LASSERT(isEnabled(), /**/);
string ext_file = absFilename();
string emb_file = embeddedFile();
}
-void EmbeddedFile::updateInsets(Buffer const * buf) const
+EmbeddedFile EmbeddedFile::copyTo(Buffer const & buf)
+{
+ EmbeddedFile file = EmbeddedFile(absFilename(), buf.filePath());
+ file.setEmbed(embedded());
+ file.enable(buf.embedded(), buf, false);
+
+ // use external file.
+ if (!embedded())
+ return file;
+
+ LYXERR(Debug::FILES, "Copy " << availableFile()
+ << " to " << file.availableFile());
+
+ FileName from_file = availableFile();
+ FileName to_file = file.availableFile();
+
+ if (!from_file.exists()) {
+ // no from file
+ throw ExceptionMessage(ErrorException,
+ _("Failed to copy embedded file"),
+ bformat(_("Failed to embed file %1$s.\n"
+ "Please check whether the source file is available"),
+ from_utf8(absFilename())));
+ file.setEmbed(false);
+ return file;
+ }
+
+ // if destination file already exists ...
+ if (to_file.exists()) {
+ // no need to copy if the files are the same
+ if (checksum() == to_file.checksum())
+ return file;
+ // other wise, ask if overwrite
+ int const ret = Alert::prompt(
+ _("Update embedded file?"),
+ bformat(_("Embedded file %1$s already exists, do you want to overwrite it"),
+ from_utf8(to_file.absFilename())), 1, 1, _("&Overwrite"), _("&Cancel"));
+ if (ret != 0)
+ // if the user does not want to overwrite, we still consider it
+ // a successful operation.
+ return file;
+ }
+ // copy file
+ // need to make directory?
+ FileName path = to_file.onlyPath();
+ if (!path.isDirectory())
+ path.createPath();
+ if (from_file.copyTo(to_file))
+ return file;
+ throw ExceptionMessage(ErrorException,
+ _("Copy file failure"),
+ bformat(_("Cannot copy file %1$s to %2$s.\n"
+ "Please check whether the directory exists and is writeable."),
+ from_utf8(from_file.absFilename()), from_utf8(to_file.absFilename())));
+ return file;
+}
+
+
+void EmbeddedFile::updateInsets() const
{
vector<Inset const *>::const_iterator it = inset_list_.begin();
vector<Inset const *>::const_iterator it_end = inset_list_.end();
for (; it != it_end; ++it)
- const_cast<Inset *>(*it)->updateEmbeddedFile(*buf, *this);
+ const_cast<Inset *>(*it)->updateEmbeddedFile(*this);
}
$temp/$embDirName/$absDirName/a/absolute/path for /a/absolute/path
+FIXME:
+embDirName is set to . so that embedded layout and class files can be
+used directly. However, putting all embedded files directly under
+the temp directory may lead to file conflicts. For example, if a user
+embeds a file blah.log in blah.lyx, it will be replaced when
+'latex blah.tex' is called.
*/
-const std::string embDirName = "LyX.Embedded.Files";
+const std::string embDirName = ".";
const std::string upDirName = "LyX.Embed.Dir.Up";
const std::string absDirName = "LyX.Embed.Dir.Abs";
const std::string driveName = "LyX.Embed.Drive";
void EmbeddedFile::syncInzipFile(std::string const & buffer_path)
{
- BOOST_ASSERT(enabled());
+ LASSERT(isEnabled(), /**/);
string old_emb_file = temp_path_ + '/' + inzip_name_;
FileName old_emb(old_emb_file);
+ if (!old_emb.exists())
+ throw ExceptionMessage(ErrorException, _("Failed to open file"),
+ bformat(_("Embedded file %1$s does not exist. Did you tamper lyx temporary directory?"),
+ old_emb.displayName()));
+
+ string new_inzip_name = calcInzipName(buffer_path);
+ if (new_inzip_name == inzip_name_)
+ return;
+
LYXERR(Debug::FILES, " OLD ZIP " << old_emb_file <<
" NEW ZIP " << calcInzipName(buffer_path));
- //BOOST_ASSERT(old_emb.exists());
-
- string new_inzip_name = calcInzipName(buffer_path);
string new_emb_file = temp_path_ + '/' + new_inzip_name;
FileName new_emb(new_emb_file);
}
-void EmbeddedFileList::enable(bool flag, Buffer & buffer)
+void EmbeddedFileList::enable(bool enabled, Buffer & buffer, bool updateFile)
{
- if (buffer.embedded() == flag)
- return;
-
// update embedded file list
update(buffer);
int count_embedded = 0;
int count_external = 0;
- std::vector<EmbeddedFile>::iterator it = begin();
- std::vector<EmbeddedFile>::iterator it_end = end();
+ iterator it = begin();
+ iterator it_end = end();
// an exception may be thrown
for (; it != it_end; ++it) {
- it->enable(flag, &buffer);
+ it->enable(enabled, buffer, updateFile);
if (it->embedded())
- count_embedded ++;
+ ++count_embedded;
else
- count_external ++;
+ ++count_external;
}
// if operation is successful (no exception is thrown)
- buffer.markDirty();
- buffer.params().embedded = flag;
+ buffer.params().embedded = enabled;
// if the operation is successful, update insets
for (it = begin(); it != it_end; ++it)
- it->updateInsets(&buffer);
-
+ it->updateInsets();
+
+ if (!updateFile || (count_external == 0 && count_embedded == 0))
+ return;
+
// show result
- if (flag) {
+ if (enabled) {
docstring const msg = bformat(_("%1$d external files are ignored.\n"
"%2$d embeddable files are embedded.\n"), count_external, count_embedded);
Alert::information(_("Packing all files"), msg);
void EmbeddedFileList::registerFile(EmbeddedFile const & file,
Inset const * inset, Buffer const & buffer)
{
- BOOST_ASSERT(!buffer.embedded() || file.availableFile().exists());
- BOOST_ASSERT(!buffer.embedded() || file.enabled());
+ LASSERT(!buffer.embedded() || file.isEnabled(), /**/);
+
+ string newfile = file.absFilename();
+ iterator efp = findFile(newfile);
+ if (efp != end()) {
+ if (efp->embedded() != file.embedded()) {
+ Alert::error(_("Wrong embedding status."),
+ bformat(_("File %1$s is included in more than one insets, "
+ "but with different embedding status. Assuming embedding status."),
+ from_utf8(efp->outputFilename())));
+ efp->setEmbed(true);
+ // update the inset with this embedding status.
+ const_cast<Inset*>(inset)->updateEmbeddedFile(*efp);
+ }
+ efp->addInset(inset);
+ return;
+ }
+ file.clearInsets();
+ push_back(file);
+ back().addInset(inset);
+}
+
+EmbeddedFileList::const_iterator
+ EmbeddedFileList::findFile(std::string const & filename) const
+{
+ // try to find this file from the list
+ std::vector<EmbeddedFile>::const_iterator it = begin();
+ std::vector<EmbeddedFile>::const_iterator it_end = end();
+ for (; it != it_end; ++it)
+ if (it->absFilename() == filename)
+ return it;
+ return end();
+}
+
+
+EmbeddedFileList::iterator
+ EmbeddedFileList::findFile(std::string const & filename)
+{
// try to find this file from the list
std::vector<EmbeddedFile>::iterator it = begin();
std::vector<EmbeddedFile>::iterator it_end = end();
for (; it != it_end; ++it)
- if (it->absFilename() == file.absFilename()) {
- if (it->embedded() != file.embedded()) {
- Alert::error(_("Wrong embedding status."),
- bformat(_("File %1$s is included in more than one insets, "
- "but with different embedding status. Assuming embedding status."),
- from_utf8(it->outputFilename())));
- it->setEmbed(true);
- // update the inset with this embedding status.
- const_cast<Inset*>(inset)->updateEmbeddedFile(buffer, *it);
- }
- it->addInset(inset);
- return;
- }
- //
- push_back(file);
- back().addInset(inset);
+ if (it->absFilename() == filename)
+ return it;
+ return end();
+}
+
+
+void EmbeddedFileList::validate(Buffer const & buffer)
+{
+ clear();
+
+ for (InsetIterator it = inset_iterator_begin(buffer.inset()); it; ++it)
+ it->registerEmbeddedFiles(*this);
+
+ iterator it = begin();
+ iterator it_end = end();
+ for (; it != it_end; ++it) {
+ if (buffer.embedded() && it->embedded())
+ // An exception will be raised if inzip file does not exist
+ it->syncInzipFile(buffer.filePath());
+ else
+ // inzipName may be OS dependent
+ it->setInzipName(it->calcInzipName(buffer.filePath()));
+ }
+ for (it = begin(); it != it_end; ++it)
+ it->updateInsets();
+
+ if (!buffer.embedded())
+ return;
+
+ // check if extra embedded files exist
+ vector<string> extra = buffer.params().extraEmbeddedFiles();
+ vector<string>::iterator e_it = extra.begin();
+ vector<string>::iterator e_end = extra.end();
+ for (; e_it != e_end; ++e_it) {
+ EmbeddedFile file = EmbeddedFile(*e_it, buffer.filePath());
+ // do not update from external file
+ file.enable(true, buffer, false);
+ // but we do need to check file existence.
+ if (!FileName(file.embeddedFile()).exists())
+ throw ExceptionMessage(ErrorException, _("Failed to open file"),
+ bformat(_("Embedded file %1$s does not exist. Did you tamper lyx temporary directory?"),
+ file.displayName()));
+ }
}
clear();
for (InsetIterator it = inset_iterator_begin(buffer.inset()); it; ++it)
- it->registerEmbeddedFiles(buffer, *this);
+ it->registerEmbeddedFiles(*this);
+
+ // add extra embedded files
+ vector<string> extra = buffer.params().extraEmbeddedFiles();
+ vector<string>::iterator it = extra.begin();
+ vector<string>::iterator it_end = extra.end();
+ for (; it != it_end; ++it) {
+ EmbeddedFile file = EmbeddedFile(*it, buffer.filePath());
+ file.setEmbed(true);
+ file.enable(buffer.embedded(), buffer, false);
+ insert(end(), file);
+ }
}
filenames.push_back(make_pair(content, "content.lyx"));
// prepare list of embedded file
update(buffer);
- std::vector<EmbeddedFile>::iterator it = begin();
- std::vector<EmbeddedFile>::iterator it_end = end();
+ //
+ iterator it = begin();
+ iterator it_end = end();
for (; it != it_end; ++it) {
if (it->embedded()) {
string file = it->embeddedFile();
// copy file back
if (!zipfile.copyTo(filename)) {
Alert::error(_("Save failure"),
- bformat(_("Cannot create file %1$s.\n"
+ bformat(_("Cannot create file %1$s.\n"
"Please check whether the directory exists and is writeable."),
from_utf8(filename.absFilename())));
- //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
}
return true;
}
-}
+} // namespace lyx