X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FEmbeddedFiles.cpp;h=155ed86ac787c1b57f58d669b59c9bc71f0bf482;hb=3189e7b5dd438876016091463b8e66b134295fb5;hp=76d852a6b244afdd258592d37c1b3f517e8e6d31;hpb=cc6fe67c35bd807ee3fdf5945a550b247561be65;p=lyx.git diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index 76d852a6b2..155ed86ac7 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -16,10 +16,12 @@ #include "Buffer.h" #include "BufferParams.h" #include "Paragraph.h" -#include "ParIterator.h" +#include "InsetIterator.h" #include "debug.h" #include "gettext.h" #include "Format.h" +#include "Lexer.h" +#include "ErrorList.h" #include "frontends/alert.h" @@ -28,6 +30,11 @@ #include "support/filetools.h" #include "support/fs_extras.h" #include "support/convert.h" +#include "support/lyxlib.h" +#include "support/lstrings.h" + +#include "LyX.h" +#include "Session.h" #include #include @@ -59,14 +66,19 @@ using support::onlyFilename; using support::makeRelPath; using support::changeExtension; using support::bformat; -using support::zipFiles; +using support::prefixIs; +using support::sum; +using support::makedir; EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name, - STATUS status, ParConstIterator const & pit) - : DocFileName(file, true), inzip_name_(inzip_name), status_(status), - valid_(true), par_it_(pit) -{} + bool embed, Inset const * inset) + : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed), + valid_(true), inset_list_() +{ + if (inset != NULL) + inset_list_.push_back(inset); +} string EmbeddedFile::embeddedFile(Buffer const * buf) const @@ -75,9 +87,141 @@ string EmbeddedFile::embeddedFile(Buffer const * buf) const } -void EmbeddedFile::setParIter(ParConstIterator const & pit) +void EmbeddedFile::addInset(Inset const * inset) +{ + inset_list_.push_back(inset); +} + + +Inset const * EmbeddedFile::inset(int idx) const +{ + BOOST_ASSERT(idx < refCount()); + // some embedded file do not have a valid par iterator + return inset_list_[idx]; +} + + +void EmbeddedFile::saveBookmark(Buffer const * buf, int idx) const { - par_it_ = pit; + Inset const * ptr = inset(idx); + // This might not be the most efficient method ... + for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it) + if (&(*it) == ptr) { + // this is basically BufferView::saveBookmark(0) + LyX::ref().session().bookmarks().save( + FileName(buf->fileName()), + it.bottom().pit(), + it.bottom().pos(), + it.paragraph().id(), + it.pos(), + 0 + ); + } + // this inset can not be located. There is something wrong that needs + // to be fixed. + BOOST_ASSERT(true); +} + + +string EmbeddedFile::availableFile(Buffer const * buf) const +{ + if (embedded()) + return embeddedFile(buf); + else + return absFilename(); +} + + +void EmbeddedFile::invalidate() +{ + // Clear inset_list_ because they will be registered again. + inset_list_.clear(); + valid_ = false; +} + + +bool EmbeddedFile::extract(Buffer const * buf) const +{ + + string ext_file = absFilename(); + string emb_file = embeddedFile(buf); + + if (!fs::exists(emb_file)) + return false; + + // if external file already exists ... + if (fs::exists(ext_file)) { + // no need to copy if the files are the same + if (sum(*this) == sum(FileName(emb_file))) + return true; + // otherwise, ask if overwrite + int ret = Alert::prompt( + _("Overwrite external file?"), + 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 + // a successful operation. + return true; + } + // copy file + try { + // need to make directory? + string path = onlyPath(ext_file); + if (!fs::is_directory(path)) + makedir(const_cast(path.c_str()), 0755); + fs::copy_file(emb_file, ext_file, false); + return true; + } catch (fs::filesystem_error const & fe) { + Alert::error(_("Copy file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(emb_file), from_utf8(ext_file))); + LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; + } + return false; +} + + +bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const +{ + string ext_file = absFilename(); + string emb_file = embeddedFile(buf); + + if (!fs::exists(ext_file)) + return false; + + // if embedded file already exists ... + if (fs::exists(emb_file)) { + // no need to copy if the files are the same + if (sum(*this) == sum(FileName(emb_file))) + return true; + // other wise, ask if overwrite + int const ret = Alert::prompt( + _("Update embedded file?"), + bformat(_("Embeddedl 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 + // a successful operation. + return true; + } + // copy file + try { + // need to make directory? + string path = onlyPath(emb_file); + if (!fs::is_directory(path)) + makedir(const_cast(path.c_str()), 0755); + fs::copy_file(ext_file, emb_file, false); + return true; + } catch (fs::filesystem_error const & fe) { + Alert::error(_("Copy file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(ext_file), from_utf8(emb_file))); + LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; + } + return false; } @@ -87,15 +231,14 @@ bool EmbeddedFiles::enabled() const } -void EmbeddedFiles::enable(bool flag) +bool EmbeddedFiles::enable(bool flag) { - // FIXME: there are much more to do here. - // If we enable embedding, it is maybe a good idea to copy embedded files - // to temppath() - // if we disable embedding, embedded files need to be copied to their - // original positions. if (enabled() != flag) { - // file will be changed + // if enable, copy all files to temppath() + // if disable, extract all files + if ((flag && !updateFromExternalFile()) || (!flag && !extract())) + return false; + // if operation is successful buffer_->markDirty(); buffer_->params().embedded = flag; } @@ -103,39 +246,25 @@ void EmbeddedFiles::enable(bool flag) void EmbeddedFiles::registerFile(string const & filename, - EmbeddedFile::STATUS status, ParConstIterator const & pit) + bool embed, Inset const * inset, string const & inzipName) { + // filename can be relative or absolute, translate to absolute filename string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename(); // try to find this file from the list EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); for (; it != it_end; ++it) - if (it->absFilename() == abs_filename) + if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename) break; - // find this filename + // find this filename, keep the original embedding status if (it != file_list_.end()) { - it->setParIter(pit); - it->setStatus(status); + it->addInset(inset); it->validate(); return; } - // register a new one, using relative file path as inzip_name - string inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename), - from_utf8(buffer_->fileName()))); - // if inzip_name is an absolute path, use filename only to avoid - // leaking of filesystem information in inzip_name - if (absolutePath(inzip_name)) - inzip_name = onlyFilename(inzip_name); - // if this name has been used... - // use _1_name, _2_name etc - if (!validInzipName(inzip_name)) { - size_t i = 1; - string tmp = inzip_name; - do { - inzip_name = convert(i) + "_" + tmp; - } while (!validInzipName(inzip_name)); - } - file_list_.push_back(EmbeddedFile(abs_filename, inzip_name, status, pit)); + // try to be more careful + file_list_.push_back(EmbeddedFile(abs_filename, + getInzipName(abs_filename, inzipName), embed, inset)); } @@ -148,22 +277,12 @@ void EmbeddedFiles::update() EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); for (; it != it_end; ++it) - it->invalidate(); - - ParIterator pit = buffer_->par_iterator_begin(); - ParIterator pit_end = buffer_->par_iterator_end(); - for (; pit != pit_end; ++pit) { - // For each paragraph, traverse its insets and register embedded files - InsetList::const_iterator iit = pit->insetlist.begin(); - InsetList::const_iterator iit_end = pit->insetlist.end(); - for (; iit != iit_end; ++iit) { - Inset & inset = *iit->inset; - inset.registerEmbeddedFiles(*buffer_, *this, pit); - } - } - LYXERR(Debug::FILES) << "Manifest updated: " << endl - << *this - << "End Manifest" << endl; + // we do not update items that are manually inserted + if (it->refCount() > 0) + it->invalidate(); + + for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it) + it->registerEmbeddedFiles(*buffer_, *this); } @@ -171,42 +290,30 @@ bool EmbeddedFiles::write(DocFileName const & filename) { // file in the temporary path has the content string const content = FileName(addName(buffer_->temppath(), - onlyFilename(filename.toFilesystemEncoding()))).toFilesystemEncoding(); + "content.lyx")).toFilesystemEncoding(); - // get a file list and write a manifest file vector > filenames; - string const manifest = FileName( - addName(buffer_->temppath(), "manifest.txt")).toFilesystemEncoding(); - - // write a manifest file - ofstream os(manifest.c_str()); - os << *this; - os.close(); + // add content.lyx to filenames + filenames.push_back(make_pair(content, "content.lyx")); // prepare list of embedded file EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); for (; it != it_end; ++it) { if (it->valid() && it->embedded()) { - // use external file if possible - if (it->status() != EmbeddedFile::EMBEDDED && fs::exists(it->absFilename())) - filenames.push_back(make_pair(it->absFilename(), it->inzipName())); - // use embedded file (AUTO or EMBEDDED mode) - else if(fs::exists(it->embeddedFile(buffer_))) - filenames.push_back(make_pair(it->embeddedFile(buffer_), it->inzipName())); - else + string file = it->availableFile(buffer_); + if (file.empty()) lyxerr << "File " << it->absFilename() << " does not exist. Skip embedding it. " << endl; + else + filenames.push_back(make_pair(file, it->inzipName())); } } - // add filename (.lyx) and manifest to filenames - filenames.push_back(make_pair(content, onlyFilename(filename.toFilesystemEncoding()))); - filenames.push_back(make_pair(manifest, "manifest.txt")); // write a zip file with all these files. Write to a temp file first, to // avoid messing up the original file in case something goes terribly wrong. DocFileName zipfile(addName(buffer_->temppath(), onlyFilename(changeExtension( filename.toFilesystemEncoding(), ".zip")))); - zipFiles(zipfile, filenames); + ::zipFiles(zipfile.toFilesystemEncoding(), filenames); // copy file back try { fs::copy_file(zipfile.toFilesystemEncoding(), filename.toFilesystemEncoding(), false); @@ -221,114 +328,144 @@ bool EmbeddedFiles::write(DocFileName const & filename) } -string EmbeddedFiles::filename(size_t idx) const +EmbeddedFiles::EmbeddedFileList::const_iterator EmbeddedFiles::find(std::string filename) const { - return (file_list_.begin() + idx)->absFilename(); + EmbeddedFileList::const_iterator it = file_list_.begin(); + EmbeddedFileList::const_iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename) + return it; + return file_list_.end(); } -EmbeddedFile::STATUS EmbeddedFiles::status(size_t idx) const +bool EmbeddedFiles::extract() const { - return (file_list_.begin() + idx)->status(); + EmbeddedFileList::const_iterator it = file_list_.begin(); + EmbeddedFileList::const_iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + if (it->valid() && it->embedded()) + if(!it->extract(buffer_)) + return false; + return true; } -void EmbeddedFiles::setStatus(size_t idx, EmbeddedFile::STATUS status) +bool EmbeddedFiles::updateFromExternalFile() const { - if ((file_list_.begin() + idx)->status() != status) { - // file will be changed - buffer_->markDirty(); - (file_list_.begin() + idx)->setStatus(status); - } + EmbeddedFileList::const_iterator it = file_list_.begin(); + EmbeddedFileList::const_iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + if (it->valid() && it->embedded()) + if (!it->updateFromExternalFile(buffer_)) + return false; + return true; } -bool EmbeddedFiles::validInzipName(string const & name) +string const EmbeddedFiles::getInzipName(string const & abs_filename, string const & name) { - EmbeddedFileList::iterator it = file_list_.begin(); + // register a new one, using relative file path as inzip_name + string inzip_name = name; + if (name.empty()) + inzip_name = to_utf8(makeRelPath(from_utf8(abs_filename), + from_utf8(buffer_->filePath()))); + // if inzip_name is an absolute path, use filename only to avoid + // leaking of filesystem information in inzip_name + // The second case covers cases '../path/file' and '.' + if (absolutePath(inzip_name) || prefixIs(inzip_name, ".")) + inzip_name = onlyFilename(abs_filename); + // if this name has been used... + // use _1_name, _2_name etc + string tmp = inzip_name; + EmbeddedFileList::iterator it; EmbeddedFileList::iterator it_end = file_list_.end(); - for (; it != it_end; ++it) - if (it->inzipName() == name) - return false; - return true; + bool unique_name = false; + size_t i = 0; + while (!unique_name) { + unique_name = true; + if (i > 0) + inzip_name = convert(i) + "_" + tmp; + it = file_list_.begin(); + for (; it != it_end; ++it) + if (it->inzipName() == inzip_name) { + unique_name = false; + ++i; + break; + } + } + return inzip_name; } -istream & operator>> (istream & is, EmbeddedFiles & files) +bool EmbeddedFiles::readManifest(Lexer & lex, ErrorList & errorList) { - files.clear(); - string tmp; - getline(is, tmp); - // get version - istringstream itmp(tmp); - int version; - itmp.ignore(string("# LyX manifest version ").size()); - itmp >> version; - - if (version != 1) { - lyxerr << "This version of LyX can only read LyX manifest version 1" << endl; - return is; - } + int line = -1; + int begin_manifest_line = -1; - getline(is, tmp); - if (tmp != "") { - lyxerr << "Invalid manifest file, lacking " << endl; - return is; - } - // manifest file may be messed up, be carefully - while (is.good()) { - getline(is, tmp); - if (tmp != "") - break; + file_list_.clear(); + string filename = ""; + string inzipName = ""; + bool status = ""; - string fname; - getline(is, fname); - string inzip_name; - getline(is, inzip_name); - getline(is, tmp); - istringstream itmp(tmp); - int status; - itmp >> status; - - getline(is, tmp); - if (tmp != "") { - lyxerr << "Invalid manifest file, lacking " << endl; + while (lex.isOK()) { + lex.next(); + string const token = lex.getString(); + + if (token.empty()) + continue; + + if (token == "\\end_manifest") break; - } - files.registerFile(fname, static_cast(status)); - }; - // the last line must be - if (tmp != "") { - lyxerr << "Invalid manifest file, lacking " << endl; - return is; + ++line; + if (token == "\\begin_manifest") { + begin_manifest_line = line; + continue; + } + + LYXERR(Debug::PARSER) << "Handling document manifest token: `" + << token << '\'' << endl; + + if (token == "\\filename") + lex >> filename; + else if (token == "\\inzipName") + lex >> inzipName; + else if (token == "\\status") { + lex >> status; + registerFile(filename, status, NULL, inzipName); + filename = ""; + inzipName = ""; + } else { + docstring const s = _("\\begin_file is missing"); + errorList.push_back(ErrorItem(_("Manifest error"), + s, -1, 0, 0)); + } + } + if (begin_manifest_line) { + docstring const s = _("\\begin_manifest is missing"); + errorList.push_back(ErrorItem(_("Manifest error"), + s, -1, 0, 0)); } - return is; + return true; } -ostream & operator<< (ostream & os, EmbeddedFiles const & files) +void EmbeddedFiles::writeManifest(ostream & os) const { - // store a version so that operator >> can read later versions - // using version information. - os << "# lyx manifest version 1\n"; - os << "\n"; - EmbeddedFiles::EmbeddedFileList::const_iterator it = files.begin(); - EmbeddedFiles::EmbeddedFileList::const_iterator it_end = files.end(); + EmbeddedFiles::EmbeddedFileList::const_iterator it = begin(); + EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end(); for (; it != it_end; ++it) { if (!it->valid()) continue; - // use differnt lines to make reading easier. - os << "\n" - // save the relative path + // save the relative path + os << "\\filename " << to_utf8(makeRelPath(from_utf8(it->absFilename()), - from_utf8(files.buffer_->filePath()))) << '\n' - << it->inzipName() << '\n' - << it->status() << '\n' - << "\n"; + from_utf8(buffer_->filePath()))) << '\n' + << "\\inzipName " << it->inzipName() << '\n' + << "\\status " << (it->embedded() ? "true" : "false") << '\n'; } - os << "\n"; - return os; } + }