]> git.lyx.org Git - lyx.git/blobdiff - src/Buffer.cpp
Fix yet another thinko in the math grid paste code
[lyx.git] / src / Buffer.cpp
index 4be7e2119701c6a456ee574300d5aa3ffb18c39d..03368dc8762e2b6400482dd5da0a70da5da54c33 100644 (file)
@@ -57,7 +57,7 @@
 #include "PDFOptions.h"
 #include "Session.h"
 #include "SpellChecker.h"
-#include "sgml.h"
+#include "xml.h"
 #include "texstream.h"
 #include "TexRow.h"
 #include "Text.h"
 #include "support/textutils.h"
 #include "support/types.h"
 
-#include "support/bind.h"
-
 #include <algorithm>
 #include <fstream>
 #include <iomanip>
@@ -297,6 +295,8 @@ public:
        ///
        CloneList_ptr clone_list_;
 
+       ///
+       std::list<Buffer const *> include_list_;
 private:
        /// So we can force access via the accessors.
        mutable Buffer const * parent_buffer;
@@ -1093,6 +1093,10 @@ bool Buffer::readDocument(Lexer & lex)
                d->old_position = params().origin;
        else
                d->old_position = filePath();
+
+       if (!parent())
+               clearIncludeList();
+
        bool const res = text().read(lex, errorList, d->inset);
        d->old_position.clear();
 
@@ -2191,13 +2195,13 @@ Buffer::ExportStatus Buffer::writeDocBookSource(odocstream & os, string const &
 
                params().documentClass().counters().reset();
 
-               sgml::openTag(os, top);
+               xml::openTag(os, top);
                os << '\n';
                try {
                        docbookParagraphs(text(), *this, os, runparams);
                }
                catch (ConversionException const &) { return ExportKilled; }
-               sgml::closeTag(os, top_element);
+               xml::closeTag(os, top_element);
        }
        return ExportSuccess;
 }
@@ -2255,7 +2259,7 @@ Buffer::ExportStatus Buffer::writeLyXHTMLSource(odocstream & os,
                os << "<title>"
                   << (doctitle.empty() ?
                         from_ascii("LyX Document") :
-                        html::htmlize(doctitle, XHTMLStream::ESCAPE_ALL))
+                        xml::xmlize(doctitle, XMLStream::ESCAPE_ALL))
                   << "</title>\n";
 
                docstring styles = features.getTClassHTMLPreamble();
@@ -2321,7 +2325,7 @@ Buffer::ExportStatus Buffer::writeLyXHTMLSource(odocstream & os,
                bool const output_body_tag = (output != IncludedFile);
                if (output_body_tag)
                        os << "<body dir=\"auto\">\n";
-               XHTMLStream xs(os);
+               XMLStream xs(os);
                if (output != IncludedFile)
                        // if we're an included file, the counters are in the master.
                        params().documentClass().counters().reset();
@@ -2398,6 +2402,9 @@ void Buffer::validate(LaTeXFeatures & features) const
        if (!features.runparams().is_child)
                params().validate(features);
 
+       if (!parent())
+               clearIncludeList();
+
        for (Paragraph const & p : paragraphs())
                p.validate(features);
 
@@ -2610,6 +2617,9 @@ void Buffer::reloadBibInfoCache(bool const force) const
 
 void Buffer::collectBibKeys(FileNameList & checkedFiles) const
 {
+       if (!parent())
+               clearIncludeList();
+
        for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
                it->collectBibKeys(it, checkedFiles);
                if (it->lyxCode() == BIBITEM_CODE) {
@@ -3005,7 +3015,7 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
                                // get buffer of external file
                                InsetInclude const & ins =
                                        static_cast<InsetInclude const &>(*it);
-                               Buffer * child = ins.getChildBuffer();
+                               Buffer * child = ins.loadIfNeeded();
                                if (!child)
                                        continue;
                                child->dispatch(func, dr);
@@ -3083,9 +3093,10 @@ void Buffer::changeLanguage(Language const * from, Language const * to)
        LASSERT(from, return);
        LASSERT(to, return);
 
-       for_each(par_iterator_begin(),
-                par_iterator_end(),
-                bind(&Paragraph::changeLanguage, _1, params(), from, to));
+       ParIterator it = par_iterator_begin();
+       ParIterator eit = par_iterator_end();
+       for (; it != eit; ++it)
+               it->changeLanguage(params(), from, to);
 }
 
 
@@ -3472,8 +3483,37 @@ bool Buffer::isReadonly() const
 
 void Buffer::setParent(Buffer const * buffer)
 {
-       // Avoids recursive include.
-       d->setParent(buffer == this ? nullptr : buffer);
+       // We need to do some work here to avoid recursive parent structures.
+       // This is the easy case.
+       if (buffer == this) {
+               LYXERR0("Ignoring attempt to set self as parent in\n" << fileName());
+               return;
+       }
+       // Now we check parents going upward, to make sure that IF we set the
+       // parent as requested, we would not generate a recursive include.
+       set<Buffer const *> sb;
+       Buffer const * b = buffer;
+       bool found_recursion = false;
+       while (b) {
+               if (sb.find(b) != sb.end()) {
+                       found_recursion = true;
+                       break;
+               }
+               sb.insert(b);
+               b = b->parent();
+       }
+
+       if (found_recursion) {
+               LYXERR0("Ignoring attempt to set parent of\n" <<
+                       fileName() <<
+                       "\nto " <<
+                       buffer->fileName() <<
+                       "\nbecause that would create a recursive inclusion.");
+               return;
+       }
+
+       // We should be safe now.
+       d->setParent(buffer);
        updateMacros();
 }
 
@@ -3494,8 +3534,6 @@ ListOfBuffers Buffer::allRelatives() const
 
 Buffer const * Buffer::masterBuffer() const
 {
-       // FIXME Should be make sure we are not in some kind
-       // of recursive include? A -> B -> A will crash this.
        Buffer const * const pbuf = d->parent();
        if (!pbuf)
                return this;
@@ -3766,7 +3804,7 @@ void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope)
                                InsetInclude const & incinset =
                                        static_cast<InsetInclude const &>(*insit.inset);
                                macro_lock = true;
-                               Buffer * child = incinset.getChildBuffer();
+                               Buffer * child = incinset.loadIfNeeded();
                                macro_lock = false;
                                if (!child)
                                        continue;
@@ -3861,7 +3899,7 @@ void Buffer::getUsedBranches(std::list<docstring> & result, bool const from_mast
                        // get buffer of external file
                        InsetInclude const & ins =
                                static_cast<InsetInclude const &>(*it);
-                       Buffer * child = ins.getChildBuffer();
+                       Buffer * child = ins.loadIfNeeded();
                        if (!child)
                                continue;
                        child->getUsedBranches(result, true);
@@ -4085,7 +4123,7 @@ unique_ptr<TexRow> Buffer::getSourceCode(odocstream & os, string const & format,
                        par.write(ods, params(), dt);
                        os << from_utf8(ods.str());
                } else if (runparams.flavor == OutputParams::HTML) {
-                       XHTMLStream xs(os);
+                       XMLStream xs(os);
                        setMathFlavor(runparams);
                        xhtmlParagraphs(text(), *this, xs, runparams);
                } else if (runparams.flavor == OutputParams::TEXT) {
@@ -4948,6 +4986,8 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
 
        // do the real work
        ParIterator parit = cbuf.par_iterator_begin();
+       if (scope == UpdateMaster)
+               clearIncludeList();
        updateBuffer(parit, utype);
 
        // If this document has siblings, then update the TocBackend later. The
@@ -4990,6 +5030,7 @@ void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
        }
        d->cite_labels_valid_ = true;
        /// FIXME: Perf
+       clearIncludeList();
        cbuf.tocBackend().update(true, utype);
        if (scope == UpdateMaster)
                cbuf.structureChanged();
@@ -5220,6 +5261,7 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const
 
 void Buffer::updateBuffer(ParIterator & parit, UpdateType utype, bool const deleted) const
 {
+       pushIncludedBuffer(this);
        // LASSERT: Is it safe to continue here, or should we just return?
        LASSERT(parit.pit() == 0, /**/);
 
@@ -5275,6 +5317,7 @@ void Buffer::updateBuffer(ParIterator & parit, UpdateType utype, bool const dele
        // set change indicator for the inset (or the cell that the iterator
        // points to, if applicable).
        parit.text()->inset().isChanged(changed);
+       popIncludedBuffer();
 }
 
 
@@ -5591,4 +5634,55 @@ void Buffer::clearExternalModification() const
 }
 
 
+void Buffer::pushIncludedBuffer(Buffer const * buf) const
+{
+       masterBuffer()->d->include_list_.push_back(buf);
+       if (lyxerr.debugging(Debug::FILES)) {
+               LYXERR0("Pushed. Stack now:");
+               if (masterBuffer()->d->include_list_.empty())
+                       LYXERR0("EMPTY!");
+               else
+                       for (auto const & b : masterBuffer()->d->include_list_)
+                               LYXERR0(b->fileName());
+       }
+}
+
+
+void Buffer::popIncludedBuffer() const
+{
+       masterBuffer()->d->include_list_.pop_back();
+       if (lyxerr.debugging(Debug::FILES)) {
+               LYXERR0("Popped. Stack now:");
+               if (masterBuffer()->d->include_list_.empty())
+                       LYXERR0("EMPTY!");
+               else
+                       for (auto const & b : masterBuffer()->d->include_list_)
+                               LYXERR0(b->fileName());
+       }
+}
+
+
+bool Buffer::isBufferIncluded(Buffer const * buf) const
+{
+       if (!buf)
+               return false;
+       if (lyxerr.debugging(Debug::FILES)) {
+               LYXERR0("Checking for " << buf->fileName() << ". Stack now:");
+               if (masterBuffer()->d->include_list_.empty())
+                       LYXERR0("EMPTY!");
+               else
+                       for (auto const & b : masterBuffer()->d->include_list_)
+                               LYXERR0(b->fileName());
+       }
+       list<Buffer const *> const & blist = masterBuffer()->d->include_list_;
+       return find(blist.begin(), blist.end(), buf) != blist.end();
+}
+
+
+void Buffer::clearIncludeList() const
+{
+       LYXERR(Debug::FILES, "Clearing include list for " << fileName());
+       d->include_list_.clear();
+}
+
 } // namespace lyx