+XHTMLStream & XHTMLStream::operator<<(docstring const & d)
+{
+ clearTagDeque();
+ if (nextraw_) {
+ os_ << d;
+ nextraw_ = false;
+ } else
+ os_ << html::htmlize(d);
+ return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(const char * s)
+{
+ clearTagDeque();
+ docstring const d = from_ascii(s);
+ if (nextraw_) {
+ os_ << d;
+ nextraw_ = false;
+ } else
+ os_ << html::htmlize(d);
+ return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(char_type c)
+{
+ clearTagDeque();
+ if (nextraw_) {
+ os_ << c;
+ nextraw_ = false;
+ } else
+ os_ << html::escapeChar(c);
+ return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(NextRaw const &)
+{
+ nextraw_ = true;
+ return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(StartTag const & tag)
+{
+ if (tag.tag_.empty())
+ return *this;
+ pending_tags_.push_back(tag);
+ if (tag.keepempty_)
+ clearTagDeque();
+ return *this;
+}
+
+
+XHTMLStream & XHTMLStream::operator<<(CompTag const & tag)
+{
+ if (tag.tag_.empty())
+ return *this;
+ clearTagDeque();
+ // tabs?
+ os_ << tag.asTag();
+ cr();
+ return *this;
+}
+
+
+bool XHTMLStream::isTagOpen(string const & stag)
+{
+ TagStack::const_iterator sit = tag_stack_.begin();
+ TagStack::const_iterator const sen = tag_stack_.end();
+ for (; sit != sen; ++sit)
+ // we could check for the
+ if (sit->tag_ == stag)
+ return true;
+ return false;
+}
+
+
+// this is complicated, because we want to make sure that
+// everything is properly nested. the code ought to make
+// 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.
+XHTMLStream & XHTMLStream::operator<<(EndTag const & etag)
+{
+ if (etag.tag_.empty())
+ return *this;
+ // first make sure we're not closing an empty tag
+ if (!pending_tags_.empty()) {
+ StartTag const & stag = pending_tags_.back();
+ if (etag.tag_ == stag.tag_) {
+ // we have <tag></tag>, so we discard it and remove it
+ // from the pending_tags_.
+ pending_tags_.pop_back();
+ return *this;
+ }
+ // there is a pending tag that isn't the one we are trying
+ // to close.
+ // is this tag itself pending?
+ // non-const iterators because we may call erase().
+ TagDeque::iterator dit = pending_tags_.begin();
+ TagDeque::iterator const den = pending_tags_.end();
+ for (; dit != den; ++dit) {
+ if (dit->tag_ == etag.tag_) {
+ // it was pending, so we just erase it
+ writeError("Tried to close pending tag `" + etag.tag_
+ + "' when other tags were pending. Last pending tag is `"
+ + pending_tags_.back().tag_ + "'. Tag discarded.");
+ pending_tags_.erase(dit);
+ return *this;
+ }
+ }
+ // so etag isn't itself pending. is it even open?
+ if (!isTagOpen(etag.tag_)) {
+ writeError("Tried to close `" + etag.tag_
+ + "' when tag was not open. Tag discarded.");
+ return *this;
+ }
+ // ok, so etag is open.
+ // our strategy will be as below: we will do what we need to
+ // do to close this tag.
+ string estr = "Closing tag `" + etag.tag_
+ + "' when other tags are pending. Discarded pending tags:\n";
+ for (dit = pending_tags_.begin(); dit != den; ++dit)
+ estr += dit->tag_ + "\n";
+ writeError(estr);
+ // clear the pending tags...
+ pending_tags_.clear();
+ // ...and then just fall through.
+ }