diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 3f079a69d9..d5c5aaa519 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -502,7 +502,8 @@ int Buffer::readHeader(Lexer & lex) LYXERR(Debug::PARSER, "Handling document header token: `" << token << '\''); - string unknown = params().readToken(lex, token, d->filename.onlyPath()); + string unknown = params().readToken(lex, token, d->filename.onlyPath(), + d->temppath); if (!unknown.empty()) { if (unknown[0] != '\\' && token == "\\textclass") { Alert::warning(_("Unknown document class"), @@ -577,7 +578,19 @@ bool Buffer::readDocument(Lexer & lex) // Enable embeded files, which will set temp path and move // inconsistent inzip files if needed. - embeddedFiles().enable(params().embedded, *this, false); + try { + embeddedFiles().validate(*this); + embeddedFiles().enable(params().embedded, *this, false); + } catch (ExceptionMessage const & message) { + Alert::error(message.title_, message.details_); + Alert::warning(_("Failed to read embedded files"), + _("Due to most likely a bug, LyX failed to locate all embedded " + "file. If you unzip the LyX file, you should be able to see and " + "open content.lyx which is your main text. You may also be able " + "to recover some embedded files. Please report this bug to the " + "lyx-devel mailing list.")); + return false; + } updateMacros(); updateMacroInstances(); diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 22da6812ee..84c066114d 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -469,7 +469,7 @@ void BufferParams::setDefSkip(VSpace const & vs) string const BufferParams::readToken(Lexer & lex, string const & token, - FileName const & filepath) + FileName const & filepath, FileName const & temppath) { if (token == "\\textclass") { lex.next(); @@ -478,8 +478,10 @@ string const BufferParams::readToken(Lexer & lex, string const & token, // NOTE: in this case, the textclass (.cls file) is assumed to be available. string tcp; LayoutFileList & bcl = LayoutFileList::get(); - if (!filepath.empty()) - tcp = bcl.addLayoutFile(classname, filepath.absFilename()); + if (!temppath.empty()) + tcp = bcl.addLayoutFile(classname, temppath.absFilename(), LayoutFileList::Embedded); + if (tcp.empty() && !filepath.empty()) + tcp = bcl.addLayoutFile(classname, filepath.absFilename(), LayoutFileList::Local); if (!tcp.empty()) setBaseClass(tcp); else if (bcl.haveClass(classname)) { diff --git a/src/BufferParams.h b/src/BufferParams.h index 39d515eec0..c442f98fd5 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -70,7 +70,8 @@ public: /// read a header token, if unrecognised, return it or an unknown class name std::string const readToken(Lexer & lex, std::string const & token, ///< token to read. - support::FileName const & filepath); ///< where to look for local layout file. + support::FileName const & filepath, + support::FileName const & temppath); ///< where to look for local layout file. /// void writeFile(std::ostream &) const; diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index 3eb5476cdc..8800edd9b2 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -129,8 +129,6 @@ void EmbeddedFile::enable(bool flag, Buffer const * buf, bool updateFile) if (!suffixIs(temp_path_, '/')) temp_path_ += '/'; if (embedded()) { - if (inzip_name_ != calcInzipName(buf->filePath())) - syncInzipFile(buf->filePath()); if (updateFile) updateFromExternalFile(); } @@ -299,8 +297,14 @@ an absolute filename can be saved as $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"; @@ -334,12 +338,18 @@ void EmbeddedFile::syncInzipFile(std::string const & buffer_path) 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); @@ -450,6 +460,46 @@ void EmbeddedFileList::registerFile(EmbeddedFile const & file, } +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 extra = buffer.params().extraEmbeddedFiles(); + vector::iterator e_it = extra.begin(); + vector::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())); + } +} + + void EmbeddedFileList::update(Buffer const & buffer) { clear(); @@ -464,7 +514,7 @@ void EmbeddedFileList::update(Buffer const & buffer) for (; it != it_end; ++it) { EmbeddedFile file = EmbeddedFile(*it, buffer.filePath()); file.setEmbed(true); - file.enable(buffer.embedded(), &buffer, true); + file.enable(buffer.embedded(), &buffer, false); insert(end(), file); } } diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h index 3981d1687e..e612fcc1fd 100644 --- a/src/EmbeddedFiles.h +++ b/src/EmbeddedFiles.h @@ -166,7 +166,6 @@ public: /// Calculate checksum of availableFile unsigned long checksum() const; -private: // calculate inzip_name_ from filename std::string calcInzipName(std::string const & buffer_path); // move an embedded disk file with an existing inzip_name_ to @@ -205,6 +204,9 @@ public: */ void registerFile(EmbeddedFile const & file, Inset const * inset, Buffer const & buffer); + /// validate embedded fies after a file is read. + void validate(Buffer const & buffer); + /// scan the buffer and get a list of EmbeddedFile void update(Buffer const & buffer); diff --git a/src/LayoutFile.cpp b/src/LayoutFile.cpp index e2d7653454..0680f33f02 100644 --- a/src/LayoutFile.cpp +++ b/src/LayoutFile.cpp @@ -186,10 +186,12 @@ void LayoutFileList::reset(LayoutFileIndex const & classname) { string const LayoutFileList::localPrefix = "LOCAL:"; +string const LayoutFileList::embeddedPrefix = "EMBED:"; LayoutFileIndex - LayoutFileList::addLayoutFile(string const & textclass, string const & path) + LayoutFileList::addLayoutFile(string const & textclass, string const & path, + Layout_Type type) { // FIXME There is a bug here: 4593 // @@ -197,7 +199,12 @@ LayoutFileIndex // NOTE: latex class name is defined in textclass.layout, which can be // different from textclass string fullName = addName(path, textclass + ".layout"); - string localIndex = localPrefix + fullName; + string localIndex; + + if (type == Local) + localIndex = localPrefix + fullName; + else if (type == Embedded) + localIndex = embeddedPrefix + textclass; // if the local file has already been loaded, return it if (haveClass(localIndex)) diff --git a/src/LayoutFile.h b/src/LayoutFile.h index ed38f0ccad..e23d214bd0 100644 --- a/src/LayoutFile.h +++ b/src/LayoutFile.h @@ -86,15 +86,24 @@ public: bool read(); /// Clears the textclass so as to force it to be reloaded void reset(LayoutFileIndex const & tc); + + enum Layout_Type { + System, + Local, + Embedded + }; + /// add a textclass from user local directory. /// \return the identifier for the loaded file, or else an /// empty string if no file was loaded. LayoutFileIndex - addLayoutFile(std::string const & textclass, std::string const & path); + addLayoutFile(std::string const & textclass, std::string const & path, + Layout_Type type); /// a list of the available classes std::vector classList() const; /// static std::string const localPrefix; + static std::string const embeddedPrefix; private: /// typedef std::map ClassMap; diff --git a/src/LyXFunc.cpp b/src/LyXFunc.cpp index 15a6bc2e03..7e2f25f5df 100644 --- a/src/LyXFunc.cpp +++ b/src/LyXFunc.cpp @@ -1579,7 +1579,8 @@ void LyXFunc::dispatch(FuncRequest const & cmd) BOOST_ASSERT(lyx_view_); Buffer * buffer = lyx_view_->buffer(); - if (!loadLayoutFile(argument, buffer->filePath())) + if (!loadLayoutFile(argument, buffer->temppath()) && + !loadLayoutFile(argument, buffer->filePath())) break; LayoutFile const * old_layout = buffer->params().baseClass(); @@ -1612,6 +1613,7 @@ void LyXFunc::dispatch(FuncRequest const & cmd) } case LFUN_TEXTCLASS_LOAD: + loadLayoutFile(argument, lyx_view_->buffer()->temppath()) || loadLayoutFile(argument, lyx_view_->buffer()->filePath()); break; diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index 944c65bfde..045d0b9cd1 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -1242,7 +1242,8 @@ void GuiDocument::browseLayout() string classname = layoutFile.onlyFileName(); LayoutFileIndex name = bcl.addLayoutFile( classname.substr(0, classname.size() - 7), - layoutFile.onlyPath().absFilename()); + layoutFile.onlyPath().absFilename(), + LayoutFileList::Local); if (name.empty()) { Alert::error(_("Error"), @@ -1284,6 +1285,16 @@ void GuiDocument::classChanged() setLayoutComboByIDString(bp_.baseClassID()); return; } + } else if (prefixIs(classname, LayoutFileList::embeddedPrefix)) { + int const ret = Alert::prompt(_("Embedded layout"), + _("The layout file you have selected is an embedded layout that\n" + "is embedded to a buffer. You cannot make use of it unless\n" + "it is already embedded to this buffer.\n"), + 1, 1, _("&Set Layout"), _("&Cancel")); + if (ret == 1) { + setLayoutComboByIDString(bp_.baseClassID()); + return; + } } if (!bp_.setBaseClass(classname)) { Alert::error(_("Error"), _("Unable to set document class."));