summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pugixml.cpp423
-rw-r--r--src/pugixml.hpp84
2 files changed, 346 insertions, 161 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 30ed7aa..b98bf4f 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -434,60 +434,6 @@ namespace
template <bool _1, bool _2, bool _3, bool _4> const bool opt4_to_type<_1, _2, _3, _4>::o3 = _3;
template <bool _1, bool _2, bool _3, bool _4> const bool opt4_to_type<_1, _2, _3, _4>::o4 = _4;
-#ifndef PUGIXML_NO_STL
- template <typename opt1> void text_output_escaped(std::ostream& os, const char* s, opt1)
- {
- const bool attribute = opt1::o1;
-
- while (*s)
- {
- const char* prev = s;
-
- // While *s is a usual symbol
- while (*s && *s != '&' && *s != '<' && *s != '>' && (*s != '"' || !attribute)
- && (*s < 0 || *s >= 32 || (*s == '\r' && !attribute) || (*s == '\n' && !attribute) || *s == '\t'))
- ++s;
-
- if (prev != s) os.write(prev, static_cast<std::streamsize>(s - prev));
-
- switch (*s)
- {
- case 0: break;
- case '&':
- os << "&amp;";
- ++s;
- break;
- case '<':
- os << "&lt;";
- ++s;
- break;
- case '>':
- os << "&gt;";
- ++s;
- break;
- case '"':
- os << "&quot;";
- ++s;
- break;
- case '\r':
- os << "&#13;";
- ++s;
- break;
- case '\n':
- os << "&#10;";
- ++s;
- break;
- default: // s is not a usual symbol
- {
- unsigned int ch = (unsigned char)*s++;
-
- os << "&#" << ch << ";";
- }
- }
- }
- }
-#endif
-
struct gap
{
char* end;
@@ -1361,6 +1307,236 @@ namespace
return find;
}
}
+
+ // Output facilities
+ struct xml_buffered_writer
+ {
+ xml_buffered_writer(const xml_buffered_writer&);
+ xml_buffered_writer& operator=(const xml_buffered_writer&);
+
+ xml_buffered_writer(xml_writer& writer): writer(writer), bufsize(0)
+ {
+ }
+
+ ~xml_buffered_writer()
+ {
+ flush();
+ }
+
+ void flush()
+ {
+ if (bufsize > 0) writer.write(buffer, bufsize);
+ bufsize = 0;
+ }
+
+ void write(const void* data, size_t size)
+ {
+ if (bufsize + size > sizeof(buffer))
+ {
+ flush();
+
+ if (size > sizeof(buffer))
+ {
+ writer.write(data, size);
+ return;
+ }
+ }
+ else
+ {
+ memcpy(buffer + bufsize, data, size);
+ bufsize += size;
+ }
+ }
+
+ void write(const char* data)
+ {
+ write(data, strlen(data));
+ }
+
+ void write(char data)
+ {
+ if (bufsize + 1 > sizeof(buffer)) flush();
+
+ buffer[bufsize++] = data;
+ }
+
+ xml_writer& writer;
+ char buffer[8192];
+ size_t bufsize;
+ };
+
+ template <typename opt1> void text_output_escaped(xml_buffered_writer& writer, const char* s, opt1)
+ {
+ const bool attribute = opt1::o1;
+
+ while (*s)
+ {
+ const char* prev = s;
+
+ // While *s is a usual symbol
+ while (*s && *s != '&' && *s != '<' && *s != '>' && (*s != '"' || !attribute)
+ && (*s < 0 || *s >= 32 || (*s == '\r' && !attribute) || (*s == '\n' && !attribute) || *s == '\t'))
+ ++s;
+
+ writer.write(prev, static_cast<size_t>(s - prev));
+
+ switch (*s)
+ {
+ case 0: break;
+ case '&':
+ writer.write("&amp;");
+ ++s;
+ break;
+ case '<':
+ writer.write("&lt;");
+ ++s;
+ break;
+ case '>':
+ writer.write("&gt;");
+ ++s;
+ break;
+ case '"':
+ writer.write("&quot;");
+ ++s;
+ break;
+ case '\r':
+ writer.write("&#13;");
+ ++s;
+ break;
+ case '\n':
+ writer.write("&#10;");
+ ++s;
+ break;
+ default: // s is not a usual symbol
+ {
+ unsigned int ch = (unsigned char)*s++;
+
+ char buf[8];
+ sprintf(buf, "&#%d;", ch);
+
+ writer.write(buf);
+ }
+ }
+ }
+ }
+
+ void node_output(xml_buffered_writer& writer, const xml_node& node, const char* indent, unsigned int flags, unsigned int depth)
+ {
+ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
+
+ switch (node.type())
+ {
+ case node_document:
+ {
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth);
+ break;
+ }
+
+ case node_element:
+ {
+ writer.write('<');
+ writer.write(node.name());
+
+ for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
+ {
+ writer.write(' ');
+ writer.write(a.name());
+ writer.write('=');
+ writer.write('"');
+
+ text_output_escaped(writer, a.value(), opt1_to_type<1>());
+
+ writer.write('"');
+ }
+
+ if (flags & format_raw)
+ {
+ if (!node.first_child())
+ writer.write(" />");
+ else
+ {
+ writer.write('>');
+
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth + 1);
+
+ writer.write('<');
+ writer.write('/');
+ writer.write(node.name());
+ writer.write('>');
+ }
+ }
+ else if (!node.first_child())
+ writer.write(" />\n");
+ else if (node.first_child() == node.last_child() && node.first_child().type() == node_pcdata)
+ {
+ writer.write('>');
+
+ text_output_escaped(writer, node.first_child().value(), opt1_to_type<0>());
+
+ writer.write('<');
+ writer.write('/');
+ writer.write(node.name());
+ writer.write('>');
+ writer.write('\n');
+ }
+ else
+ {
+ writer.write('>');
+ writer.write('\n');
+
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth + 1);
+
+ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
+
+ writer.write('<');
+ writer.write('/');
+ writer.write(node.name());
+ writer.write('>');
+ writer.write('\n');
+ }
+
+ break;
+ }
+
+ case node_pcdata:
+ text_output_escaped(writer, node.value(), opt1_to_type<0>());
+ break;
+
+ case node_cdata:
+ writer.write("<![CDATA[");
+ writer.write(node.value());
+ writer.write("]]>");
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_comment:
+ writer.write("<!--");
+ writer.write(node.value());
+ writer.write("-->");
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_pi:
+ writer.write("<?");
+ writer.write(node.name());
+ if (node.value()[0])
+ {
+ writer.write(' ');
+ writer.write(node.value());
+ }
+ writer.write("?>");
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ default:
+ ;
+ }
+ }
}
namespace pugi
@@ -1386,6 +1562,26 @@ namespace pugi
}
}
+ xml_writer_file::xml_writer_file(void* file): file(file)
+ {
+ }
+
+ void xml_writer_file::write(const void* data, size_t size)
+ {
+ fwrite(data, size, 1, static_cast<FILE*>(file));
+ }
+
+#ifndef PUGIXML_NO_STL
+ xml_writer_stream::xml_writer_stream(std::ostream& stream): stream(&stream)
+ {
+ }
+
+ void xml_writer_stream::write(const void* data, size_t size)
+ {
+ stream->write(reinterpret_cast<const char*>(data), size);
+ }
+#endif
+
xml_tree_walker::xml_tree_walker(): _depth(0)
{
}
@@ -2181,100 +2377,14 @@ namespace pugi
}
}
-#ifndef PUGIXML_NO_STL
- void xml_node::print(std::ostream& os, const char* indent, unsigned int flags, unsigned int depth)
+ void xml_node::print(xml_writer& writer, const char* indent, unsigned int flags, unsigned int depth)
{
if (empty()) return;
- if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
- for (unsigned int i = 0; i < depth; ++i) os << indent;
+ xml_buffered_writer buffered_writer(writer);
- switch (type())
- {
- case node_document:
- {
- for (xml_node n = first_child(); n; n = n.next_sibling())
- n.print(os, indent, flags, depth);
- break;
- }
-
- case node_element:
- {
- os << '<' << name();
-
- for (xml_attribute a = first_attribute(); a; a = a.next_attribute())
- {
- os << ' ' << a.name() << "=\"";
-
- text_output_escaped(os, a.value(), opt1_to_type<1>());
-
- os << "\"";
- }
-
- if (flags & format_raw)
- {
- if (!_root->first_child) // 0 children
- os << " />";
- else
- {
- os << ">";
- for (xml_node n = first_child(); n; n = n.next_sibling())
- n.print(os, indent, flags, depth + 1);
- os << "</" << name() << ">";
- }
- }
- else if (!_root->first_child) // 0 children
- os << " />\n";
- else if (_root->first_child == _root->last_child && first_child().type() == node_pcdata)
- {
- os << ">";
-
- text_output_escaped(os, first_child().value(), opt1_to_type<0>());
-
- os << "</" << name() << ">\n";
- }
- else
- {
- os << ">\n";
-
- for (xml_node n = first_child(); n; n = n.next_sibling())
- n.print(os, indent, flags, depth + 1);
-
- if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
- for (unsigned int i = 0; i < depth; ++i) os << indent;
-
- os << "</" << name() << ">\n";
- }
-
- break;
- }
-
- case node_pcdata:
- text_output_escaped(os, value(), opt1_to_type<0>());
- break;
-
- case node_cdata:
- os << "<![CDATA[" << value() << "]]>";
- if ((flags & format_raw) == 0) os << "\n";
- break;
-
- case node_comment:
- os << "<!--" << value() << "-->";
- if ((flags & format_raw) == 0) os << "\n";
- break;
-
- case node_pi:
- os << "<?" << name();
- if (value()[0]) os << ' ' << value();
- os << "?>";
- if ((flags & format_raw) == 0) os << "\n";
- break;
-
- default:
- ;
- }
+ node_output(buffered_writer, *this, indent, flags, depth);
}
-#endif
#ifdef __BORLANDC__
bool operator&&(const xml_node& lhs, bool rhs)
@@ -2599,25 +2709,34 @@ namespace pugi
return res;
}
-#ifndef PUGIXML_NO_STL
- bool xml_document::save_file(const char* name, const char* indent, unsigned int flags)
+ void xml_document::save(xml_writer& writer, const char* indent, unsigned int flags)
{
- std::ofstream out(name, std::ios::out);
- if (!out) return false;
+ xml_buffered_writer buffered_writer(writer);
if (flags & format_write_bom_utf8)
{
static const unsigned char utf8_bom[] = {0xEF, 0xBB, 0xBF};
- out.write(reinterpret_cast<const char*>(utf8_bom), 3);
+ buffered_writer.write(utf8_bom, 3);
}
- out << "<?xml version=\"1.0\"?>";
- if (!(flags & format_raw)) out << "\n";
- print(out, indent, flags);
+ buffered_writer.write("<?xml version=\"1.0\"?>");
+ if (!(flags & format_raw)) buffered_writer.write("\n");
+ node_output(buffered_writer, *this, indent, flags, 0);
+ }
+
+ bool xml_document::save_file(const char* name, const char* indent, unsigned int flags)
+ {
+ FILE* file = fopen(name, "wb");
+ if (!file) return false;
+
+ xml_writer_file writer(file);
+ save(writer, indent, flags);
+
+ fclose(file);
+
return true;
}
-#endif
void xml_document::precompute_document_order()
{
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index 927d18e..bebc246 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -289,6 +289,67 @@ namespace pugi
#endif
/**
+ * Abstract writer class
+ * \see xml_node::print
+ */
+ class xml_writer
+ {
+ public:
+ /**
+ * Virtual dtor
+ */
+ virtual ~xml_writer() {}
+
+ /**
+ * Write memory chunk into stream/file/whatever
+ *
+ * \param data - data pointer
+ * \param size - data size
+ */
+ virtual void write(const void* data, size_t size) = 0;
+ };
+
+ /** xml_writer implementation for FILE*
+ * \see xml_writer
+ */
+ class xml_writer_file: public xml_writer
+ {
+ public:
+ /**
+ * Construct writer instance
+ *
+ * \param file - this is FILE* object, void* is used to avoid header dependencies on stdio
+ */
+ xml_writer_file(void* file);
+
+ virtual void write(const void* data, size_t size);
+
+ private:
+ void* file;
+ };
+
+ #ifndef PUGIXML_NO_STL
+ /** xml_writer implementation for streams
+ * \see xml_writer
+ */
+ class xml_writer_stream: public xml_writer
+ {
+ public:
+ /**
+ * Construct writer instance
+ *
+ * \param stream - output stream object
+ */
+ xml_writer_stream(std::ostream& stream);
+
+ virtual void write(const void* data, size_t size);
+
+ private:
+ std::ostream* stream;
+ };
+ #endif
+
+ /**
* A light-weight wrapper for manipulating attributes in DOM tree.
* Note: xml_attribute does not allocate any memory for the attribute it wraps; it only wraps a
* pointer to existing attribute.
@@ -947,7 +1008,7 @@ namespace pugi
*/
template <typename Predicate> xml_node find_node(Predicate pred) const;
-#ifndef PUGIXML_NO_STL
+ #ifndef PUGIXML_NO_STL
/**
* Get the absolute node path from root as a text string.
*
@@ -955,7 +1016,7 @@ namespace pugi
* \return path string (e.g. '/bookstore/book/author').
*/
std::string path(char delimiter = '/') const;
-#endif
+ #endif
/**
* Search for a node by path.
@@ -1014,17 +1075,15 @@ namespace pugi
/// \internal Document order or 0 if not set
unsigned int document_order() const;
- #ifndef PUGIXML_NO_STL
/**
- * Print subtree to stream
+ * Print subtree to writer
*
- * \param os - output stream
+ * \param writer - writer object
* \param indent - indentation string
* \param flags - formatting flags
* \param depth - starting depth (used for indentation)
*/
- void print(std::ostream& os, const char* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0);
- #endif
+ void print(xml_writer& writer, const char* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0);
};
#ifdef __BORLANDC__
@@ -1381,7 +1440,15 @@ namespace pugi
*/
bool parse(const transfer_ownership_tag&, char* xmlstr, unsigned int options = parse_default);
-#ifndef PUGIXML_NO_STL
+ /**
+ * Save XML to writer
+ *
+ * \param writer - writer object
+ * \param indent - indentation string
+ * \param flags - formatting flags
+ */
+ void save(xml_writer& writer, const char* indent = "\t", unsigned int flags = format_default);
+
/**
* Save XML to file
*
@@ -1391,7 +1458,6 @@ namespace pugi
* \return success flag
*/
bool save_file(const char* name, const char* indent = "\t", unsigned int flags = format_default);
-#endif
/**
* Compute document order for the whole tree