#include "support/convert.h"
#include "support/docstream.h"
+#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/textutils.h"
#include <map>
#include <functional>
#include <QThreadStorage>
-#include <support/lassert.h>
using namespace std;
using namespace lyx::support;
{
docstring str;
switch (e) { // For HTML: always ESCAPE_NONE. For XML: it depends, hence the parameter.
- case XMLStream::ESCAPE_NONE:
- str += c;
+ case XMLStream::ESCAPE_NONE:
+ case XMLStream::ESCAPE_COMMENTS:
+ str += c;
+ break;
+ case XMLStream::ESCAPE_ALL:
+ if (c == '<') {
+ str += "<";
break;
- case XMLStream::ESCAPE_ALL:
- if (c == '<') {
- str += "<";
- break;
- } else if (c == '>') {
- str += ">";
- break;
- }
- // fall through
- case XMLStream::ESCAPE_AND:
- if (c == '&')
- str += "&";
- else
- str +=c ;
+ } else if (c == '>') {
+ str += ">";
break;
+ }
+ // fall through
+ case XMLStream::ESCAPE_AND:
+ if (c == '&')
+ str += "&";
+ else
+ str +=c ;
+ break;
}
return str;
}
-// escape what needs escaping
-docstring xmlize(docstring const &str, XMLStream::EscapeSettings e) {
- odocstringstream d;
- docstring::const_iterator it = str.begin();
- docstring::const_iterator en = str.end();
- for (; it != en; ++it)
- d << escapeChar(*it, e);
- return d.str();
+docstring escapeChar(char c, XMLStream::EscapeSettings e)
+{
+ LATTEST(static_cast<unsigned char>(c) < 0x80);
+ return escapeChar(static_cast<char_type>(c), e);
}
-docstring escapeChar(char c, XMLStream::EscapeSettings e) {
- LATTEST(static_cast<unsigned char>(c) < 0x80);
- return escapeChar(static_cast<char_type>(c), e);
+docstring escapeString(docstring const & raw, XMLStream::EscapeSettings e)
+{
+ docstring bin;
+ bin.reserve(raw.size() * 2); // crude approximation is sufficient
+ for (size_t i = 0; i != raw.size(); ++i) {
+ char_type c = raw[i];
+ if (e == XMLStream::ESCAPE_COMMENTS && c == '-' && i > 0 && raw[i - 1] == '-')
+ bin += "-";
+ else
+ bin += xml::escapeChar(c, e);
+ }
+
+ return bin;
}
}
-docstring StartTag::writeTag() const {
+docstring StartTag::writeTag() const
+{
docstring output = '<' + tag_;
if (!attr_.empty()) {
- docstring attributes = xml::xmlize(attr_, XMLStream::ESCAPE_NONE);
+ docstring attributes = xml::escapeString(attr_, XMLStream::ESCAPE_NONE);
attributes.erase(attributes.begin(), std::find_if(attributes.begin(), attributes.end(),
[](int c) {return !std::isspace(c);}));
if (!attributes.empty()) {
}
-docstring StartTag::writeEndTag() const {
+docstring StartTag::writeEndTag() const
+{
return from_utf8("</") + tag_ + from_utf8(">");
}
-bool StartTag::operator==(FontTag const &rhs) const {
+bool StartTag::operator==(FontTag const &rhs) const
+{
return rhs == *this;
}
-docstring EndTag::writeEndTag() const {
+docstring EndTag::writeEndTag() const
+{
return from_utf8("</") + tag_ + from_utf8(">");
}
-docstring CompTag::writeTag() const {
+docstring CompTag::writeTag() const
+{
docstring output = '<' + from_utf8(tag_);
if (!attr_.empty()) {
// Erase the beginning of the attributes if it contains space characters: this function deals with that
// automatically.
- docstring attributes = xmlize(from_utf8(attr_), XMLStream::ESCAPE_NONE);
+ docstring attributes = escapeString(from_utf8(attr_), XMLStream::ESCAPE_NONE);
attributes.erase(attributes.begin(), std::find_if(attributes.begin(), attributes.end(),
[](int c) {return !std::isspace(c);}));
if (!attributes.empty()) {
} // namespace xml
-void XMLStream::writeError(std::string const &s) const {
+void XMLStream::writeError(std::string const &s) const
+{
LYXERR0(s);
os_ << from_utf8("<!-- Output Error: " + s + " -->\n");
}
-void XMLStream::writeError(docstring const &s) const {
+void XMLStream::writeError(docstring const &s) const
+{
LYXERR0(s);
os_ << from_utf8("<!-- Output Error: ") << s << from_utf8(" -->\n");
}
-bool XMLStream::closeFontTags() {
+bool XMLStream::closeFontTags()
+{
if (isTagPending(xml::parsep_tag))
// we haven't had any content
return true;
tag_stack_.pop_back();
// this shouldn't happen, since then the font tags
// weren't in any other tag.
- LASSERT(!tag_stack_.empty(), return true);
+// LASSERT(!tag_stack_.empty(), return true);
+ if (tag_stack_.empty())
+ return true;
curtag = &tag_stack_.back();
}
}
-void XMLStream::startDivision(bool keep_empty) {
+void XMLStream::startDivision(bool keep_empty)
+{
pending_tags_.push_back(makeTagPtr(xml::StartTag(xml::parsep_tag)));
if (keep_empty)
clearTagDeque();
}
-void XMLStream::endDivision() {
+void XMLStream::endDivision()
+{
if (isTagPending(xml::parsep_tag)) {
// this case is normal. it just means we didn't have content,
// so the parsep_tag never got moved onto the tag stack.
}
-void XMLStream::clearTagDeque() {
+void XMLStream::clearTagDeque()
+{
while (!pending_tags_.empty()) {
TagPtr const & tag = pending_tags_.front();
if (*tag != xml::parsep_tag)
}
-XMLStream &XMLStream::operator<<(docstring const &d) {
+XMLStream &XMLStream::operator<<(docstring const &d)
+{
clearTagDeque();
- os_ << xml::xmlize(d, escape_);
+ os_ << xml::escapeString(d, escape_);
escape_ = ESCAPE_ALL;
return *this;
}
-XMLStream &XMLStream::operator<<(const char *s) {
+XMLStream &XMLStream::operator<<(const char *s)
+{
clearTagDeque();
docstring const d = from_ascii(s);
- os_ << xml::xmlize(d, escape_);
+ os_ << xml::escapeString(d, escape_);
escape_ = ESCAPE_ALL;
return *this;
}
-XMLStream &XMLStream::operator<<(char_type c) {
+XMLStream &XMLStream::operator<<(char_type c)
+{
clearTagDeque();
os_ << xml::escapeChar(c, escape_);
escape_ = ESCAPE_ALL;
}
-XMLStream &XMLStream::operator<<(char c) {
+XMLStream &XMLStream::operator<<(char c)
+{
clearTagDeque();
os_ << xml::escapeChar(c, escape_);
escape_ = ESCAPE_ALL;
}
-XMLStream &XMLStream::operator<<(int i) {
+XMLStream &XMLStream::operator<<(int i)
+{
clearTagDeque();
os_ << i;
escape_ = ESCAPE_ALL;
}
-XMLStream &XMLStream::operator<<(EscapeSettings e) {
+XMLStream &XMLStream::operator<<(EscapeSettings e)
+{
escape_ = e;
return *this;
}
-XMLStream &XMLStream::operator<<(xml::StartTag const &tag) {
+XMLStream &XMLStream::operator<<(xml::StartTag const &tag)
+{
if (tag.tag_.empty())
return *this;
pending_tags_.push_back(makeTagPtr(tag));
}
-XMLStream &XMLStream::operator<<(xml::ParTag const &tag) {
+XMLStream &XMLStream::operator<<(xml::ParTag const &tag)
+{
if (tag.tag_.empty())
return *this;
pending_tags_.push_back(makeTagPtr(tag));
}
-XMLStream &XMLStream::operator<<(xml::CompTag const &tag) {
+XMLStream &XMLStream::operator<<(xml::CompTag const &tag)
+{
if (tag.tag_.empty())
return *this;
clearTagDeque();
}
-XMLStream &XMLStream::operator<<(xml::FontTag const &tag) {
+XMLStream &XMLStream::operator<<(xml::FontTag const &tag)
+{
if (tag.tag_.empty())
return *this;
pending_tags_.push_back(makeTagPtr(tag));
}
-XMLStream &XMLStream::operator<<(xml::CR const &) {
+XMLStream &XMLStream::operator<<(xml::CR const &)
+{
+ clearTagDeque();
os_ << from_ascii("\n");
return *this;
}
-bool XMLStream::isTagOpen(xml::StartTag const &stag) const {
+bool XMLStream::isTagOpen(xml::StartTag const &stag, int maxdepth) const
+{
auto sit = tag_stack_.begin();
auto sen = tag_stack_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (**sit == stag)
return true;
+ maxdepth -= 1;
+ }
return false;
}
-bool XMLStream::isTagOpen(xml::EndTag const &etag) const {
+bool XMLStream::isTagOpen(xml::EndTag const &etag, int maxdepth) const
+{
auto sit = tag_stack_.begin();
auto sen = tag_stack_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (etag == **sit)
return true;
+ maxdepth -= 1;
+ }
return false;
}
-bool XMLStream::isTagPending(xml::StartTag const &stag) const {
+bool XMLStream::isTagPending(xml::StartTag const &stag, int maxdepth) const
+{
auto sit = pending_tags_.begin();
auto sen = pending_tags_.cend();
- for (; sit != sen; ++sit)
+ for (; sit != sen && maxdepth != 0; ++sit) {
if (**sit == stag)
return true;
+ maxdepth -= 1;
+ }
return false;
}
// sure of that, but we won't assert (yet) if we run into
// a problem. we'll just output error messages and try our
// best to make things work.
-XMLStream &XMLStream::operator<<(xml::EndTag const &etag) {
+XMLStream &XMLStream::operator<<(xml::EndTag const &etag)
+{
if (etag.tag_.empty())
return *this;
string estr = "Closing tag `" + to_utf8(etag.tag_)
+ "' when other tags are pending. Discarded pending tags:\n";
for (dit = pending_tags_.begin(); dit != den; ++dit)
- estr += to_utf8(xml::xmlize((*dit)->writeTag(), XMLStream::ESCAPE_ALL)) + "\n";
+ estr += to_utf8(xml::escapeString((*dit)->writeTag(), XMLStream::ESCAPE_ALL)) + "\n";
writeError(estr);
// clear the pending tags...
pending_tags_.clear();
}
-docstring xml::escapeString(docstring const & raw, XMLStream::EscapeSettings e)
-{
- docstring bin;
- bin.reserve(raw.size() * 2); // crude approximation is sufficient
- for (size_t i = 0; i != raw.size(); ++i)
- bin += xml::escapeChar(raw[i], e);
-
- return bin;
-}
-
-
-docstring const xml::uniqueID(docstring const & label)
+docstring xml::uniqueID(docstring const & label)
{
// thread-safe
static atomic_uint seed(1000);
}
-docstring xml::cleanID(docstring const &orig)
+docstring xml::cleanID(docstring const & orig)
{
// The standard xml:id only allows letters,
// digits, '-' and '.' in a name.
if (mangle) {
int & mangleID = tMangleID.localData();
content += "-" + convert<docstring>(mangleID++);
- } else if (isDigitASCII(content[content.size() - 1]))
- content += ".";
+ }
mangledNames[orig] = content;