diff options
| author | arseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640> | 2006-11-06 18:31:28 +0000 | 
|---|---|---|
| committer | arseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640> | 2006-11-06 18:31:28 +0000 | 
| commit | 69cc3fcb3a28d4b7f69dfa5f4dcc025eb53332d8 (patch) | |
| tree | 7deeb3217f91fad20ebbd10883331cae569dc3bb | |
| parent | 41b24d0905d14883cc61a37a68fa6c8a9ebf3289 (diff) | |
pugixml 0.1: sources and documentation
git-svn-id: http://pugixml.googlecode.com/svn/trunk@2 99668b35-9821-0410-8761-19e4c4f06640
| -rw-r--r-- | docs/index.html | 993 | ||||
| -rw-r--r-- | docs/tree.png | bin | 0 -> 16103 bytes | |||
| -rw-r--r-- | src/pugixml.cpp | 1868 | ||||
| -rw-r--r-- | src/pugixml.hpp | 619 | 
4 files changed, 3480 insertions, 0 deletions
| diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..f56af74 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,993 @@ +<html>
 +<head>
 +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 +<title>pugixml documentation</title>
 +</head>
 +<body link="#0000ff" vlink="#800080">
 +<table border="0" cellpadding="4" cellspacing="0" width="100%" summary="header">
 +  <tr> 
 +    <td valign="top" bgcolor="#eeeeeee"> 
 +      <h2 align="left">pugixml documentation</h2>
 +    </td>
 +  </tr>
 +</table>
 +<hr>
 +<h2>Contents</h2>
 +<dl class="index">
 +  <dt><a href="#Introduction">Introduction</a></dt>
 +  <dt><a href="#DOM">Document Object Model</a></dt>
 +  <dt><a href="#Documentation">Documentation</a>
 +	<dd><a href="#Doc_Introduction">Introduction</a></dd>
 +	<dd><a href="#Doc_Parser">xml_parser class</a></dd>
 +	<dd><a href="#Doc_Node">xml_node class</a></dd>
 +	<dd><a href="#Doc_Attribute">xml_attribute class</a></dd>
 +	<dd><a href="#Doc_Iterators">Iterators</a></dd>
 +	<dd><a href="#Doc_Misc">Miscellaneous</a></dd>
 +	<dd><a href="#Doc_Lifetime">Lifetime issues and memory management</a></dd>
 +  </dt>
 +  <dt><a href="#Parsing">Parsing process</a></dt>
 +  <dt><a href="#Compliance">W3C compliance</a></dt>
 +  <dt><a href="#FAQ">FAQ</a></dt>
 +  <dt><a href="#Bugs">Bugs</a></dt>
 +  <dt><a href="#Future_work">Future work</a></dt>
 +  <dt><a href="#Changelog">Changelog</a></dt>
 +  <dt><a href="#Acknowledgements">Acknowledgements</a></dt>
 +  <dt><a href="#License">License</a></dt>
 +</dl>
 +
 +<hr>
 +
 +<a name="Introduction">
 +<h2>Introduction</h2>
 +<p><i>pugixml</i> is just another XML parser. This is a successor to
 +<a href="http://www.codeproject.com/soap/pugxml.asp">pugxml</a> (well, to be honest, the only part
 +that is left as is is wildcard matching code, the rest was either heavily refactored or rewritten
 +from scratch). The main features (call it USP) are:</p>
 +
 +<ul>
 +<li>low memory consumption and fragmentation (the win over <i>pugxml</i> is ~1.5 times, <i>TinyXML</i>
 +- ~4.5 times, <i>Xerces (DOM)</i> - ~7 times <a href="#annot-1"><sup>1</sup></a>)</li>
 +<li>extremely high parsing speed (the win over <i>pugxml</i> is ~11.8 times, <i>TinyXML</i> - ~13.6
 +times, <i>Xerces-DOM</i> - ~20 times <a href="#annot-1"><sup>1</sup></a></li>
 +<li>extremely high parsing speed (well, I'm repeating myself, but it's so fast, that it outperforms
 +<i>expat</i> by <b>2 times</b> on test XML) <a href="#annot-2"><sup>2</sup></a></li>
 +<li>more or less standard-conformant (it will parse any standard-compliant file correctly in w3c-
 +compliance mode, with the exception of DTD related issues and XML namespaces)</li>
 +<li>pretty much error-ignorant (it will not choke on something like <text>You & Me</text>,
 +like <i>expat</i> will; it will try to recover the state even if meeting an error (like finding matching
 +tags for closing ones); it will parse files with data in wrong encoding; and so on)</li>
 +<li>clean interface (a heavily refactored pugxml's one)</li>
 +<li>more or less unicode-aware (actually, it assumes UTF-8 encoding of the input data, though
 +it will readily work with ANSI - no UTF-16 for now (see <a href="#Future_work">Future work</a>), with
 +helper conversion functions (UTF-8 <-> UTF-16/32 (whatever is the default for std::wstring & wchar_t))</li>
 +<li>fully standard compliant code (approved by <a href="http://www.comeaucomputing.com/tryitout/">Comeau</a>
 +strict mode), multiplatform (tested on win32 only ^_^)</li>
 +<li>high flexibility. You can control many aspects of file parsing and DOM tree building via parsing
 +options.
 +</ul>
 +
 +<p>Okay, you might ask - what's the catch? Everything is so cute - it's small, fast, robust, clean solution
 +for parsing XML. What is missing? Ok, we are fair developers - so here is a misfeature list:</p>
 +
 +<ul>
 +<li>memory consumption. It beats every DOM-based parser that I know of - but when SAX parser comes,
 +there is no chance. You can't process a 2 Gb XML file with less than 4 Gb of memory - and do it fast.
 +Though <i>pugixml</i> behaves better, than all other DOM-based parser, so if you're stuck with DOM,
 +it's not a problem.</li>
 +<li>memory consumption. Ok, I'm repeating myself. Again. When other parsers will allow you to provide
 +XML file in a constant storage (or even as a memory mapped area), <i>pugixml</i> will not. So you'll
 +have to copy the entire data into a non-constant storage. Moreover, it should persist during the
 +parser's lifetime (the reasons for that and more about lifetimes is written below). Again, if you're
 +ok with DOM - it should not be a problem, because the overall memory consumption is less (well, though
 +you'll need a contiguous chunk of memory, which can be a problem).</li>
 +<li>lack of validation, DTD processing, XML namespaces, proper handling of encoding. If you need those -
 +go take MSXML or XercesC or anything like that.</li>
 +<li>lack of XPath & UTF-16/32 parsing. These are not implemented for now, but they are the features
 +for the next release.</li>
 +<li>immutability of DOM tree. It's constant. You can't change it. There are good reasons for prohibiting
 +that, though it is a thing that will likely be in the next release.</li>
 +</ul>
 +
 +<hr>
 +
 +<a name="annot-1"><sup>1</sup><small> The tests were done on a 1 mb XML file with a 4 levels deep tree
 +with a small amount of text. The times are that of building DOM tree.</small> <br>
 +<a name="annot-2"><sup>2</sup><small> Obviously, you can't estimate time of building DOM tree for a
 +SAX parser, so the times of reading the data into storage that closely represented the structure of
 +an XML file were measured.</small>
 +
 +<hr>
 +
 +<a name="DOM">
 +<h2>Document Object Model</h2>
 +
 +<p><i>pugixml</i> is a DOM-based parser. This means, that the XML document is converted to a tree.
 +Each XML tag is converted to a node in DOM tree. If a tag is contained in some other tag, its node
 +is a child to the outer tag's one. Comments, CDATA sections and PIs (Processing Instructions) also are
 +transformed into tree nodes, as is the standalone text. Each node has its type.</p>
 +
 +<p>Here is an example of an XML document:
 +
 +<pre>
 +<span style='color:#004a43; '><?</span><span style='color:#004a43; '>xml</span> <span style='color:#004a43; '>version</span><span style='color:#808030; '>=</span><span style='color:#008c00; '>"1.0"</span><span style='color:#004a43; '>?></span>
 +<span style='color:#a65700; '><</span><span style='color:#5f5035; '>mesh</span> <span style='color:#274796; '>name</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>mesh_root</span><span style='color:#0000e6; '>"</span><span style='color:#a65700; '>></span>
 +    <span style='color:#696969; '><!--</span><span style='color:#696969; '> here is a mesh node </span><span style='color:#696969; '>--></span>
 +    some text
 +    <span style='color:#606060; '><![CDATA[</span>[someothertext<span style='color:#606060; '>]]></span>
 +    some more text
 +    <span style='color:#a65700; '><</span><span style='color:#5f5035; '>node</span> <span style='color:#274796; '>attr1</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>value1</span><span style='color:#0000e6; '>"</span> <span style='color:#a65700; '>/></span>
 +    <span style='color:#a65700; '><</span><span style='color:#5f5035; '>node</span> <span style='color:#274796; '>attr1</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>value2</span><span style='color:#0000e6; '>"</span><span style='color:#a65700; '>></span>
 +        <span style='color:#004a43; '><?</span><span style='color:#004a43; '>TARGET</span><span style='color:#004a43; '> somedata</span><span style='color:#004a43; '>?></span>
 +        <span style='color:#a65700; '><</span><span style='color:#5f5035; '>innernode</span><span style='color:#a65700; '>/></span>
 +    <span style='color:#a65700; '></</span><span style='color:#5f5035; '>node</span><span style='color:#a65700; '>></span>
 +<span style='color:#a65700; '></</span><span style='color:#5f5035; '>mesh</span><span style='color:#a65700; '>></span>
 +</pre>
 +
 +It gets converted to the following tree (note, that with some parsing options comments, PIs and CDATA
 +sections are not stored in the tree, and with some options there are also nodes with whitespaces
 +and the contents of PCDATA sections is a bit different (with trailing/leading whitespaces). So generally
 +the resulting DOM tree depends on the parsing options):</p>
 +
 +<p><img src="tree.png"></p>
 +
 +<p>The parent-children relations are shown with lines. Some nodes have previous and next siblings
 +(for example, the next sibling for node_comment node is node_pcdata with value "some text", and the
 +previous sibling for node_element with name "mesh" is node_pi with target "xml" (target for PI nodes
 +is stored in the node name)).</p>
 +<hr>
 +
 +<a name="Documentation">
 +<h2>Documentation</h2>
 +
 +<a name="Doc_Introduction">
 +<h3>Introduction</h3>
 +
 +<p><i>pugixml</i> is a library for parsing XML files, which means that you give it XML data some way,
 +and it gives you the DOM tree and the ways to traverse it and to get some useful information from it.
 +The library source consist of two files, the header <b>pugixml.hpp</b>, and the source code <b>pugixml.cpp</b>.
 +You can either compile cpp file in your project, or build a static library (or perhaps even a DLL),
 +or make the whole code use inline linkage and make one big file (as it was done in <i>pugxml</i>).
 +All library classes reside in namespace <b>pugi</b>, so you can either use fully qualified
 +names (<b>pugi::xml_node</b>) or write a using declaration (<b>using namespace pugi;</b>, <b>using
 +pugi::xml_node</b>) and use plain names. All classes have the <b>xml_</b> prefix.</p>
 +
 +<p>By default it's supposed that you compile the source file with your project (add it into your
 +project, or add relevant entry in your Makefile, or do whatever you need to do with your compilation
 +environment). The library is written in standard-conformant C++ and was tested on win32 platform
 +(MSVC 7.1 (2003), MSVC 8.0 (2005)).</p>
 +
 +<a name="Doc_Parser">
 +<h3>xml_parser class</h3>
 +
 +<p><b>xml_parser</b> class is the core of parsing process; you initiate parsing with it, you get DOM
 +tree from it, the nodes and attributes are stored in it. You have two ways to load a file: either
 +provide a string with XML-data (it has to be null-terminated, and it will be modified during parsing
 +process, so it can not be a piece of read-only memory), or with an <b>std::istream</b> object (any input
 +stream, like <b>std::ifstream</b>, <b>std::istringstream</b>, etc.) - in this case the parser will allocate
 +the necessary amount of memory (equivalent to stream's size) and read everything from the stream.</p>
 +
 +<p>The functions for parsing are:
 +<dl>
 +<dt>
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >void</font></b> <font color="#000000" >parse(std::istream&</font> <font color="#000000" >stream,</font> <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >optmsk</font> <font color="#000000" >=</font> <font color="#000000" >parse_noset);</font></font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +<dd>This function will create a buffer with the size equal to that of provided <code>stream</code>,
 +read the chunk of data from the stream and parse it with provided options (<code>optmsk</code>).
 +The stream does not have to persist after the call to the function, the lifetime of internal buffer
 +with stream's data is managed by <i>pugixml</i>.
 +</dd>
 +</dt>
 +<dt> </dt>
 +<dt>
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >parse(</font><b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >xmlstr,</font> <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >optmsk</font> <font color="#000000" >=</font> <font color="#000000" >parse_noset);</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +<dd>This function parses the provided string with provided options, and returns the position where the
 +parsing stopped (do not expect, that parsing will stop on every error, or on most of them - as I've
 +said, <i>pugixml</i> is error ignorant). The input string is modified. The string must persist for the
 +lifetime of the parser.
 +</dt>
 +<dt> </dt>
 +<dt>
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_parser(std::istream&</font> <font color="#000000" >stream,</font> <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >optmsk</font> <font color="#000000" >=</font> <font color="#000000" >parse_default);</font></font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +<dd>Just a convenience ctor, that calls the corresponding parse() function.</dd>
 +</dt>
 +<dt> </dt>
 +<dt>
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_parser(</font><b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >xmlstr,</font> <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >optmsk</font> <font color="#000000" >=</font> <font color="#000000" >parse_default);</font></font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +<dd>Just a convenience ctor, that calls the corresponding parse() function.</dd>
 +</dt>
 +</dl>
 +
 +<p>If you want to provide XML data after the creation of the parser, use the default ctor. Otherwise
 +you are free to use either parsing ctors or default ctor and later - parsing function.</p>
 +
 +<p>After parsing an XML file, you'll get a DOM tree. To get access to it (or, more precisely, to its
 +root), call either <b>document()</b> function or cast <b>xml_parser</b> object to <b>xml_node</b> by
 +using the following functions:</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >operator</font></b> <font color="#000000" >xml_node()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >document()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Ok, easy part behind - now let's dive into parsing options. There is a variety of them, and you
 +must choose them wisely to get the needed results and the best speed/least memory overhead. At first,
 +there are flags that determine which parts of the document will be put into DOM tree, and which will
 +be just skipped:</p>
 +
 +<ul>
 +<li>If <b>parse_pi</b> is on, then processing instructions (<b><? ... ?></b>) are put into DOM
 +tree (with node type <b>node_pi</b>, otherwise they are discarded. Note that for now the prolog
 +(<?xml ... ?>) is parsed as a processing instruction.
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_comments</b> is on, then comments (<b><!-- ... --></b>) are put into DOM
 +tree (with node type <b>node_comment</b>, otherwise they are discarded.
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_cdata</b> is on, then the content of CDATA section (<b><![CDATA[[ ... ]]></b>)
 +is put into DOM tree (with node type <b>node_cdata</b>, otherwise it is discarded.
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_ws_pcdata</b> is off, then the content of PCDATA section (it's the plain text
 +in the node, like in <b><some_tag>Hello!</some_tag></b>) is discarded if it consists only
 +of space-like characters (spaces, tabs and newlines).
 +<br>Default value: off
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_ext_pcdata</b> is off, then the content of PCDATA section is discarded if it belongs
 +to root (document) node, that is it does not have a parent tag.
 +<br>Default value: on
 +<br>In W3C mode: off</li>
 +</ul>
 +
 +<p>Then there are flags that determine how the processing of the retrieved data is done. There are
 +several reasons for these flags, mainly:
 +<ul>
 +<li>parsing speed. The less processing - the more speed.</li>
 +<li>data fetching comfort. Sometimes you're ok with messed linefeeds, sometimes you're not. Sometimes
 +you want your PCDATA trimmed, sometimes you do not. Sometimes you want your attribute values normalized,
 +sometimes you do not. Some of these are normally specified in DOCTYPE, though...
 +<li>...parser is not DOCTYPE aware (and will never be), so you need a way to set those properties -
 +if not on per-node basis, then on per-document</li>
 +</ul>
 +So, these are the processing flags:
 +</p>
 +
 +<ul>
 +<li>If <b>parse_trim_pcdata</b> is on, then the trimming of leading/trailing space-like characters
 +is performed for PCDATA content
 +<br>Default value: on
 +<br>In W3C mode: off</li>
 +<li>If <b>parse_trim_attribute</b> is on, then the trimming of leading/trailing space-like characters
 +is performed for attribute values
 +<br>Default value: on
 +<br>In W3C mode: off</li>
 +<li>If <b>parse_escapes_pcdata</b> is on, then the character reference expansion is done for PCDATA
 +content (replacing <lt; with <, &#4c; with L, etc.).
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_escapes_attribute</b> is on, then the character reference expansion is done for
 +attribute values (replacing <lt; with <, &#4c; with L, etc.).
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_wnorm_pcdata</b> is on, then the whitespace normalisation is done for PCDATA content
 +(this includes replacing any space-like character by a space character and converting sequences of
 +spaces into a single space)
 +<br>Default value: on
 +<br>In W3C mode: off</li>
 +<li>If <b>parse_wnorm_attribute</b> is on, then the whitespace normalisation is done for attribute
 +values
 +<br>Default value: on
 +<br>In W3C mode: off</li>
 +<li>If <b>parse_wconv_attribute</b> is on, then the whitespace conversion is done for attribute
 +values (this is a subset of whitespace normalization, and includes only replacing space-like characters
 +with spaces). If <b>parse_wnorm_attribute</b> is on, this flag has no effect.
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +<li>If <b>parse_eol_cdata</b> is on, then the end-of-line handling is done for CDATA content (this
 +includes converting any pair of 0x0d 0x0a characters to a single 0x0a and converting any standalone
 +0x0d to 0x0a). Note, that end-of-line handling is done for all content (PCDATA, attribute values)
 +except CDATA sections (if this flag is off).
 +<br>Default value: on
 +<br>In W3C mode: on</li>
 +</ul>
 +
 +<p>Finally, there are two more flags, that indicate closing tag parsing. When <i>pugixml</i> meets a
 +close tags, there are three ways:
 +<ul>
 +<li>check that the tag name matches the opening tag, return an error if it does not. This is a
 +standard-compliant way, is controlled by <b>parse_check_end_tags</b> flag, which is on in W3C mode</li>
 +<li>try to find the corresponding tag name (so that <b><foo> <bar> </foo></b> will be parsed
 +correctly). This is controlled by <b>parse_match_end_tags</b>, which is on by default</li>
 +<li>just treat the tag as a closing tag for the node (so that <b><foo> ... </bar></b> will
 +be parsed as <b><foo> ... </foo></b>). This is the fastest way, and this is what <i>pugxml</i>
 +is doing, but it can corrupt your DOM tree. This way is chosen if both <b>parse_check_end_tags</b> and
 +<b>parsse_match_end_tags</b> are off.
 +</ul>
 +Note, that these 2 flags are mutually exclusive.
 +</p>
 +
 +<p>Did I say finally? Ok, so <b>finally</b> there are some helper flags, or better groups of flags.
 +These are:
 +<ul>
 +<li><b>parse_minimal</b> - no flag is set (this also means the fastest parsing)</li>
 +<li><b>parse_default</b> - default set of flags</li>
 +<li><b>parse_noset</b> - use the current parser options (see below)</li>
 +<li><b>parse_w3c</b> - use the W3C compliance mode</li>
 +</ul>
 +</p>
 +
 +<p>A couple of words on flag usage. The parsing options are just a set of bits, with each bit corresponding
 +to one flag. You can turn the flag on by OR-ing the options value with this flag's constant:
 +<pre>
 +	parse_w3c | parse_wnorm_pcdata
 +</pre>
 +or turn the flag off by AND-ing the options value with the NEGation of this flag's constant:
 +<pre>
 +	parse_w3c & ~parse_comments
 +</pre>
 +You can access the current options of parser by <b>options()</b> method:
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >options()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >options(</font><b><font color="#0000ff" >unsigned</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >optmsk);</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +(the latter one returns previous options). These options are used when <b>parse_noset</b> flag set is
 +passed to <b>parse()</b> functions (which is the default value of corresponding parameter).
 +</p>
 +
 +<a name="Doc_Node">
 +<h3>xml_node class</h3>
 +
 +<p>If <b>xml_parser</b> is a heart of constructing a DOM tree from file, <b>xml_node</b> is a heart
 +of processing the tree. This is a simple wrapper, so it's small (4/8 bytes, depending on the size of
 +pointer), you're free to copy it and it does not own anything. I'll continue with a list of methods
 +with their description, with one note in advance. Some functions, that do something according to a
 +string-like parameter, have a pair with a suffix <b>_w</b>. The <b>_w</b> suffix tells, that this
 +function is doing a wildcard matching, instead of simple string comparison. You're free to use wildcards
 +<b>*</b> (that is equal to any sequence of characters (possibly empty)), <b>?</b> (that is equal to
 +any character) and character sets (<b>[Abc]</b> means 'any symbol of A, b and c', <b>[A-Z4]</b> means
 +'any symbol from A to Z, or 4', <b>[!0-9]</b> means 'any symbol, that is not a digit'). So the wildcard
 +<b>?ell_[0-9][0-9]_*</b> will match strings like 'cell_23_xref', 'hell_00_', but will not match the
 +strings like 'ell_23_xref', 'cell_0_x' or 'cell_0a_x'.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <i><font color="#808080" >/// Access iterators for this node's collection of child nodes.</font></i>
 +        <font color="#000000" >iterator</font> <font color="#000000" >begin()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >iterator</font> <font color="#000000" >end()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        
 +        <i><font color="#808080" >/// Access iterators for this node's collection of child nodes (same as begin/end).</font></i>
 +        <font color="#000000" >iterator</font> <font color="#000000" >children_begin()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >iterator</font> <font color="#000000" >children_end()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +    
 +        <i><font color="#808080" >/// Access iterators for this node's collection of attributes.</font></i>
 +        <font color="#000000" >attribute_iterator</font> <font color="#000000" >attributes_begin()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >attribute_iterator</font> <font color="#000000" >attributes_end()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +
 +        <i><font color="#808080" >/// Access iterators for this node's collection of siblings.</font></i>
 +        <font color="#000000" >iterator</font> <font color="#000000" >siblings_begin()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >iterator</font> <font color="#000000" >siblings_end()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Functions, returning the iterators to walk through children/siblings/attributes. More on that in
 +<a href="#Doc_Iterators">Iterators</a> section.</p>
 +	
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >operator</font></b> <font color="#000000" >unspecified_bool_type()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>This is a safe bool-like conversion operator. You can check node's validity (<b>if (xml_node)</b>,
 + <b>if (!xml_node)</b>, <b>if (node1 && node2 && !node3 && cond1 && ...)</b> - you get the idea) with
 +it.
 +</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >==(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >!=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" ><(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >>(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" ><=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >>=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Comparison operators</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <font color="#000000" >empty()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p><code>if (node.empty())</code> is equivalent to <code>if (!node)</code></p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node_type</font> <font color="#000000" >type()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >value()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Access node's properties (type, name and value). If there is no name/value, the corresponding functions
 +return <b>""</b> - they <u>never</u> return NULL.</p>	
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >child(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >child_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get a child node with specified name, or <b>xml_node()</b> (this is an invalid node) if nothing is
 +found</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >attribute(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >attribute_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get an attribute with specified name, or <b>xml_attribute()</b> (this is an invalid attribute) if
 +nothing is found</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >sibling(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >sibling_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get a node's sibling with specified name, or <b>xml_node()</b> if nothing is found.<br>
 +<code>node.sibling(name)</code> is equivalent to <code>node.parent().child(name)</code>.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >next_sibling(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >next_sibling_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >next_sibling()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions get the next sibling, that is, one of the siblings of that node, that is to the
 +right. <code>next_sibling()</code> just returns the right brother of the node (or <b>xml_node()</b>),
 +the two other functions are searching for the sibling with the given name</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >previous_sibling(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >previous_sibling_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >previous_sibling()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions do exactly the same as <code>next_sibling</code> ones, with the exception that they
 +search for the left siblings.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >parent()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get a parent node. The parent node for the root one (the document) is considered to be the document
 +itself.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >child_value()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Look for the first node of type <b>node_pcdata</b> or <b>node_cdata</b> among the
 +children of the current node and return its contents (or <b>""</b> if nothing is found)</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >first_attribute()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >last_attribute()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions get the first and last attributes of the node (or <b>xml_attribute()</b> if the node
 +has no attributes).</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_child()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >last_child()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions get the first and last children of the node (or <b>xml_node()</b> if the node has
 +no children).</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >template</font></b> <font color="#000000" ><</font><b><font color="#0000ff" >typename</font></b> <font color="#000000" >OutputIterator></font> <b><font color="#0000ff" >void</font></b> <font color="#000000" >all_elements_by_name(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <font color="#000000" >OutputIterator</font> <font color="#000000" >it)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >template</font></b> <font color="#000000" ><</font><b><font color="#0000ff" >typename</font></b> <font color="#000000" >OutputIterator></font> <b><font color="#0000ff" >void</font></b> <font color="#000000" >all_elements_by_name_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <font color="#000000" >OutputIterator</font> <font color="#000000" >it)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get all elements with the specified name in the subtree (depth-first search) and return them with
 +the help of output iterator (i.e. std::back_inserter)</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >template</font></b> <font color="#000000" ><</font><b><font color="#0000ff" >typename</font></b> <font color="#000000" >Predicate></font> <font color="#000000" >xml_attribute</font> <font color="#000000" >find_attribute(Predicate</font> <font color="#000000" >pred)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >template</font></b> <font color="#000000" ><</font><b><font color="#0000ff" >typename</font></b> <font color="#000000" >Predicate></font> <font color="#000000" >xml_node</font> <font color="#000000" >find_child(Predicate</font> <font color="#000000" >pred)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >template</font></b> <font color="#000000" ><</font><b><font color="#0000ff" >typename</font></b> <font color="#000000" >Predicate></font> <font color="#000000" >xml_node</font> <font color="#000000" >find_element(Predicate</font> <font color="#000000" >pred)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Find attribute, child or a node in the subtree (find_element - depth-first search) with the help
 +of the given predicate. Predicate should behave like a function which accepts a <b>xml_node</b> or
 +<b>xml_attribute</b> (for find_attribute) parameter and returns <b>bool</b>. The first entity for which
 +the predicate returned true is returned. If predicate returned false for all entities, <b>xml_node()</b>
 +or <b>xml_attribute()</b> is returned.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_value(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_value_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_attribute(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_attribute_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_attribute(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_attribute_w(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_name,</font> <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >attr_value)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Find the first node (depth-first search), which corresponds to the given criteria (i.e. either has
 +a matching name, or a matching value, or has an attribute with given name/value, or has an attribute
 +and has a matching name). Note that <b>_w</b> versions treat all parameters as wildcards.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_node(xml_node_type</font> <font color="#000000" >type)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Return a first node (depth-first search) with a given type, or <b>xml_node()</b>.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >std::string</font> <font color="#000000" >path(</font><b><font color="#0000ff" >char</font></b> <font color="#000000" >delimiter</font> <font color="#000000" >=</font> <font color="#ff0000" >'/'</font><font color="#000000" >)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get a path of the node (i.e. the string of names of the nodes on the path from the DOM tree root
 +to the node, separated with delimiter (/ by default).</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_node</font> <font color="#000000" >first_element_by_path(</font><b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >path,</font> <b><font color="#0000ff" >char</font></b> <font color="#000000" >delimiter</font> <font color="#000000" >=</font> <font color="#ff0000" >'/'</font><font color="#000000" >)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get the first element that has the following path. The path can be absolute (beginning with delimiter) or
 +relative, '..' means 'up-level' (so if we are at the path <b>mesh/fragment/geometry/stream</b>, <b>../..</b>
 +will lead us to <b>mesh/fragment</b>, and <b>/mesh</b> will lead us to <b>mesh</b>).</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <font color="#000000" >traverse(xml_tree_walker&</font> <font color="#000000" >walker)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Traverse the subtree (beginning with current node) with the walker, return the result. See
 +<a href="#Doc_Misc">Miscellaneous</a> section for details.</p>
 +
 +<a name="Doc_Attribute">
 +<h3>xml_attribute class</h3>
 +
 +<p>Like <b>xml_node</b>, <b>xml_attribute</b> is a simple wrapper of the node's attribute.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >==(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >!=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" ><(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >>(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" ><=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >bool</font></b> <b><font color="#0000ff" >operator</font></b><font color="#000000" >>=(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_attribute&</font> <font color="#000000" >r)</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Comparison operators.</p>	
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >operator</font></b> <font color="#000000" >unspecified_bool_type()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Safe bool conversion - like in <b>xml_node</b>, use this to check for validity.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <font color="#000000" >empty()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Like with <b>xml_node</b>, <code>if (attr.empty())</code> is equivalent to <code>if (!attr)</code>.
 +</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >next_attribute()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >previous_attribute()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get the next/previous attribute of the node, that owns the current attribute. Return <b>xml_attribute()</b>
 +if no such attribute is found.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >name()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >const</font></b> <b><font color="#0000ff" >char</font></b><font color="#000000" >*</font> <font color="#000000" >value()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get the name and value of the attribute. These methods never return NULL - they return <b>""</b> instead.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >int</font></b> <font color="#000000" >as_int()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >double</font></b> <font color="#000000" >as_double()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +        <b><font color="#0000ff" >float</font></b> <font color="#000000" >as_float()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Convert the value of an attribute to the desired type. If the conversion is not successfull, return
 +default value (0 for int, 0.0 for double, 0.0f for float). These functions rely on CRT functions ato*.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <font color="#000000" >as_bool()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Convert the value of an attribute to bool. This method returns true if the first character of the
 +value is '1', 't', 'T', 'y' or 'Y'. Otherwise it returns false.</p>
 +
 +<a name="Doc_Iterators">
 +<h3>Iterators</h3>
 +
 +<p>Sometimes you have to cycle through the children or the attributes of the node. You can do it either
 +by using <b>next_sibling</b>, <b>previous_sibling</b>, <b>next_attribute</b> and <b>previous_attribute</b>
 +(along with <b>first_child</b>, <b>last_child</b>, <b>first_attribute</b> and <b>last_attribute</b>),
 +or you can use an iterator-like interface. There are two iterator types, <b>xml_node_iterator</b> and
 +<b>xml_attribute_iterator</b>. They are bidirectional constant iterators, which means that you can
 +either increment or decrement them, and use dereferencing and member access operators to get constant
 +access to node/attribute (the constness of iterators may change with the introducing of mutable trees).</p>
 +
 +<p>In order to get the iterators, use corresponding functions of <b>xml_node</b>. Note that <b>_end()</b>
 +functions return past-the-end iterator, that is, in order to get the last attribute, you'll have to
 +do something like:
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +    <b><font color="#0000ff" >if</font></b> <font color="#000000" >(node.attributes_begin()</font> <font color="#000000" >!=</font> <font color="#000000" >node.attributes_end())</font> <i><font color="#808080" >// we have at least one attribute</font></i>
 +    <font color="#000000" >{</font>
 +        <font color="#000000" >xml_attribute</font> <font color="#000000" >last_attrib</font> <font color="#000000" >=</font> <font color="#000000" >*(--node.attributes_end());</font>
 +        <font color="#000000" >...</font>
 +    <font color="#000000" >}</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +</p>
 +
 +<a name="Doc_Misc">
 +<h3>Miscellaneous</h3>
 +
 +<p>If you want to traverse a subtree, you can use <b>traverse</b> function. There is a class
 +<b>xml_tree_walker</b>, which has some functions that you can override in order to get custom traversing
 +(the default one just does nothing).
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >bool</font></b> <font color="#000000" >begin(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&);</font>
 +        <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >bool</font></b> <font color="#000000" >end(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&);</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions are called when the processing of the node starts/ends. First <b>begin()</b>
 +is called, then all children of the node are processed recursively, then <b>end()</b> is called. If
 +any of these functions returns false, the traversing is stopped and the <b>traverse()</b> function
 +returns false.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >void</font></b> <font color="#000000" >push();</font>
 +        <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >void</font></b> <font color="#000000" >pop();</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>These functions are called before and after the processing of node's children. If node has no children,
 +none of these is called. The default behavior is to increment/decrement current node depth.</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >int</font></b> <font color="#000000" >depth()</font> <b><font color="#0000ff" >const</font></b><font color="#000000" >;</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>Get the current depth. You can use this function to do your own indentation, for example.</p>
 +
 +<p>Lets get to some minor notes. You can safely write something like:
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +        <b><font color="#0000ff" >bool</font></b> <font color="#000000" >value</font> <font color="#000000" >=</font> <font color="#000000" >node.child(</font><font color="#ff0000" >"stream"</font><font color="#000000" >).attribute(</font><font color="#ff0000" >"compress"</font><font color="#000000" >).as_bool();</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +If node has a child with the name 'geometry', and this child has an attribute 'compress', than everything
 +is ok. If node has a child with the name 'geometry' with no attribute 'compress', then attribute("compress")
 +will return xml_attribute(), and the corresponding call to as_bool() will return default value (false).
 +If there is no child node 'geometry', the child(...) call will return xml_node(), the subsequent call
 +to attribute(...) will return xml_attribute() (because there are no attributes belonging to invalid
 +node), and as_bool() will again return false, so this call sequence is perfectly safe.</p>
 +
 +<a name="Doc_Lifetime">
 +<h3>Lifetime issues and memory management</h3>
 +
 +<p>As parsing is done in-situ, the XML data is to persist during the lifetime of <b>xml_parser</b>. If
 +the parsing is called via a function of <b>xml_parser</b>, that accepts <b>char*</b>, you have to ensure
 +yourself, that the string will outlive the <b>xml_parser</b> object.</p>
 +
 +<p>The memory for nodes and attributes is allocated in blocks of data (the blocks form a linked list;
 +the default size of the block is 32 kb, though you can change it via changing a <b>memory_block_size</b>
 +constant in <b>pugixml.hpp</b> file. Remember that the first block is allocated on stack (it resides
 +inside <b>xml_parser</b> object), and all subsequent blocks are allocated on heap, so expect a stack overflow
 +when setting too large memory block size), so the <b>xml_parser</b> object (which contains the blocks)
 +should outlive all <b>xml_node</b> and <b>xml_attribute</b> objects (as well as iterators), which belong
 +to the parser's tree. Again, you should ensure it yourself.</p>
 +
 +<hr>
 +
 +<a name="Example">
 +<h2>Example</h2>
 +
 +<p>Ok, so you are not much of documentation reader, are you? So am I. Let's assume that you're going
 +to parse an xml file... something like this:
 +
 +<pre>
 +<span style='color:#004a43; '><?</span><span style='color:#004a43; '>xml</span> <span style='color:#004a43; '>version</span><span style='color:#808030; '>=</span><span style='color:#008c00; '>"1.0"</span> <span style='color:#004a43; '>encoding</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"UTF-8"</span><span style='color:#004a43; '>?></span>
 +<span style='color:#a65700; '><</span><span style='color:#5f5035; '>mesh</span> <span style='color:#274796; '>name</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>Cathedral</span><span style='color:#0000e6; '>"</span><span style='color:#a65700; '>></span>
 +    <span style='color:#a65700; '><</span><span style='color:#5f5035; '>fragment</span> <span style='color:#274796; '>name</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>Cathedral</span><span style='color:#0000e6; '>"</span><span style='color:#a65700; '>></span>    
 +        <span style='color:#a65700; '><</span><span style='color:#5f5035; '>geometry</span><span style='color:#a65700; '>></span>
 +            <span style='color:#a65700; '><</span><span style='color:#5f5035; '>stream</span> <span style='color:#274796; '>usage</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>main</span><span style='color:#0000e6; '>"</span> <span style='color:#274796; '>source</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>StAnna.dmesh</span><span style='color:#0000e6; '>"</span> <span style='color:#274796; '>compress</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>true</span><span style='color:#0000e6; '>"</span> <span style='color:#a65700; '>/></span>
 +            <span style='color:#a65700; '><</span><span style='color:#5f5035; '>stream</span> <span style='color:#274796; '>usage</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>ao</span><span style='color:#0000e6; '>"</span> <span style='color:#274796; '>source</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>StAnna.ao</span><span style='color:#0000e6; '>"</span> <span style='color:#a65700; '>/></span>
 +        <span style='color:#a65700; '></</span><span style='color:#5f5035; '>geometry</span><span style='color:#a65700; '>></span>
 +    <span style='color:#a65700; '></</span><span style='color:#5f5035; '>fragment</span><span style='color:#a65700; '>></span>
 +    <span style='color:#a65700; '><</span><span style='color:#5f5035; '>fragment</span> <span style='color:#274796; '>name</span><span style='color:#808030; '>=</span><span style='color:#0000e6; '>"</span><span style='color:#0000e6; '>Cathedral</span><span style='color:#0000e6; '>"</span><span style='color:#a65700; '>></span>    
 +    	...
 +    <span style='color:#a65700; '></</span><span style='color:#5f5035; '>fragment</span><span style='color:#a65700; '>></span>
 +	...
 +<span style='color:#a65700; '></</span><span style='color:#5f5035; '>mesh</span><span style='color:#a65700; '>></span>
 +</pre>
 +
 +<p><mesh> is a root node, it has 0 or more <fragment>s, each of them has a <geometry>
 +node, and there are <stream> nodes with the shown attributes. We'd like to parse the file and...
 +well, and do something with it's contents. There are several methods of doing that; I'll show 2 of them
 +(the remaining one is using iterators).</p>
 +
 +<p>Here we exploit the knowledge of the strict hierarchy of our XML document and read the nodes from
 +DOM tree accordingly. When we have an <b>xml_node</b> object, we can get the desired information from
 +it (name, value, attributes list, nearby nodes in a tree - siblings, parent and children).</p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">
 +<font color="#008000" >#include</font> <font color="#ff0000" ><fstream></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><vector></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><algorithm></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><iterator></font>
 +
 +<font color="#008000" >#include</font> <font color="#ff0000" >"pugixml.hpp"</font>
 +
 +<b><font color="#0000ff" >using</font></b> <b><font color="#0000ff" >namespace</font></b> <font color="#000000" >pugi;</font>
 +
 +<b><font color="#0000ff" >int</font></b> <font color="#000000" >main()</font>
 +<font color="#000000" >{</font>
 +    <font color="#000000" >std::ifstream</font> <font color="#000000" >in(</font><font color="#ff0000" >"mesh.xml"</font><font color="#000000" >);</font>
 +    <font color="#000000" >in.unsetf(std::ios::skipws);</font>
 +                
 +    <font color="#000000" >std::vector<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >></font> <font color="#000000" >buf;</font>
 +    <font color="#000000" >std::copy(std::istream_iterator<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >>(in),</font> <font color="#000000" >std::istream_iterator<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >>(),</font> <font color="#000000" >std::back_inserter(buf));</font>
 +    <font color="#000000" >buf.push_back(</font><b><font color="#40b440" >0</font></b><font color="#000000" >);</font> <i><font color="#808080" >// zero-terminate</font></i>
 +    
 +    <font color="#000000" >xml_parser</font> <font color="#000000" >parser(&buf[</font><b><font color="#40b440" >0</font></b><font color="#000000" >],</font> <font color="#000000" >pugi::parse_w3c);</font>
 +
 +    <font color="#000000" >xml_node</font> <font color="#000000" >doc</font> <font color="#000000" >=</font> <font color="#000000" >parser.document();</font>
 +        
 +    <b><font color="#0000ff" >if</font></b> <font color="#000000" >(xml_node</font> <font color="#000000" >mesh</font> <font color="#000000" >=</font> <font color="#000000" >doc.first_element(</font><font color="#ff0000" >"mesh"</font><font color="#000000" >))</font>
 +    <font color="#000000" >{</font>
 +        <i><font color="#808080" >// store mesh.attribute("name").value()</font></i>
 +
 +        <b><font color="#0000ff" >for</font></b> <font color="#000000" >(xml_node</font> <font color="#000000" >fragment</font> <font color="#000000" >=</font> <font color="#000000" >mesh.first_element(</font><font color="#ff0000" >"fragment"</font><font color="#000000" >);</font> <font color="#000000" >fragment;</font> <font color="#000000" >fragment</font> <font color="#000000" >=</font> <font color="#000000" >fragment.next_sibling())</font>
 +        <font color="#000000" >{</font>
 +            <i><font color="#808080" >// store fragment.attribute("name").value()</font></i>
 +    
 +            <b><font color="#0000ff" >if</font></b> <font color="#000000" >(xml_node</font> <font color="#000000" >geometry</font> <font color="#000000" >=</font> <font color="#000000" >fragment.first_element(</font><font color="#ff0000" >"geometry"</font><font color="#000000" >))</font>
 +                <b><font color="#0000ff" >for</font></b> <font color="#000000" >(xml_node</font> <font color="#000000" >stream</font> <font color="#000000" >=</font> <font color="#000000" >geometry.first_element(</font><font color="#ff0000" >"stream"</font><font color="#000000" >);</font> <font color="#000000" >stream;</font> <font color="#000000" >stream</font> <font color="#000000" >=</font> <font color="#000000" >stream.next_sibling())</font>
 +                <font color="#000000" >{</font>
 +                    <i><font color="#808080" >// store stream.attribute("usage").value()</font></i>
 +                    <i><font color="#808080" >// store stream.attribute("source").value()</font></i>
 +                    
 +                    <b><font color="#0000ff" >if</font></b> <font color="#000000" >(stream.attribute(</font><font color="#ff0000" >"compress"</font><font color="#000000" >))</font>
 +                        <i><font color="#808080" >// store stream.attribute("compress").as_bool()</font></i>
 +    
 +                <font color="#000000" >}</font>
 +        <font color="#000000" >}</font>
 +    <font color="#000000" >}</font>
 +<font color="#000000" >}</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<p>We can also write a class that will traverse the DOM tree and store the information from nodes based
 +on their names, depths, attributes, etc. This way is well known by the users of SAX parsers. To do that,
 +we have to write an implementation of <b>xml_tree_walker</b> interface </p>
 +
 +<table width = "100%" bgcolor="#e6e6e6"><tr><td><pre><font color="white">                   
 +<font color="#008000" >#include</font> <font color="#ff0000" ><fstream></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><vector></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><algorithm></font>
 +<font color="#008000" >#include</font> <font color="#ff0000" ><iterator></font>
 +
 +<font color="#008000" >#include</font> <font color="#ff0000" >"pugixml.hpp"</font>
 +
 +<b><font color="#0000ff" >using</font></b> <b><font color="#0000ff" >namespace</font></b> <font color="#000000" >pugi;</font>
 +
 +<b><font color="#0000ff" >struct</font></b> <font color="#000000" >mesh_parser:</font> <b><font color="#0000ff" >public</font></b> <font color="#000000" >xml_tree_walker</font>
 +<font color="#000000" >{</font>
 +    <b><font color="#0000ff" >virtual</font></b> <b><font color="#0000ff" >bool</font></b> <font color="#000000" >begin(</font><b><font color="#0000ff" >const</font></b> <font color="#000000" >xml_node&</font> <font color="#000000" >node)</font>
 +    <font color="#000000" >{</font>
 +        <b><font color="#0000ff" >if</font></b> <font color="#000000" >(strcmp(node.name(),</font> <font color="#ff0000" >"mesh"</font><font color="#000000" >)</font> <font color="#000000" >==</font> <b><font color="#40b440" >0</font></b><font color="#000000" >)</font>
 +        <font color="#000000" >{</font>
 +            <i><font color="#808080" >// store node.attribute("name").value()</font></i>
 +        <font color="#000000" >}</font>
 +        <b><font color="#0000ff" >else</font></b> <b><font color="#0000ff" >if</font></b> <font color="#000000" >(strcmp(node.name(),</font> <font color="#ff0000" >"fragment"</font><font color="#000000" >)</font> <font color="#000000" >==</font> <b><font color="#40b440" >0</font></b><font color="#000000" >)</font>
 +        <font color="#000000" >{</font>
 +            <i><font color="#808080" >// store node.attribute("name").value()</font></i>
 +        <font color="#000000" >}</font>   
 +        <b><font color="#0000ff" >else</font></b> <b><font color="#0000ff" >if</font></b> <font color="#000000" >(strcmp(node.name(),</font> <font color="#ff0000" >"geometry"</font><font color="#000000" >)</font> <font color="#000000" >==</font> <b><font color="#40b440" >0</font></b><font color="#000000" >)</font>
 +        <font color="#000000" >{</font>
 +            <i><font color="#808080" >// ...</font></i>
 +        <font color="#000000" >}</font>
 +        <b><font color="#0000ff" >else</font></b> <b><font color="#0000ff" >if</font></b> <font color="#000000" >(strcmp(node.name(),</font> <font color="#ff0000" >"stream"</font><font color="#000000" >)</font> <font color="#000000" >==</font> <b><font color="#40b440" >0</font></b><font color="#000000" >)</font>
 +        <font color="#000000" >{</font>
 +            <i><font color="#808080" >// store node.attribute("usage").value()</font></i>
 +            <i><font color="#808080" >// store node.attribute("source").value()</font></i>
 +                    
 +            <b><font color="#0000ff" >if</font></b> <font color="#000000" >(node.attribute(</font><font color="#ff0000" >"compress"</font><font color="#000000" >))</font>
 +                <i><font color="#808080" >// store stream.attribute("compress").as_bool()</font></i>
 +        <font color="#000000" >}</font>
 +        <b><font color="#0000ff" >else</font></b> <b><font color="#0000ff" >return</font></b> <b><font color="#0000ff" >false</font></b><font color="#000000" >;</font>
 +
 +        <b><font color="#0000ff" >return</font></b> <b><font color="#0000ff" >true</font></b><font color="#000000" >;</font>
 +    <font color="#000000" >}</font>
 +<font color="#000000" >};</font>
 +
 +<b><font color="#0000ff" >int</font></b> <font color="#000000" >main()</font>
 +<font color="#000000" >{</font>
 +    <font color="#000000" >std::ifstream</font> <font color="#000000" >in(</font><font color="#ff0000" >"mesh.xml"</font><font color="#000000" >);</font>
 +    <font color="#000000" >in.unsetf(std::ios::skipws);</font>
 +                
 +    <font color="#000000" >std::vector<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >></font> <font color="#000000" >buf;</font>
 +    <font color="#000000" >std::copy(std::istream_iterator<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >>(in),</font> <font color="#000000" >std::istream_iterator<</font><b><font color="#0000ff" >char</font></b><font color="#000000" >>(),</font> <font color="#000000" >std::back_inserter(buf));</font>
 +    <font color="#000000" >buf.push_back(</font><b><font color="#40b440" >0</font></b><font color="#000000" >);</font> <i><font color="#808080" >// zero-terminate</font></i>
 +    
 +    <font color="#000000" >xml_parser</font> <font color="#000000" >parser(&buf[</font><b><font color="#40b440" >0</font></b><font color="#000000" >],</font> <font color="#000000" >pugi::parse_w3c);</font>
 +
 +    <font color="#000000" >mesh_parser</font> <font color="#000000" >mp;</font>
 +
 +    <b><font color="#0000ff" >if</font></b> <font color="#000000" >(!parser.document().traverse(mp))</font>
 +        <i><font color="#808080" >// generate an error</font></i>
 +<font color="#000000" >}</font>
 +</font></pre></td></tr><tr><td align="right"><b><i><a href="http://dobrokot.nm.ru/WinnieColorizer.html"><font color="#666666">_Winnie C++ Colorizer</font></a></i></b></td></tr></table>
 +
 +<hr>
 +
 +<a name="Parsing">
 +<h2>Parsing process</h2>
 +
 +<p>So, let's talk a bit about parsing process, and about the reason for providing XML data as a contiguous
 +writeable block of memory. Parsing is done in-situ. This means, that the strings, representing the
 +parts of DOM tree (node names, attribute names and values, CDATA content, etc.) are not separately
 +allocated on heap, but instead are parts of the original data. This is the keypoint to parsing speed,
 +because it helps achieve the minimal amount of memory allocations (more on that below) and minimal
 +amount of copying data.</p>
 +
 +<p>In-situ parsing can be done in two ways, with zero-segmenting the string (that is, set the past-the-end
 +character for the part of XML string to 0, see <a href="http://www.codeproject.com/soap/pugxml/pugxml_steps.gif">
 +this image</a> for further details), and storing pointer + size of the string instead of pointer to
 +the beginning of ASCIIZ string.</p>
 +
 +<p>Originally, <i>pugxml</i> had only the first way, but then authors added the second method, 'non-segmenting'
 +or non-destructive parsing. The advantages of this method are: you no longer need non-constant storage;
 +you can even read data from memory-mapped files directly. Well, there are disadvantages.
 +For one thing, you can not do any of the transformations in-situ. The transformations that are required
 +by XML standard are:
 +<ul>
 +<li>End of line handling (replacing 0x0d 0x0a with 0x0a and any standalone 0x0d with 0x0a) (for the whole
 +document)</li>
 +<li>White space normalization for attribute values (converting space-like characters to spaces (0x20),
 +sometimes trimming leading/trailing spaces and converting sequences of spaces to a single space</li>
 +<li>Character reference expansion (&lt; and alike, <#0a; and alike, <40; and alike)</li>
 +<li>Entity reference expansion (&entityname;)</li>
 +</ul>
 +
 +None of these can be done in-situ. <i>pugxml</i> did neither character nor entity reference expansion,
 +and allocated new memory when normalizing white spaces when in non-destructive mode. I chose complete
 +in-situ parsing (the good thing about it is that any transformation, except entity reference, can be
 +done in-situ because it does not increase the amount of characters - even converting a character
 +reference to UTF-8). There is no entity reference expansion because of this and because I do not want
 +to parse DOCTYPE and, moreover, use DOCTYPE in following parsing (performing selective whitespace
 +normalization in attributes and CDATA sections and so on).</p>
 +
 +<p>In order to be able to modify the tree (change attribute/node names & values) with in-situ parsing,
 +one needs to implement two ways of storing data (both in-situ and not). The DOM tree is now mutable,
 +but it will change in the future releases (without introducing speed/memory overhead, except on clean-
 +up stage).</p>
 +
 +<p>The parsing process itself is more or less straightforward, when you see it - but the impression
 +is fake, because the explicit jumps are made (i.e. we know, that if we come to a closing brace (>),
 +we should expect CDATA after it (or a new tag), so let's just jump to the corresponding code), and,
 +well, there can be bugs (see <a href="#Bugs">Bugs</a> section).</p>
 +
 +<p>And, to make things worse, memory allocation (which is done only for node and attribute structures)
 +is done in pools. The pools are single-linked lists with predefined block size (32 kb by default), and
 +well, it increases speed a lot (allocations are slow, and the memory gets fragmented when allocating
 +a bunch of 16-byte (attribute) or 40-byte (node) structures)</p>
 +
 +<hr>
 +
 +<a name="Compliance">
 +<h2>W3C compliance</h2>
 +
 +<p><i>pugixml</i> is not a compliant XML parser. The main reason for that is that it does not reject
 +most malformed XML files. The more or less complete list of incompatibilities follows (I will be talking
 +of ones when using <b>parse_w3c</b> mode):
 +
 +<ul>
 +<li>The parser is completely DOCTYPE-ignorant, that is, it does not even skip all possible DOCTYPEs
 +correctly, let alone use them for parsing
 +<li>It accepts multiple attributes with the same name in one node
 +<li>It is charset-ignorant
 +<li>It accepts invalid names of tags
 +<li>It accepts invalid attribute values (those with < in them) and does not reject invalid entity
 +references or character references (in fact, it does not do DOCTYPE parsing, so it does not perform
 +entity reference expansion)
 +<li>It does not reject comments with -- inside
 +<li>It does not reject PI with the names of 'xml' and alike; in fact, it parses prolog as a PI, which
 +is not conformant
 +<li>All characters from #x1 to #x20 are considered to be whitespaces
 +<li>And some other things that I forgot to mention
 +</ul>
 +
 +In short, it accepts most malformed XML files and does not do anything that is related to DOCTYPE.
 +This is because the main goal was developing fast, easy-to-use and error ignorant (so you can always
 +get something even from a malformed document) parser, there are some good validating and conformant
 +parsers already.</p>
 +
 +<hr>
 +
 +<a name="FAQ">
 +<h2>FAQ</h2>
 +
 +<p>I'm always open for questions; feel free to write them to <a href="mailto:zeux@mathcentre.com">zeux@mathcentre.com</a>.
 +</p>
 +
 +<hr>
 +
 +<a name="Bugs">
 +<h2>Bugs</h2>
 +
 +<p>I'm always open for bug reports; feel free to write them to <a href="mailto:zeux@mathcentre.com">zeux@mathcentre.com</a>.
 +Please provide as much information as possible - version of <i>pugixml</i>, compiling and OS environment
 +(compiler and it's version, STL version, OS version, etc.), the description of the situation in which
 +the bug arises, the code and data files that show the bug, etc. - the more, the better. Though, please,
 +do not send executable files.</p>
 +
 +<hr>
 +
 +<a name="Future_work">
 +<h2>Future work</h2>
 +
 +<p>Here are some improvements that will be done in future versions (they are sorted by priority, the
 +upper ones will get there sooner).</p>
 +
 +<ul>
 +<li>Support for altering the tree (both changing nodes'/attributes' names and values and adding/deleting
 +attributes/nodes) and writing the tree to stream
 +<li>Support for UTF-16 files (parsing BOM to get file's type and converting UTF-16 file to UTF-8 buffer
 +if necessary)
 +<li>Improved API (I'm going to look at SelectNode from MS XML and perhaps there will be some other
 +changes)
 +<li>Externally provided entity reference table (or perhaps even taken from DOCTYPE?)
 +<li>More intelligent parsing of DOCTYPE (it does not always skip DOCTYPE for now)
 +<li>XML 1.1 changes (changed EOL handling, normalization issues, 
 +<li>XPath support
 +<li>Name your own?
 +</ul>
 +
 +<hr>
 +
 +<a name="Changelog">
 +<h2>Changelog</h2>
 +
 +<dl>
 +<dt>15.07.2006 - v0.1
 +<dd>First private release for testing purposes
 +</dt>
 +</dl>
 +
 +<hr>
 +
 +<a name="Acknowledgements">
 +<h2>Acknowledgements</h2>
 +
 +<ul>
 +<li><a href="mailto:kristen@tima.net">Kristen Wegner</a> for <i>pugxml</i> parser
 +<li><a href="mailto:readonly@getsoft.com">Neville Franks</a> for contributions to <i>pugxml</i> parser
 +</ul>
 +
 +<hr>
 +
 +<a name="License">
 +<h2>License</h2>
 +
 +<p>The <i>pugixml</i> parser is released into the public domain (though this may change).</p>
 +
 +<hr>
 +
 +<p>Revised 15 July, 2006</p>
 +<p><i>© Copyright <a href="mailto:zeux@mathcentre.com">Zeux</a> 2006. All Rights Reserved.</i></p>
 +</body>
 +</html>
 diff --git a/docs/tree.png b/docs/tree.pngBinary files differ new file mode 100644 index 0000000..14d48d6 --- /dev/null +++ b/docs/tree.png diff --git a/src/pugixml.cpp b/src/pugixml.cpp new file mode 100644 index 0000000..1018bda --- /dev/null +++ b/src/pugixml.cpp @@ -0,0 +1,1868 @@ +///////////////////////////////////////////////////////////////////////////////
 +//
 +// Pug Improved XML Parser - Version 0.1
 +// --------------------------------------------------------
 +// Copyright (C) 2006, by Zeux (zeux@mathcentre.com)
 +// This work is based on the pugxml parser, which is:
 +// Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
 +// Released into the Public Domain. Use at your own risk.
 +// See pugxml.xml for further information, history, etc.
 +// Contributions by Neville Franks (readonly@getsoft.com).
 +//
 +///////////////////////////////////////////////////////////////////////////////
 +
 +#include <vector>
 +#include <string>
 +
 +#include "pugixml.hpp"
 +
 +namespace pugi
 +{
 +	/// A 'name=value' XML attribute structure.
 +	struct xml_attribute_struct
 +	{
 +		/// Default ctor
 +		xml_attribute_struct();
 +
 +		char*		name;			///< Pointer to attribute name.
 +		char*		value;			///< Pointer to attribute value.
 +
 +		xml_attribute_struct* prev_attribute;	///< Previous attribute
 +		xml_attribute_struct* next_attribute;	///< Next attribute
 +	};
 +
 +	/// An XML document tree node.
 +	struct xml_node_struct
 +	{
 +		/// Default ctor
 +		/// \param type - node type
 +		xml_node_struct(xml_node_type type = node_element);
 +
 +		xml_node_type			type;					///< Node type; see xml_node_type.
 +		xml_node_struct*		parent;					///< Pointer to parent
 +
 +		char*					name;					///< Pointer to element name.
 +		char*					value;					///< Pointer to any associated string data.
 +
 +		xml_node_struct*		first_child;			///< First child
 +		xml_node_struct*		last_child;				///< Last child
 +		
 +		xml_node_struct*		prev_sibling;			///< Left brother
 +		xml_node_struct*		next_sibling;			///< Right brother
 +		
 +		xml_attribute_struct*	first_attribute;		///< First attribute
 +		xml_attribute_struct*	last_attribute;			///< Last attribute
 +	};
 +
 +	class xml_allocator
 +	{
 +	public:
 +		xml_allocator(xml_memory_block* root): _root(root), _count(0)
 +		{
 +		}
 +
 +		template <typename T> T* allocate()
 +		{
 +			void* buf = memalloc(sizeof(T));
 +			return new (buf) T();
 +		}
 +		
 +		template <typename T, typename U> T* allocate(U val)
 +		{
 +			void* buf = memalloc(sizeof(T));
 +			return new (buf) T(val);
 +		}
 +
 +	private:
 +		xml_memory_block* _root;
 +		unsigned int _count;
 +
 +		void* memalloc(size_t size)
 +		{
 +			if (_root->size + size <= memory_block_size)
 +			{
 +				void* buf = _root->data + _root->size;
 +				_root->size += size;
 +				return buf;
 +			}
 +			else
 +			{
 +				_root->next = new xml_memory_block();
 +				_root = _root->next;
 +
 +				_root->size = size;
 +
 +				++_count;
 +
 +				return _root->data;
 +			}
 +		}
 +	};
 +}
 +
 +namespace
 +{
 +	using namespace pugi;
 +	
 +	namespace utf8
 +	{
 +		const unsigned char BYTE_MASK = 0xBF;
 +		const unsigned char BYTE_MARK = 0x80;
 +		const unsigned char BYTE_MASK_READ = 0x3F;
 +		const unsigned char FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 +	}
 +
 +	// Get the size that is needed for strutf16_utf8 applied to all s characters
 +	size_t strutf16_utf8_size(const wchar_t* s)
 +	{
 +		size_t length = 0;
 +
 +		for (; *s; ++s)
 +		{
 +			if (*s < 0x80) length += 1;
 +			else if (*s < 0x800) length += 2;
 +			else if (*s < 0x10000) length += 3;
 +			else if (*s < 0x200000) length += 4;
 +		}
 +
 +		return length;
 +	}
 +
 +	// Write utf16 char to stream, return position after the last written char
 +	// \param s - pointer to string
 +	// \param ch - char
 +	// \return position after the last char
 +	// \rem yes, this is trom TinyXML. How would you write it the other way, without switch trick?..
 +	char* strutf16_utf8(char* s, unsigned int ch)
 +	{
 +		unsigned int length;
 +
 +		if (ch < 0x80) length = 1;
 +		else if (ch < 0x800) length = 2;
 +		else if (ch < 0x10000) length = 3;
 +		else if (ch < 0x200000) length = 4;
 +		else return s;
 +	
 +		s += length;
 +
 +		// Scary scary fall throughs.
 +		switch (length) 
 +		{
 +			case 4:
 +				*--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK); 
 +				ch >>= 6;
 +			case 3:
 +				*--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK); 
 +				ch >>= 6;
 +			case 2:
 +				*--s = (char)((ch | utf8::BYTE_MARK) & utf8::BYTE_MASK); 
 +				ch >>= 6;
 +			case 1:
 +				*--s = (char)(ch | utf8::FIRST_BYTE_MARK[length]);
 +		}
 +		
 +		return s + length;
 +	}
 +
 +	// Get the size that is needed for strutf8_utf16 applied to all s characters
 +	size_t strutf8_utf16_size(const char* s)
 +	{
 +		size_t length = 0;
 +
 +		for (; *s; ++s)
 +		{
 +			if (*s < 0x80 || (*s >= 0xC0 && *s < 0xFC)) ++length;
 +		}
 +
 +		return length;
 +	}
 +
 +	// Read utf16 char from utf8 stream, return position after the last read char
 +	// \param s - pointer to string
 +	// \param ch - char
 +	// \return position after the last char
 +	const char* strutf8_utf16(const char* s, unsigned int& ch)
 +	{
 +		unsigned int length;
 +
 +		const unsigned char* str = reinterpret_cast<const unsigned char*>(s);
 +
 +		if (*str < utf8::BYTE_MARK)
 +		{
 +			ch = *str;
 +			return s + 1;
 +		}
 +		else if (*str < 0xC0)
 +		{
 +			ch = ' ';
 +			return s + 1;
 +		}
 +		else if (*str < 0xE0) length = 2;
 +		else if (*str < 0xF0) length = 3;
 +		else if (*str < 0xF8) length = 4;
 +		else if (*str < 0xFC) length = 5;
 +		else
 +		{
 +			ch = ' ';
 +			return s + 1;
 +		}
 +
 +		ch = (*str++ & ~utf8::FIRST_BYTE_MARK[length]);
 +	
 +		// Scary scary fall throughs.
 +		switch (length) 
 +		{
 +			case 5:
 +				ch <<= 6;
 +				ch += (*str++ & utf8::BYTE_MASK_READ);
 +			case 4:
 +				ch <<= 6;
 +				ch += (*str++ & utf8::BYTE_MASK_READ);
 +			case 3:
 +				ch <<= 6;
 +				ch += (*str++ & utf8::BYTE_MASK_READ);
 +			case 2:
 +				ch <<= 6;
 +				ch += (*str++ & utf8::BYTE_MASK_READ);
 +		}
 +		
 +		return reinterpret_cast<const char*>(str);
 +	}
 +
 +	struct xml_parser_impl
 +	{
 +		xml_allocator& alloc;
 +		bool chartype_symbol_table[256];
 +
 +		bool chartype_symbol(char c) { return chartype_symbol_table[(unsigned char)c]; }
 +		
 +		static bool chartype_space(char c) { return c < '!' && c > 0; }
 +		static bool chartype_enter(char c) { return c == '<'; }
 +		static bool chartype_leave(char c) { return c == '>'; }
 +		static bool chartype_close(char c)	{ return c == '/'; }
 +		static bool chartype_equals(char c) { return c == '='; }
 +		static bool chartype_special(char c) { return c == '!'; }
 +		static bool chartype_pi(char c) { return c == '?'; }
 +		static bool chartype_dash(char c) { return c == '-'; }
 +		static bool chartype_quote(char c) { return c == '"' || c == '\''; }
 +		static bool chartype_lbracket(char c) { return c == '['; }
 +		static bool chartype_rbracket(char c) { return c == ']'; }
 +
 +		template <bool opt_trim, bool opt_escape, bool opt_wnorm, bool opt_wconv> static void strconv_t(char** s)
 +		{
 +			if (!s || !*s) return;
 +
 +			// Trim whitespaces
 +			if (opt_trim) while (chartype_space(**s)) ++(*s);
 +
 +			char* str = *s;
 +			char* lastpos = str;
 +
 +			if (!*str) return;
 +
 +			while (*str)
 +			{
 +				if (*str == '&' && opt_escape)	// &
 +				{
 +					char* stre = str + 1;
 +
 +					switch (*stre)
 +					{
 +						case '#':	// &#...
 +						{
 +							unsigned int ucsc = 0;
 +
 +							++stre;
 +
 +							if (*stre == 'x') // &#x... (hex code)
 +							{
 +								++stre;
 +								
 +								while (*stre)
 +								{
 +									if (*stre >= '0' && *stre <= '9')
 +										ucsc = 16 * ucsc + (*stre++ - '0');
 +									else if (*stre >= 'A' && *stre <= 'F')
 +										ucsc = 16 * ucsc + (*stre++ - 'A' + 10);
 +									else if (*stre >= 'a' && *stre <= 'f')
 +										ucsc = 16 * ucsc + (*stre++ - 'a' + 10);
 +									else break;
 +								}
 +
 +								if (*stre == ';') ++stre;
 +							}
 +							else	// &#... (dec code)
 +							{
 +								while (*stre >= '0' && *stre <= '9')
 +									ucsc = 10 * ucsc + (*stre++ - '0');
 +
 +								if (*stre == ';') ++stre;
 +							}
 +
 +							str = stre;
 +							lastpos = strutf16_utf8(lastpos, ucsc);
 +							continue;
 +						}
 +						case 'a':	// &a
 +						{
 +							++stre;
 +
 +							if (*stre == 'm') // &am
 +							{
 +								if (*++stre == 'p' && *++stre == ';') // &
 +								{
 +									*lastpos++ = '&';
 +									str = ++stre;
 +									continue;
 +								}
 +							}
 +							else if (*stre == 'p') // &ap
 +							{
 +								if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // '
 +								{
 +									*lastpos++ = '\'';
 +									str = ++stre;
 +									continue;
 +								}
 +							}
 +							break;
 +						}
 +						case 'g': // &g
 +						{
 +							if (*++stre == 't' && *++stre == ';') // >
 +							{
 +								*lastpos++ = '>';
 +								str = ++stre;
 +								continue;
 +							}
 +							break;
 +						}
 +						case 'l': // &l
 +						{
 +							if (*++stre == 't' && *++stre == ';') // <
 +							{
 +								*lastpos++ = '<';
 +								str = ++stre;
 +								continue;
 +							}
 +							break;
 +						}
 +						case 'q': // &q
 +						{
 +							if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // "
 +							{
 +								*lastpos++ = '"';
 +								str = ++stre;
 +								continue;
 +							}
 +							break;
 +						}
 +					}
 +				}
 +				else if (chartype_space(*str) && opt_wconv)
 +				{
 +					if (*str == 0x0d && *(str + 1) == 0x0a) ++str;
 +
 +					++str;
 +					*lastpos++ = ' ';
 +
 +					continue;
 +				}
 +				else if (*str == 0x0d && !opt_wnorm)
 +				{
 +					if (*(str + 1) == 0x0a) ++str;
 +					++str;
 +					*lastpos++ = 0x0a;
 +
 +					continue;
 +				}
 +				else if (chartype_space(*str) && opt_wnorm)
 +				{
 +					*lastpos++ = ' ';
 +		
 +					while (chartype_space(*str)) ++str;
 +
 +					continue;
 +				}
 +				
 +				*lastpos++ = *str++;
 +			}
 +
 +			if (opt_trim)
 +			{
 +				do *lastpos-- = 0;
 +				while (chartype_space(*lastpos));
 +			}
 +			else *lastpos = 0;
 +		}
 +
 +		static void strconv_setup(void (*&func)(char**), unsigned int opt_trim, unsigned int opt_escape, unsigned int opt_wnorm, unsigned int opt_wconv)
 +		{
 +			if (opt_wconv)
 +			{
 +				if (opt_trim)
 +				{
 +					if (opt_escape)
 +					{
 +						if (opt_wnorm) func = &strconv_t<true, true, true, true>;
 +						else func = &strconv_t<true, true, false, true>;
 +					}
 +					else
 +					{
 +						if (opt_wnorm) func = &strconv_t<true, false, true, true>;
 +						else func = &strconv_t<true, false, false, true>;
 +					}
 +				}
 +				else
 +				{
 +					if (opt_escape)
 +					{
 +						if (opt_wnorm) func = &strconv_t<false, true, true, true>;
 +						else func = &strconv_t<false, true, false, true>;
 +					}
 +					else
 +					{
 +						if (opt_wnorm) func = &strconv_t<false, false, true, true>;
 +						else func = &strconv_t<false, false, false, true>;
 +					}
 +				}
 +			}
 +			else
 +			{
 +				if (opt_trim)
 +				{
 +					if (opt_escape)
 +					{
 +						if (opt_wnorm) func = &strconv_t<true, true, true, false>;
 +						else func = &strconv_t<true, true, false, false>;
 +					}
 +					else
 +					{
 +						if (opt_wnorm) func = &strconv_t<true, false, true, false>;
 +						else func = &strconv_t<true, false, false, false>;
 +					}
 +				}
 +				else
 +				{
 +					if (opt_escape)
 +					{
 +						if (opt_wnorm) func = &strconv_t<false, true, true, false>;
 +						else func = &strconv_t<false, true, false, false>;
 +					}
 +					else
 +					{
 +						if (opt_wnorm) func = &strconv_t<false, false, true, false>;
 +						else func = &strconv_t<false, false, false, false>;
 +					}
 +				}
 +			}
 +		}
 +		// Allocate & append a new xml_node_struct onto the given parent.
 +		// \param parent - pointer to parent node.
 +		// \param type - desired node type.
 +		// \return pointer to the new node
 +		xml_node_struct* append_node(xml_node_struct* parent, xml_node_type type = node_element)
 +		{
 +			if(!parent) return NULL; // Must have a parent.
 +
 +			xml_node_struct* child = alloc.allocate<xml_node_struct>(type); // Allocate a new child.
 +			child->parent = parent; // Set it's parent pointer.
 +			if (parent->last_child)
 +			{
 +				parent->last_child->next_sibling = child;
 +				child->prev_sibling = parent->last_child;
 +				parent->last_child = child;
 +			}
 +			else parent->first_child = parent->last_child = child;
 +			return child;
 +		}
 +
 +		// Allocate & append a new attribute to the given xml_node_struct.
 +		// \param node - pointer to parent node.
 +		// \return pointer to appended xml_attribute_struct.
 +		xml_attribute_struct* append_attribute(xml_node_struct* node)
 +		{
 +			if(!node) return NULL;
 +			xml_attribute_struct* a = alloc.allocate<xml_attribute_struct>();
 +
 +			if (node->last_attribute)
 +			{
 +				node->last_attribute->next_attribute = a;
 +				a->prev_attribute = node->last_attribute;
 +				node->last_attribute = a;
 +			}
 +			else node->first_attribute = node->last_attribute = a;
 +			
 +			return a;
 +		}
 +		
 +		// Parser utilities.
 +		#define SKIPWS()			{ while(chartype_space(*s)) ++s; if(*s==0) return s; }
 +		#define OPTSET(OPT)			( optmsk & OPT )
 +		#define PUSHNODE(TYPE)		{ cursor = append_node(cursor,TYPE); }
 +		#define POPNODE()			{ cursor = cursor->parent; }
 +		#define SCANFOR(X)			{ while(*s!=0 && !(X)) ++s; if(*s==0) return s; }
 +		#define SCANWHILE(X)		{ while((X)) ++s; if(*s==0) return s; }
 +		#define ENDSEG()			{ ch = *s; *s = 0; ++s; if(*s==0) return s; }
 +		
 +		xml_parser_impl(xml_allocator& alloc): alloc(alloc)
 +		{
 +			for (unsigned int c = 0; c < 256; ++c)
 +			{
 +				chartype_symbol_table[c] = c > 127 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
 +										(c >= '0' && c <= '9') || c == '_' || c == ':' || c == '-' || c == '.';
 +			}
 +		}
 +		
 +		// Static single-pass in-situ parse the given xml string.
 +		// \param s - pointer to XML-formatted string.
 +		// \param xmldoc - pointer to root.
 +		// \param optmsk - parse options mask.
 +		// \return last string position or null.
 +		char* parse(register char* s,xml_node_struct* xmldoc,unsigned int optmsk = parse_default)
 +		{
 +			if(!s || !xmldoc) return s;
 +
 +			void (*strconv_pcdata)(char**);
 +			void (*strconv_attribute)(char**);
 +
 +			strconv_setup(strconv_attribute, OPTSET(parse_trim_attribute), OPTSET(parse_escapes_attribute), OPTSET(parse_wnorm_attribute), OPTSET(parse_wconv_attribute));
 +			strconv_setup(strconv_pcdata, OPTSET(parse_trim_pcdata), OPTSET(parse_escapes_pcdata), OPTSET(parse_wnorm_pcdata), false);
 +
 +			char ch = 0; // Current char, in cases where we must null-terminate before we test.
 +			xml_node_struct* cursor = xmldoc; // Tree node cursor.
 +			char* mark = s; // Marked string position for temporary look-ahead.
 +			while(*s!=0)
 +			{
 +			LOC_SEARCH: // Obliviously search for next element.
 +				SCANFOR(chartype_enter(*s)); // Find the next '<'.
 +				if(chartype_enter(*s))
 +				{
 +					++s;
 +				LOC_CLASSIFY: // What kind of element?
 +					if(chartype_pi(*s)) // '<?...'
 +					{
 +						++s;
 +						if(chartype_symbol(*s) && OPTSET(parse_pi))
 +						{
 +							mark = s;
 +							SCANWHILE(chartype_symbol(*s)); // Read PI target
 +							ENDSEG();
 +							
 +							PUSHNODE(node_pi); // Append a new node on the tree.
 +
 +							cursor->name = mark;
 +
 +							if (chartype_space(ch))
 +							{
 +								SKIPWS();
 +
 +								mark = s;
 +							}
 +							else mark = 0;
 +
 +							SCANFOR(chartype_pi(*s) && chartype_leave(*(s+1))); // Look for '?>'.
 +							ENDSEG();
 +
 +							cursor->value = mark;
 +
 +							POPNODE();
 +
 +							goto LOC_LEAVE;
 +						}
 +						else // Bad PI or parse_pi not set.
 +						{
 +							SCANFOR(chartype_pi(*s) && chartype_leave(*(s+1))); // Look for '?>'.
 +							++s;
 +							goto LOC_LEAVE;
 +						}
 +					}
 +					else if(chartype_special(*s)) // '<!...'
 +					{
 +						++s;
 +						if(chartype_dash(*s)) // '<!-...'
 +						{
 +							++s;
 +							if(chartype_dash(*s)) // '<!--...'
 +							{
 +								++s;
 +								
 +								if (OPTSET(parse_comments))
 +								{
 +									PUSHNODE(node_comment); // Append a new node on the tree.
 +									cursor->value = s; // Save the offset.
 +								}
 +
 +								// Scan for terminating '-->'.
 +								SCANFOR(chartype_dash(*s) && chartype_dash(*(s+1)) && chartype_leave(*(s+2)));
 +								
 +								if (OPTSET(parse_comments))
 +								{
 +									*s = 0; // Zero-terminate this segment at the first terminating '-'.
 +									POPNODE(); // Pop since this is a standalone.
 +								}
 +								
 +								s += 2; // Step over the '\0-'.
 +								goto LOC_LEAVE; // Look for any following PCDATA.
 +							}
 +						}
 +						else if(chartype_lbracket(*s))
 +						{
 +							// '<![CDATA[...'
 +							if(*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && chartype_lbracket(*++s))
 +							{
 +								++s;
 +								if(OPTSET(parse_cdata))
 +								{
 +									PUSHNODE(node_cdata); // Append a new node on the tree.
 +									cursor->value = s; // Save the offset.
 +									// Scan for terminating ']]>'.
 +									SCANFOR(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)));
 +									ENDSEG(); // Zero-terminate this segment.
 +
 +									if (OPTSET(parse_eol_cdata))
 +									{
 +										strconv_t<false, false, false, false>(&cursor->value);
 +									}
 +
 +									POPNODE(); // Pop since this is a standalone.
 +								}
 +								else // Flagged for discard, but we still have to scan for the terminator.
 +								{
 +									// Scan for terminating ']]>'.
 +									SCANFOR(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)));
 +									++s;
 +								}
 +								++s; // Step over the last ']'.
 +								goto LOC_LEAVE; // Look for any following PCDATA.
 +							}
 +							continue; // Probably a corrupted CDATA section, so just eat it.
 +						}
 +						else if(*s=='D' && *++s=='O' && *++s=='C' && *++s=='T' && *++s=='Y' && *++s=='P' && *++s=='E')
 +						{
 +							++s;
 +							SKIPWS(); // Eat any whitespace.
 +						LOC_DOCTYPE:
 +							SCANWHILE(chartype_quote(*s) || chartype_lbracket(*s) || chartype_leave(*s));
 +							if(chartype_quote(*s)) // '...SYSTEM "..."
 +							{
 +								ch = *s++;
 +								SCANFOR(*s == ch);
 +								++s;
 +								goto LOC_DOCTYPE;
 +							}
 +							if(chartype_lbracket(*s)) // '...[...'
 +							{
 +								++s;
 +								unsigned int bd = 1; // Bracket depth counter.
 +								while(*s!=0) // Loop till we're out of all brackets.
 +								{
 +									if(chartype_rbracket(*s)) --bd;
 +									else if(chartype_lbracket(*s)) ++bd;
 +									if(bd == 0) break;
 +									++s;
 +								}
 +								// Note: 's' now points to end of DTD, i.e.: ']'.
 +								SCANFOR(chartype_leave(*s));
 +								continue;
 +							}
 +							// Fall-through
 +							continue;
 +						}
 +					}
 +					else if(chartype_symbol(*s)) // '<#...'
 +					{
 +						cursor = append_node(cursor); // Append a new node to the tree.
 +
 +						cursor->name = s;
 +						SCANWHILE(chartype_symbol(*s)); // Scan for a terminator.
 +						ENDSEG(); // Save char in 'ch', terminate & step over.
 +						if (*s!=0 && chartype_close(ch)) // '</...'
 +						{
 +							SCANFOR(chartype_leave(*s)); // Scan for '>'
 +							POPNODE(); // Pop.
 +							goto LOC_LEAVE;
 +						}
 +						else if(*s!=0 && !chartype_space(ch))
 +						{
 +							if (!chartype_leave(ch)) SCANWHILE(!chartype_leave(*s));
 +							if (!*s) return s;
 +							goto LOC_PCDATA; // No attributes, so scan for PCDATA.
 +						}
 +						else if(*s!=0 && chartype_space(ch))
 +						{
 +							SKIPWS(); // Eat any whitespace.
 +						LOC_ATTRIBUTE:
 +							if(chartype_symbol(*s)) // <... #...
 +							{
 +								xml_attribute_struct* a = append_attribute(cursor); // Make space for this attribute.
 +								a->name = s; // Save the offset.
 +								SCANWHILE(chartype_symbol(*s)); // Scan for a terminator.
 +								ENDSEG(); // Save char in 'ch', terminate & step over.
 +								if(*s!=0 && chartype_space(ch)) SKIPWS(); // Eat any whitespace.
 +								if(*s!=0 && (chartype_equals(ch) || chartype_equals(*s))) // '<... #=...'
 +								{
 +									if(chartype_equals(*s)) ++s;
 +									SKIPWS(); // Eat any whitespace.
 +									if(chartype_quote(*s)) // '<... #="...'
 +									{
 +										ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
 +										++s; // Step over the quote.
 +										a->value = s; // Save the offset.
 +										SCANFOR(*s == ch); // Scan for the terminating quote
 +										ENDSEG(); // Save char in 'ch', terminate & step over.
 +
 +										strconv_attribute(&a->value);
 +
 +										if(chartype_leave(*s))
 +										{
 +											++s;
 +											goto LOC_PCDATA;
 +										}
 +										else if(chartype_close(*s))
 +										{
 +											++s;
 +											POPNODE();
 +											SKIPWS(); // Eat any whitespace.
 +											goto LOC_LEAVE;
 +										}
 +										else if(chartype_space(*s)) // This may indicate a following attribute.
 +										{
 +											SKIPWS(); // Eat any whitespace.
 +											goto LOC_ATTRIBUTE; // Go scan for additional attributes.
 +										}
 +									}
 +								}
 +								goto LOC_ATTRIBUTE;
 +							}
 +
 +							SCANWHILE(!chartype_leave(*s) && !chartype_close(*s));
 +						}
 +					LOC_LEAVE:
 +						if(chartype_leave(*s)) // '...>'
 +						{
 +							++s; // Step over the '>'.
 +						LOC_PCDATA: // '>...<'
 +							mark = s; // Save this offset while searching for a terminator.
 +							SKIPWS(); // Eat whitespace if no genuine PCDATA here.
 +							// We hit a '<...', with only whitespace, so don't bother storing anything.
 + 							if((mark == s || !OPTSET(parse_ws_pcdata)) && chartype_enter(*s))
 +							{
 +								if(chartype_close(*(s+1))) // '</...'
 +								{
 +								    ++s;
 +									goto LOC_CLOSE;
 +								}
 +								else goto LOC_SEARCH; // Expect a new element enter, so go scan for it.
 +							}
 +							
 +							s = mark;
 +							
 +							bool preserve = OPTSET(parse_ext_pcdata) || cursor->type != node_document;
 +
 +							if (preserve)
 +							{
 +								PUSHNODE(node_pcdata); // Append a new node on the tree.
 +								cursor->value = s; // Save the offset.
 +							}
 +													
 +							while (*s && !chartype_enter(*s)) ++s; // '...<'
 +							
 +							if (preserve)
 +							{
 +								if (*s) ENDSEG(); // Save char in 'ch', terminate & step over.
 +
 +								strconv_pcdata(&cursor->value);
 +								
 +								POPNODE(); // Pop since this is a standalone.
 +							}
 +
 +							if (!*s) return s;
 +
 +							if(chartype_enter(ch)) // Did we hit a '<...'?
 +							{
 +								if(chartype_close(*s)) goto LOC_CLOSE;
 +								else if(chartype_special(*s)) goto LOC_CLASSIFY; // We hit a '<!...'. We must test this here if we want comments intermixed w/PCDATA.
 +								else if(*s) goto LOC_CLASSIFY;
 +								else return s;
 +							}
 +						}
 +						// Fall-through A.
 +						else if(chartype_close(*s)) // '.../'
 +						{
 +							++s;
 +							if(chartype_leave(*s)) // '.../>'
 +							{
 +								POPNODE(); // Pop.
 +								goto LOC_LEAVE;
 +							}
 +						}
 +					}
 +					// Fall-through B.
 +					else if(chartype_close(*s)) // '.../'
 +					{
 +					LOC_CLOSE:
 +						++s;
 +						
 +						if (OPTSET(parse_match_end_tags))
 +						{
 +							while (cursor && cursor->type != node_document)
 +							{
 +								char* tagname = s;
 +	
 +								// is it the matching tag?
 +								char* name = cursor->name;
 +								
 +								if (name)
 +								{
 +									while (*tagname && chartype_symbol(*tagname))
 +									{
 +										if (*tagname++ != *name++) goto TAG_NEXTMATCH;
 +									}
 +									
 +									if (!*name)
 +									{
 +										// we've found matching tag
 +										POPNODE();
 +										s = tagname;
 +										break;
 +									}
 +								}
 +
 +							TAG_NEXTMATCH:
 +								POPNODE();
 +							}
 +						}
 +						else if (OPTSET(parse_check_end_tags))
 +						{
 +							char* name = cursor->name;
 +							if (!name) return s;
 +						
 +							while (*s && chartype_symbol(*s))
 +							{
 +								if (*s++ != *name++) return s;
 +							}
 +							if (*name) return s;
 +							
 +							POPNODE(); // Pop.
 +						}
 +						else
 +						{
 +							SCANFOR(chartype_leave(*s)); // '...>'
 +							POPNODE(); // Pop.
 +						}
 +
 +						SKIPWS();
 +												
 +						goto LOC_LEAVE;
 +					}
 +				}
 +			}
 +			return s;
 +		}
 +	};
 +
 +	// Compare lhs with [rhs_begin, rhs_end)
 +	int strcmprange(const char* lhs, const char* rhs_begin, const char* rhs_end)
 +	{
 +		while (*lhs && rhs_begin != rhs_end && *lhs == *rhs_begin)
 +		{
 +			++lhs;
 +			++rhs_begin;
 +		}
 +		
 +		if (rhs_begin == rhs_end && *lhs == 0) return 0;
 +		else return 1;
 +	}
 +	
 +	// Character set pattern match.
 +	int strcmpwild_cset(const char** src, const char** dst)
 +	{
 +		int find = 0, excl = 0, star = 0;
 +		
 +		if (**src == '!')
 +		{
 +			excl = 1;
 +			++(*src);
 +		}
 +		
 +		while (**src != ']' || star == 1)
 +		{
 +			if (find == 0)
 +			{
 +				if (**src == '-' && *(*src-1) < *(*src+1) && *(*src+1) != ']' && star == 0)
 +				{
 +					if (**dst >= *(*src-1) && **dst <= *(*src+1))
 +					{
 +						find = 1;
 +						++(*src);
 +					}
 +				}
 +				else if (**src == **dst) find = 1;
 +			}
 +			++(*src);
 +			star = 0;
 +		}
 +
 +		if (excl == 1) find = (1 - find);
 +		if (find == 1) ++(*dst);
 +	
 +		return find;
 +	}
 +}
 +
 +namespace pugi
 +{
 +	namespace impl
 +	{
 +		int strcmpwild(const char* src, const char* dst);
 +	}
 +}
 +
 +namespace
 +{
 +	using namespace pugi;
 +
 +	// Wildcard pattern match.
 +	int strcmpwild_astr(const char** src, const char** dst)
 +	{
 +		int find = 1;
 +		++(*src);
 +		while ((**dst != 0 && **src == '?') || **src == '*')
 +		{
 +			if(**src == '?') ++(*dst);
 +			++(*src);
 +		}
 +		while (**src == '*') ++(*src);
 +		if (**dst == 0 && **src != 0) return 0;
 +		if (**dst == 0 && **src == 0) return 1;
 +		else
 +		{
 +			if (impl::strcmpwild(*src,*dst))
 +			{
 +				do
 +				{
 +					++(*dst);
 +					while(**src != **dst && **src != '[' && **dst != 0) 
 +						++(*dst);
 +				}
 +				while ((**dst != 0) ? impl::strcmpwild(*src,*dst) : 0 != (find=0));
 +			}
 +			if (**dst == 0 && **src == 0) find = 1;
 +			return find;
 +		}
 +	}
 +}
 +
 +namespace pugi
 +{
 +	namespace impl
 +	{
 +		// Compare two strings, with globbing, and character sets.
 +		int strcmpwild(const char* src, const char* dst)
 +		{
 +			int find = 1;
 +			for(; *src != 0 && find == 1 && *dst != 0; ++src)
 +			{
 +				switch (*src)
 +				{
 +					case '?': ++dst; break;
 +					case '[': ++src; find = strcmpwild_cset(&src,&dst); break;
 +					case '*': find = strcmpwild_astr(&src,&dst); --src; break;
 +					default : find = (int) (*src == *dst); ++dst;
 +				}
 +			}
 +			while (*src == '*' && find == 1) ++src;
 +			return (find == 1 && *dst == 0 && *src == 0) ? 0 : 1;
 +		}
 +	}
 +
 +	extern "C"
 +	{
 +		int strcmpwildimpl(const char* src, const char* dst)
 +		{
 +			return impl::strcmpwild(src, dst);
 +		}
 +
 +		typedef int (*strcmpfunc)(const char*, const char*);
 +	}
 +
 +	xml_attribute_struct::xml_attribute_struct(): name(0), value(0), prev_attribute(0), next_attribute(0)
 +	{
 +	}
 +	
 +	xml_node_struct::xml_node_struct(xml_node_type type): name(0), value(0), prev_sibling(0), next_sibling(0), first_child(0), last_child(0), first_attribute(0), last_attribute(0), type(type)
 +	{
 +	}
 +
 +	xml_tree_walker::xml_tree_walker() : _deep(0)
 +	{
 +	}
 +	
 +	xml_tree_walker::~xml_tree_walker()
 +	{
 +	}
 +
 +	void xml_tree_walker::push()
 +	{
 +		++_deep;
 +	}
 +
 +	void xml_tree_walker::pop()
 +	{
 +		--_deep;
 +	}
 +
 +	int xml_tree_walker::depth() const
 +	{
 +		return (_deep > 0) ? _deep : 0;
 +	}
 +
 +	bool xml_tree_walker::begin(const xml_node&)
 +	{
 +		return true;
 +	}
 +
 +	bool xml_tree_walker::end(const xml_node&)
 +	{
 +		return true;
 +	}
 +
 +	xml_attribute::xml_attribute(): _attr(NULL)
 +	{
 +	}
 +
 +	xml_attribute::xml_attribute(const xml_attribute_struct* attr): _attr(attr)
 +	{
 +	}
 +
 +	xml_attribute::operator xml_attribute::unspecified_bool_type() const
 +	{
 +      	return empty() ? 0 : &xml_attribute::_attr;
 +   	}
 +
 +	bool xml_attribute::operator==(const xml_attribute& r) const
 +	{
 +		return (_attr == r._attr);
 +	}
 +	
 +	bool xml_attribute::operator!=(const xml_attribute& r) const
 +	{
 +		return (_attr != r._attr);
 +	}
 +
 +	bool xml_attribute::operator<(const xml_attribute& r) const
 +	{
 +		return (_attr < r._attr);
 +	}
 +	
 +	bool xml_attribute::operator>(const xml_attribute& r) const
 +	{
 +		return (_attr > r._attr);
 +	}
 +	
 +	bool xml_attribute::operator<=(const xml_attribute& r) const
 +	{
 +		return (_attr <= r._attr);
 +	}
 +	
 +	bool xml_attribute::operator>=(const xml_attribute& r) const
 +	{
 +		return (_attr >= r._attr);
 +	}
 +
 +   	xml_attribute xml_attribute::next_attribute() const
 +   	{
 +    	return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
 +   	}
 +
 +    xml_attribute xml_attribute::previous_attribute() const
 +    {
 +    	return _attr ? xml_attribute(_attr->prev_attribute) : xml_attribute();
 +    }
 +
 +	int xml_attribute::as_int() const
 +	{
 +		if(empty() || !_attr->value) return 0;
 +		return atoi(_attr->value);
 +	}
 +
 +	double xml_attribute::as_double() const
 +	{
 +		if(empty() || !_attr->value) return 0.0;
 +		return atof(_attr->value);
 +	}
 +
 +	float xml_attribute::as_float() const
 +	{
 +		if(empty() || !_attr->value) return 0.0f;
 +		return (float)atof(_attr->value);
 +	}
 +
 +	bool xml_attribute::as_bool() const
 +	{
 +		if(empty() || !_attr->value) return false;
 +		if(*(_attr->value))
 +		{
 +			return // Only look at first char:
 +			(
 +				*(_attr->value) == '1' || // 1*
 +				*(_attr->value) == 't' || // t* (true)
 +				*(_attr->value) == 'T' || // T* (true|true)
 +				*(_attr->value) == 'y' || // y* (yes)
 +				*(_attr->value) == 'Y' // Y* (Yes|YES)
 +			)
 +				? true : false; // Return true if matches above, else false.
 +		}
 +		else return false;
 +	}
 +
 +	bool xml_attribute::empty() const
 +	{
 +		return (_attr == NULL);
 +	}
 +
 +	const char* xml_attribute::name() const
 +	{
 +		return (!empty() && _attr->name) ? _attr->name : "";
 +	}
 +
 +	const char* xml_attribute::value() const
 +	{
 +		return (!empty() && _attr->value) ? _attr->value : "";
 +	}
 +
 +	xml_node::xml_node(): _root(0)
 +	{
 +	}
 +
 +	xml_node::xml_node(const xml_node_struct* p): _root(p)
 +	{
 +	}
 +	
 +	xml_node::operator xml_node::unspecified_bool_type() const
 +	{
 +      	return empty() ? 0 : &xml_node::_root;
 +   	}
 +
 +	xml_node::iterator xml_node::begin() const
 +	{
 +		return iterator(_root->first_child);
 +	}
 +
 +	xml_node::iterator xml_node::end() const
 +	{
 +		return iterator(0, _root->last_child);
 +	}
 +	
 +	xml_node::iterator xml_node::children_begin() const
 +	{
 +		return iterator(_root->first_child);
 +	}
 +
 +	xml_node::iterator xml_node::children_end() const
 +	{
 +		return iterator(0, _root->last_child);
 +	}
 +	
 +	xml_node::attribute_iterator xml_node::attributes_begin() const
 +	{
 +		return attribute_iterator(_root->first_attribute);
 +	}
 +
 +	xml_node::attribute_iterator xml_node::attributes_end() const
 +	{
 +		return attribute_iterator(0, _root->last_attribute);
 +	}
 +
 +	xml_node::iterator xml_node::siblings_begin() const
 +	{
 +		return parent().children_begin();
 +	}
 +
 +	xml_node::iterator xml_node::siblings_end() const
 +	{
 +		return parent().children_end();
 +	}
 +
 +	bool xml_node::operator==(const xml_node& r) const
 +	{
 +		return (_root == r._root);
 +	}
 +
 +	bool xml_node::operator!=(const xml_node& r) const
 +	{
 +		return (_root != r._root);
 +	}
 +
 +	bool xml_node::operator<(const xml_node& r) const
 +	{
 +		return (_root < r._root);
 +	}
 +	
 +	bool xml_node::operator>(const xml_node& r) const
 +	{
 +		return (_root > r._root);
 +	}
 +	
 +	bool xml_node::operator<=(const xml_node& r) const
 +	{
 +		return (_root <= r._root);
 +	}
 +	
 +	bool xml_node::operator>=(const xml_node& r) const
 +	{
 +		return (_root >= r._root);
 +	}
 +
 +	bool xml_node::empty() const
 +	{
 +		return (_root == 0 || _root->type == node_null);
 +	}
 +	
 +	bool xml_node::type_document() const
 +	{
 +		return (_root && _root == _root->parent && _root->type == node_document);
 +	}
 +	
 +	const char* xml_node::name() const
 +	{
 +		return (!empty() && _root->name) ? _root->name : "";
 +	}
 +
 +	xml_node_type xml_node::type() const
 +	{
 +		return (_root) ? (xml_node_type)_root->type : node_null;
 +	}
 +	
 +	const char* xml_node::value() const
 +	{
 +		return (!empty() && _root->value) ? _root->value : "";
 +	}
 +	
 +	xml_node xml_node::child(const char* name) const
 +	{
 +		if (!empty())
 +			for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 +				if (i->name && !strcmp(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_node xml_node::child_w(const char* name) const
 +	{
 +		if (!empty())
 +			for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 +				if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_attribute xml_node::attribute(const char* name) const
 +	{
 +		if (!_root) return xml_attribute();
 +
 +		for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
 +			if (i->name && !strcmp(name, i->name))
 +				return xml_attribute(i);
 +		
 +		return xml_attribute();
 +	}
 +	
 +	xml_attribute xml_node::attribute_w(const char* name) const
 +	{
 +		if (!_root) return xml_attribute();
 +
 +		for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
 +			if (i->name && !impl::strcmpwild(name, i->name))
 +				return xml_attribute(i);
 +		
 +		return xml_attribute();
 +	}
 +
 +	xml_node xml_node::sibling(const char* name) const
 +	{
 +		if (!empty() && !type_document()) return parent().child(name);
 +		else return xml_node();
 +	}
 +	
 +	xml_node xml_node::sibling_w(const char* name) const
 +	{
 +		if (!empty() && !type_document()) return parent().child_w(name);
 +		else return xml_node();
 +	}
 +
 +	xml_node xml_node::next_sibling(const char* name) const
 +	{
 +		if(empty()) return xml_node();
 +		
 +		for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
 +			if (i->name && !strcmp(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_node xml_node::next_sibling_w(const char* name) const
 +	{
 +		if(empty()) return xml_node();
 +		
 +		for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
 +			if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_node xml_node::next_sibling() const
 +	{
 +		if(empty()) return xml_node();
 +		
 +		if (_root->next_sibling) return xml_node(_root->next_sibling);
 +		else return xml_node();
 +	}
 +
 +	xml_node xml_node::previous_sibling(const char* name) const
 +	{
 +		if (empty()) return xml_node();
 +		
 +		for (xml_node_struct* i = _root->prev_sibling; i; i = i->prev_sibling)
 +			if (i->name && !strcmp(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_node xml_node::previous_sibling_w(const char* name) const
 +	{
 +		if (empty()) return xml_node();
 +		
 +		for (xml_node_struct* i = _root->prev_sibling; i; i = i->prev_sibling)
 +			if (i->name && !impl::strcmpwild(name, i->name)) return xml_node(i);
 +
 +		return xml_node();
 +	}
 +
 +	xml_node xml_node::previous_sibling() const
 +	{
 +		if(empty()) return xml_node();
 +		
 +		if (_root->prev_sibling) return xml_node(_root->prev_sibling);
 +		else return xml_node();
 +	}
 +
 +	xml_node xml_node::parent() const
 +	{
 +		return (!type_document()) ? xml_node(_root->parent) : xml_node();
 +	}
 +
 +	const char* xml_node::child_value() const
 +	{
 +		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 +			if ((i->type == node_pcdata || i->type == node_cdata) && i->value)
 +				return i->value;
 +		return "";
 +	}
 +
 +	xml_attribute xml_node::first_attribute() const
 +	{
 +		return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
 +	}
 +
 +	xml_attribute xml_node::last_attribute() const
 +	{
 +		return _root ? xml_attribute(_root->last_attribute) : xml_attribute();
 +	}
 +
 +	xml_node xml_node::first_child() const
 +	{
 +		if (_root) return xml_node(_root->first_child);
 +		else return xml_node();
 +	}
 +
 +	xml_node xml_node::last_child() const
 +	{
 +		if (_root) return xml_node(_root->last_child);
 +		else return xml_node();
 +	}
 +
 +	namespace impl
 +	{
 +		xml_node first_element(const xml_node_struct* node, const char* name, strcmpfunc pred)
 +		{
 +			for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
 +			{
 +				if (i->name && !pred(name, i->name)) return xml_node(i);
 +				else if (i->first_child)
 +				{
 +					xml_node found = first_element(i, name, pred);
 +					if (found) return found; // Found.
 +				}
 +			}
 +			return xml_node(); // Not found.
 +		}
 +	}
 +
 +	xml_node xml_node::first_element(const char* name) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element(_root, name, &strcmp);
 +	}
 +
 +	xml_node xml_node::first_element_w(const char* name) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element(_root, name, &strcmpwildimpl);
 +	}
 +
 +	namespace impl
 +	{
 +		xml_node first_element_by_value(const xml_node_struct* node, const char* name, const char* value, strcmpfunc pred)
 +		{
 +			for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
 +			{
 +				if (i->name && !pred(name,i->name))
 +				{
 +					for (xml_node_struct* j = i->first_child; j; j = j->next_sibling)
 +						if (j->type == node_pcdata && j->value && !pred(value, j->value))
 +							return xml_node(i);
 +				}
 +				else if (i->first_child)
 +				{
 +					xml_node found = first_element_by_value(i, name, value, pred);
 +					if(!found.empty()) return found; // Found.
 +				}
 +			}
 +			return xml_node(); // Not found.
 +		}
 +	}
 +
 +	xml_node xml_node::first_element_by_value(const char* name,const char* value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_value(_root, name, value, &strcmp);
 +	}
 +
 +	xml_node xml_node::first_element_by_value_w(const char* name,const char* value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_value(_root, name, value, &strcmpwildimpl);
 +	}
 +
 +	namespace impl
 +	{
 +		xml_node first_element_by_attribute(const xml_node_struct* node, const char* name, const char* attr_name, const char* attr_value, strcmpfunc pred)
 +		{
 +			for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
 +			{
 +				if (i->name && !pred(name, i->name))
 +				{
 +					for (xml_attribute_struct* j = i->first_attribute; j; j = j->next_attribute)
 +					{
 +						if (j->name && j->value && !pred(attr_name, j->name) && !pred(attr_value, j->value))
 +							return xml_node(i); // Wrap it up and return.
 +					}
 +				}
 +				else if (i->first_child)
 +				{
 +					xml_node found = first_element_by_attribute(i, name, attr_name, attr_value, pred);
 +					if(!found.empty()) return found; // Found.
 +				}
 +			}
 +			return xml_node(); // Not found.
 +		}
 +	}
 +	
 +	xml_node xml_node::first_element_by_attribute(const char* name,const char* attr_name,const char* attr_value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_attribute(_root, name, attr_name, attr_value, &strcmp);
 +	}
 +
 +	xml_node xml_node::first_element_by_attribute_w(const char* name,const char* attr_name,const char* attr_value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_attribute(_root, name, attr_name, attr_value, &strcmpwildimpl);
 +	}
 +
 +	namespace impl
 +	{
 +		xml_node first_element_by_attribute(const xml_node_struct* node, const char* attr_name,const char* attr_value, strcmpfunc pred)
 +		{
 +			for (xml_node_struct* i = node->first_child; i; i = i->next_sibling)
 +			{
 +				for (xml_attribute_struct* j = i->first_attribute; j; j = j->next_attribute)
 +				{
 +					if (j->name && j->value && !pred(attr_name, j->name) && !pred(attr_value, j->value))
 +						return xml_node(i); // Wrap it up and return.
 +				}
 +				
 +				if (i->first_child)
 +				{
 +					xml_node found = first_element_by_attribute(i->first_child, attr_name, attr_value, pred);
 +					if (!found.empty()) return found; // Found.
 +				}
 +			}
 +			return xml_node(); // Not found.
 +		}
 +	}
 +
 +	xml_node xml_node::first_element_by_attribute(const char* attr_name,const char* attr_value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_attribute(_root, attr_name, attr_value, &strcmp);
 +	}
 +
 +	xml_node xml_node::first_element_by_attribute_w(const char* attr_name,const char* attr_value) const
 +	{
 +		if (empty()) return xml_node();
 +
 +		return impl::first_element_by_attribute(_root, attr_name, attr_value, &strcmpwildimpl);
 +	}
 +
 +	xml_node xml_node::first_node(xml_node_type type) const
 +	{
 +		if(!_root) return xml_node();
 +		for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 +		{
 +			if (i->type == type) return xml_node(i);
 +			else if (i->first_child)
 +			{
 +				xml_node subsearch(i);
 +				xml_node found = subsearch.first_node(type);
 +				if(!found.empty()) return found; // Found.
 +			}
 +		}
 +		return xml_node(); // Not found.
 +	}
 +
 +	std::string xml_node::path(char delimiter) const
 +	{
 +		std::string path;
 +
 +		xml_node cursor = *this; // Make a copy.
 +		
 +		path = cursor.name();
 +
 +		while (cursor.parent() && !cursor.type_document()) // Loop to parent (stopping on actual root because it has no name).
 +		{
 +			cursor = cursor.parent();
 +			
 +			std::string temp = cursor.name();
 +			temp += delimiter;
 +			temp += path;
 +			path.swap(temp);
 +		}
 +
 +		return path;
 +	}
 +
 +	xml_node xml_node::first_element_by_path(const char* path, char delimiter) const
 +	{
 +		xml_node found = *this; // Current search context.
 +
 +		if (empty() || !path || !path[0]) return found;
 +
 +		if (path[0] == delimiter)
 +		{
 +			// Absolute path; e.g. '/foo/bar'
 +			while (found.parent()) found = found.parent();
 +			++path;
 +		}
 +
 +		const char* path_segment = path;
 +
 +		while (*path_segment == delimiter) ++path_segment;
 +
 +		const char* path_segment_end = path_segment;
 +
 +		while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;
 +
 +		if (path_segment == path_segment_end) return found;
 +
 +		const char* next_segment = path_segment_end;
 +
 +		while (*next_segment == delimiter) ++next_segment;
 +
 +		if (*path_segment == '.' && path_segment + 1 == path_segment_end)
 +			return found.first_element_by_path(next_segment, delimiter);
 +		else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)
 +			return found.parent().first_element_by_path(next_segment, delimiter);
 +		else
 +		{
 +			for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)
 +			{
 +				if (j->name && !strcmprange(j->name, path_segment, path_segment_end))
 +				{
 +					xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
 +
 +					if (subsearch) return subsearch;
 +				}
 +			}
 +
 +			return xml_node();
 +		}
 +	}
 +
 +	bool xml_node::traverse(xml_tree_walker& walker) const
 +	{
 +		if (!walker.begin(*this)) return false; // Send the callback for begin traverse if depth is zero.
 +		if(!empty()) // Don't traverse if this is a null node.
 +		{
 +			walker.push(); // Increment the walker depth counter.
 +
 +			for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
 +			{
 +				xml_node subsearch(i);
 +				if (!subsearch.traverse(walker)) return false;
 +			}
 +			walker.pop(); // Decrement the walker depth counter.
 +		}
 +		if (!walker.end(*this)) return false; // Send the callback for end traverse if depth is zero.
 +		
 +		return true;
 +	}
 +
 +	xml_node_iterator::xml_node_iterator()
 +	{
 +	}
 +
 +	xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node)
 +	{
 +	}
 +
 +	xml_node_iterator::xml_node_iterator(const xml_node_struct* ref): _wrap(ref)
 +	{
 +	}
 +		
 +	xml_node_iterator::xml_node_iterator(const xml_node_struct* ref, const xml_node_struct* prev): _wrap(ref), _prev(prev)
 +	{
 +	}
 +
 +	bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
 +	{
 +		return (_wrap == rhs._wrap);
 +	}
 +	
 +	bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
 +	{
 +		return (_wrap != rhs._wrap);
 +	}
 +
 +	const xml_node& xml_node_iterator::operator*() const
 +	{
 +		return _wrap;
 +	}
 +
 +	const xml_node* xml_node_iterator::operator->() const
 +	{
 +		return &_wrap;
 +	}
 +
 +	const xml_node_iterator& xml_node_iterator::operator++()
 +	{
 +		_prev = _wrap;
 +		_wrap = xml_node(_wrap._root->next_sibling);
 +		return *this;
 +	}
 +
 +	xml_node_iterator xml_node_iterator::operator++(int)
 +	{
 +		xml_node_iterator temp = *this;
 +		++*this;
 +		return temp;
 +	}
 +
 +	const xml_node_iterator& xml_node_iterator::operator--()
 +	{
 +		if (_wrap._root) _wrap = xml_node(_wrap._root->prev_sibling);
 +		else _wrap = _prev;
 +		return *this;
 +	}
 +
 +	xml_node_iterator xml_node_iterator::operator--(int)
 +	{
 +		xml_node_iterator temp = *this;
 +		--*this;
 +		return temp;
 +	}
 +
 +	xml_attribute_iterator::xml_attribute_iterator()
 +	{
 +	}
 +
 +	xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr): _wrap(attr)
 +	{
 +	}
 +
 +	xml_attribute_iterator::xml_attribute_iterator(const xml_attribute_struct* ref): _wrap(ref)
 +	{
 +	}
 +		
 +	xml_attribute_iterator::xml_attribute_iterator(const xml_attribute_struct* ref, const xml_attribute_struct* prev): _wrap(ref), _prev(prev)
 +	{
 +	}
 +
 +	bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
 +	{
 +		return (_wrap == rhs._wrap);
 +	}
 +	
 +	bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
 +	{
 +		return (_wrap != rhs._wrap);
 +	}
 +
 +	const xml_attribute& xml_attribute_iterator::operator*() const
 +	{
 +		return _wrap;
 +	}
 +
 +	const xml_attribute* xml_attribute_iterator::operator->() const
 +	{
 +		return &_wrap;
 +	}
 +
 +	const xml_attribute_iterator& xml_attribute_iterator::operator++()
 +	{
 +		_prev = _wrap;
 +		_wrap = xml_attribute(_wrap._attr->next_attribute);
 +		return *this;
 +	}
 +
 +	xml_attribute_iterator xml_attribute_iterator::operator++(int)
 +	{
 +		xml_attribute_iterator temp = *this;
 +		++*this;
 +		return temp;
 +	}
 +
 +	const xml_attribute_iterator& xml_attribute_iterator::operator--()
 +	{
 +		if (_wrap._attr) _wrap = xml_attribute(_wrap._attr->prev_attribute);
 +		else _wrap = _prev;
 +		return *this;
 +	}
 +
 +	xml_attribute_iterator xml_attribute_iterator::operator--(int)
 +	{
 +		xml_attribute_iterator temp = *this;
 +		--*this;
 +		return temp;
 +	}
 +
 +	xml_memory_block::xml_memory_block(): next(0), size(0)
 +	{
 +	}
 +
 +	xml_parser::xml_parser(unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
 +	{
 +	}
 +
 +	xml_parser::xml_parser(std::istream& stream,unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
 +	{
 +		parse(stream, _optmsk);
 +	}
 +
 +	xml_parser::xml_parser(char* xmlstr,unsigned int optmsk): _xmldoc(0), _optmsk(optmsk)
 +	{
 +		parse(xmlstr, _optmsk);
 +	}
 +
 +	xml_parser::~xml_parser()
 +	{
 +		xml_memory_block* current = _memory.next;
 +
 +		while (current)
 +		{
 +			xml_memory_block* next = current->next;
 +			delete current;
 +			current = next;
 +		}
 +	}
 +
 +	xml_parser::operator xml_node() const
 +	{
 +		return xml_node(_xmldoc);
 +	}
 +	
 +	xml_node xml_parser::document() const
 +	{
 +		return xml_node(_xmldoc);
 +	}
 +
 +	unsigned int xml_parser::options() const
 +	{
 +		return _optmsk;
 +	}
 +
 +	unsigned int xml_parser::options(unsigned int optmsk)
 +	{
 +		unsigned int prev = _optmsk;
 +		_optmsk = optmsk;
 +		return prev;
 +	}
 +
 +	void xml_parser::parse(std::istream& stream,unsigned int optmsk)
 +	{
 +		int length = 0, pos = stream.tellg();
 +		stream.seekg(0, std::ios_base::end);
 +		length = stream.tellg();
 +		stream.seekg(pos, std::ios_base::beg);
 +
 +		_buffer.resize(length + 1);
 +		stream.read(&_buffer[0], length);
 +		_buffer[length] = 0;
 +
 +		parse(&_buffer[0],optmsk); // Parse the input string.
 +	}
 +
 +	char* xml_parser::parse(char* xmlstr,unsigned int optmsk)
 +	{
 +		if(!xmlstr) return NULL;
 +
 +		xml_allocator alloc(&_memory);
 +		
 +		_xmldoc = alloc.allocate<xml_node_struct>(node_document); // Allocate a new root.
 +		_xmldoc->parent = _xmldoc; // Point to self.
 +		if(optmsk != parse_noset) _optmsk = optmsk;
 +		
 +		xml_parser_impl parser(alloc);
 +		
 +		return parser.parse(xmlstr,_xmldoc,_optmsk); // Parse the input string.
 +	}
 +	
 +	std::string utf8(const wchar_t* str)
 +	{
 +		std::string result;
 +		result.reserve(strutf16_utf8_size(str));
 +	  
 +		for (; *str; ++str)
 +		{
 +			char buffer[6];
 +	  	
 +			result.append(buffer, strutf16_utf8(buffer, *str));
 +		}
 +	  	
 +	  	return result;
 +	}
 +	
 +	std::wstring utf16(const char* str)
 +	{
 +		std::wstring result;
 +		result.reserve(strutf8_utf16_size(str));
 +
 +		for (; *str;)
 +		{
 +			unsigned int ch;
 +			str = strutf8_utf16(str, ch);
 +			result += (wchar_t)ch;
 +		}
 +
 +		return result;
 +	}
 +}
 diff --git a/src/pugixml.hpp b/src/pugixml.hpp new file mode 100644 index 0000000..026e6ba --- /dev/null +++ b/src/pugixml.hpp @@ -0,0 +1,619 @@ +///////////////////////////////////////////////////////////////////////////////
 +//
 +// Pug Improved XML Parser - Version 0.1
 +// --------------------------------------------------------
 +// Copyright (C) 2006, by Zeux (zeux@mathcentre.com)
 +// This work is based on the pugxml parser, which is:
 +// Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
 +// Released into the Public Domain. Use at your own risk.
 +// See pugxml.xml for further information, history, etc.
 +// Contributions by Neville Franks (readonly@getsoft.com).
 +//
 +///////////////////////////////////////////////////////////////////////////////
 +
 +#ifndef HEADER_PUGIXML_HPP
 +#define HEADER_PUGIXML_HPP
 +
 +/// The PugiXML Parser namespace.
 +namespace pugi
 +{
 +	/// Tree node classification.
 +	/// See 'xml_node_struct::type'
 +	enum xml_node_type
 +	{
 +		node_null,			///< An undifferentiated entity.
 +		node_document,		///< A document tree's absolute root.
 +		node_element,		///< E.g. '<...>'
 +		node_pcdata,		///< E.g. '<...>'
 +		node_cdata,			///< E.g. '<![CDATA[...]]>'
 +		node_comment,		///< E.g. '<!--...-->'
 +		node_pi				///< E.g. '<?...?>'
 +	};
 +
 +	/// Parser Options
 +	const size_t memory_block_size = 32768;		///< Memory block size, 32 kb
 +
 +	const unsigned int parse_minimal			= 0x00000000; ///< Unset the following flags.
 +	const unsigned int parse_pi					= 0x00000001; ///< Parse '<?...?>'
 +	const unsigned int parse_comments			= 0x00000002; ///< Parse '<!--...-->'
 +	const unsigned int parse_cdata				= 0x00000004; ///< Parse '<![CDATA[...]]>'
 +	const unsigned int parse_ws_pcdata			= 0x00000008; ///< Skip PCDATA that consists only of whitespaces
 +	const unsigned int parse_ext_pcdata			= 0x00000010; ///< Skip PCDATA that is outside all tags (i.e. root)
 +	const unsigned int parse_trim_pcdata		= 0x00000020; ///< Trim '>...<'
 +	const unsigned int parse_trim_attribute		= 0x00000040; ///< Trim 'foo="..."'.
 +	const unsigned int parse_escapes_pcdata		= 0x00000080; ///< Parse <, >, &, ", ', &#.. sequences
 +	const unsigned int parse_escapes_attribute 	= 0x00000100; ///< Parse <, >, &, ", ', &#.. sequences
 +	const unsigned int parse_wnorm_pcdata		= 0x00000200; ///< Normalize spaces in pcdata
 +	const unsigned int parse_wnorm_attribute	= 0x00000400; ///< Normalize spaces in attributes
 +	const unsigned int parse_wconv_attribute	= 0x00000800; ///< Convert space-like characters to spaces in attributes (only if wnorm is not set)
 +	const unsigned int parse_eol_cdata			= 0x00001000; ///< Perform EOL handling in CDATA sections
 +	const unsigned int parse_check_end_tags		= 0x00002000; ///< Check start and end tag names and return error if names mismatch
 +	const unsigned int parse_match_end_tags		= 0x00004000; ///< Try to find corresponding start tag for an end tag
 +	const unsigned int parse_default			= 0x0000FFFF & ~parse_ws_pcdata; ///< Set all flags, except parse_ws_pcdata
 +	const unsigned int parse_noset				= 0x80000000; ///< Parse with flags in xml_parser
 +
 +	const unsigned int parse_w3c				= parse_pi | parse_comments | parse_cdata |
 +												parse_escapes_pcdata | parse_escapes_attribute |
 +												parse_wconv_attribute | parse_check_end_tags |
 +												parse_ws_pcdata | parse_eol_cdata;
 +
 +	/// Forward declarations
 +	struct xml_attribute_struct;
 +	struct xml_node_struct;
 +
 +	class xml_node_iterator;
 +	class xml_attribute_iterator;
 +
 +	class xml_tree_walker;
 +
 +	/// Provides a light-weight wrapper for manipulating xml_attribute_struct structures.
 +	///	Note: xml_attribute does not create any memory for the attribute it wraps; 
 +	///	it only wraps a pointer to an existing xml_attribute_struct.
 +	class xml_attribute
 +	{
 +		friend class xml_attribute_iterator;
 +		friend class xml_node;
 +
 +	private:
 +		const xml_attribute_struct* _attr; ///< The internal attribute pointer.
 +	
 +    	/// Safe bool type
 +    	typedef const xml_attribute_struct* xml_attribute::*unspecified_bool_type;
 +
 +		/// Initializing ctor
 +		explicit xml_attribute(const xml_attribute_struct* attr);
 +
 +	public:
 +		/// Default ctor
 +		xml_attribute();
 +		
 +	public:
 +		/// Comparison operators
 +		bool operator==(const xml_attribute& r) const;
 +		bool operator!=(const xml_attribute& r) const;
 +		bool operator<(const xml_attribute& r) const;
 +		bool operator>(const xml_attribute& r) const;
 +		bool operator<=(const xml_attribute& r) const;
 +		bool operator>=(const xml_attribute& r) const;
 +	
 +    	/// Safe bool conversion
 +    	operator unspecified_bool_type() const;
 +
 +    	/// Get next attribute if any, else xml_attribute()
 +    	xml_attribute next_attribute() const;
 +
 +    	/// Get previous attribute if any, else xml_attribute()
 +    	xml_attribute previous_attribute() const;
 +
 +		/// Cast attribute value as int. If not found, return 0.
 +		/// \return Attribute value as int, or 0.
 +		int as_int() const;
 +
 +		/// Cast attribute value as double. If not found, return 0.0.
 +		/// \return Attribute value as double, or 0.0.
 +		double as_double() const;
 +	
 +		/// Cast attribute value as float. If not found, return 0.0.
 +		/// \return Attribute value as float, or 0.0.
 +		float as_float() const;
 +
 +		/// Cast attribute value as bool. If not found, return false.
 +		/// \return Attribute value as bool, or false.
 +		bool as_bool() const;
 +
 +	public:
 +		/// True if internal pointer is valid
 +		bool empty() const;
 +
 +	public:
 +		/// Access the attribute name.
 +		const char* name() const;
 +
 +		/// Access the attribute value.
 +		const char* value() const;
 +	};
 +
 +	/// Provides a light-weight wrapper for manipulating xml_node_struct structures.
 +	class xml_node
 +	{
 +		friend class xml_node_iterator;
 +		friend class xml_parser;
 +
 +	private:
 +		const xml_node_struct* _root; ///< Pointer to node root.
 +
 +    	/// Safe bool type
 +    	typedef const xml_node_struct* xml_node::*unspecified_bool_type;
 +
 +	private:
 +		/// Node is tree root.
 +		bool type_document() const;
 +
 +	public:
 +		/// Default constructor.
 +		///	Node root points to a dummy 'xml_node_struct' structure. Test for this 
 +		///	with 'empty'.
 +		xml_node();
 +
 +		/// Construct, wrapping the given 'xml_node_struct' pointer.
 +		explicit xml_node(const xml_node_struct* p);
 +
 +	public:
 +		/// Base iterator type (for child nodes). Same as 'child_iterator'.
 +		typedef xml_node_iterator iterator;
 +
 +		/// Attribute iterator type.
 +		typedef xml_attribute_iterator attribute_iterator;
 +
 +		/// Access the begin iterator for this node's collection of child nodes.
 +		/// Same as 'children_begin'.
 +		iterator begin() const;
 +	
 +		/// Access the end iterator for this node's collection of child nodes.
 +		/// Same as 'children_end'.
 +		iterator end() const;
 +		
 +		/// Access the begin iterator for this node's collection of child nodes.
 +		/// Same as 'begin'.
 +		iterator children_begin() const;
 +	
 +		/// Access the end iterator for this node's collection of child nodes.
 +		/// Same as 'end'.
 +		iterator children_end() const;
 +	
 +		/// Access the begin iterator for this node's collection of attributes.
 +		attribute_iterator attributes_begin() const;
 +	
 +		/// Access the end iterator for this node's collection of attributes.
 +		attribute_iterator attributes_end() const;
 +
 +		/// Access the begin iterator for this node's collection of siblings.
 +		iterator siblings_begin() const;
 +	
 +		/// Access the end iterator for this node's collection of siblings.
 +		iterator siblings_end() const;
 +	
 +	public:
 +    	/// Safe bool conversion
 +		operator unspecified_bool_type() const;
 +	
 +		/// Comparison operators
 +		bool operator==(const xml_node& r) const;
 +		bool operator!=(const xml_node& r) const;
 +		bool operator<(const xml_node& r) const;
 +		bool operator>(const xml_node& r) const;
 +		bool operator<=(const xml_node& r) const;
 +		bool operator>=(const xml_node& r) const;
 +
 +	public:
 +		/// Node pointer is null, or type is node_null. Same as type_null.
 +		bool empty() const;
 +
 +	public:
 +		/// Access node entity type.
 +		xml_node_type type() const;
 +
 +		/// Access pointer to node name if any, else empty string.
 +		const char* name() const;
 +
 +		/// Access pointer to data if any, else empty string.
 +		const char* value() const;
 +	
 +		/// Access child node at name as xml_node or xml_node(NULL) if bad name.
 +		xml_node child(const char* name) const;
 +
 +		/// Access child node at name as xml_node or xml_node(NULL) if bad name.
 +		/// Enable wildcard matching.
 +		xml_node child_w(const char* name) const;
 +
 +		/// Access the attribute having 'name'.
 +		xml_attribute attribute(const char* name) const;
 +
 +		/// Access the attribute having 'name'.
 +		/// Enable wildcard matching.
 +		xml_attribute attribute_w(const char* name) const;
 +
 +		/// Access sibling node at name as xml_node or xml_node(NULL) if bad name.
 +		xml_node sibling(const char* name) const;
 +
 +		/// Access sibling node at name as xml_node or xml_node(NULL) if bad name.
 +		/// Enable wildcard matching.
 +		xml_node sibling_w(const char* name) const;
 +
 +		/// Access current node's next sibling by position and name.
 +		xml_node next_sibling(const char* name) const;
 +
 +		/// Access current node's next sibling by position and name.
 +		/// Enable wildcard matching.
 +		xml_node next_sibling_w(const char* name) const;
 +
 +		/// Access current node's next sibling by position.
 +		xml_node next_sibling() const;
 +
 +		/// Access current node's previous sibling by position and name.
 +		xml_node previous_sibling(const char* name) const;
 +
 +		/// Access current node's previous sibling by position and name.
 +		/// Enable wildcard matching.
 +		xml_node previous_sibling_w(const char* name) const;
 +
 +		/// Access current node's previous sibling by position.
 +		xml_node previous_sibling() const;
 +
 +		/// Access node's parent if any, else xml_node(NULL)
 +		xml_node parent() const;
 +
 +		/// Return PCDATA/CDATA that is child of current node. If none, return empty string.
 +		const char* child_value() const;
 +
 +	public:
 +		/// Access node's first attribute if any, else xml_attribute()
 +		xml_attribute first_attribute() const;
 +
 +		/// Access node's last attribute if any, else xml_attribute()
 +        xml_attribute last_attribute() const;
 +
 +		/// Find all elements having the given name.
 +		template <typename OutputIterator> void all_elements_by_name(const char* name, OutputIterator it) const;
 +
 +		/// Find all elements having the given name.
 +		/// Enable wildcard matching.
 +		template <typename OutputIterator> void all_elements_by_name_w(const char* name, OutputIterator it) const;
 +
 +		/// Access node's first child if any, else xml_node()
 +		xml_node first_child() const;
 +
 +		/// Access node's last child if any, else xml_node()
 +        xml_node last_child() const;
 +		
 +		/// Find attribute using the predicate
 +		/// Predicate should take xml_attribute and return bool.
 +		template <typename Predicate> xml_attribute find_attribute(Predicate pred) const;
 +
 +		/// Find child using the predicate
 +		/// Predicate should take xml_node and return bool.
 +		template <typename Predicate> xml_node find_child(Predicate pred) const;
 +
 +		/// Recursively-implemented depth-first find element using the predicate
 +		/// Predicate should take xml_node and return bool.
 +		template <typename Predicate> xml_node find_element(Predicate pred) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element. 
 +		/// Use for shallow drill-downs.
 +		xml_node first_element(const char* name) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element. 
 +		/// Use for shallow drill-downs.
 +		/// Enable wildcard matching.
 +		xml_node first_element_w(const char* name) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element 
 +		/// also having matching PCDATA.
 +		xml_node first_element_by_value(const char* name, const char* value) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element 
 +		/// also having matching PCDATA.
 +		/// Enable wildcard matching.
 +		xml_node first_element_by_value_w(const char* name, const char* value) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element 
 +		/// also having matching attribute.
 +		xml_node first_element_by_attribute(const char* name, const char* attr_name, const char* attr_value) const;
 +
 +		/// Recursively-implemented depth-first find the first matching element 
 +		/// also having matching attribute.
 +		/// Enable wildcard matching.
 +		xml_node first_element_by_attribute_w(const char* name, const char* attr_name, const char* attr_value) const;
 +
 +		/// Recursively-implemented depth-first find the first element 
 +		/// having matching attribute.
 +		xml_node first_element_by_attribute(const char* attr_name, const char* attr_value) const;
 +
 +		/// Recursively-implemented depth-first find the first element 
 +		/// having matching attribute.
 +		/// Enable wildcard matching.
 +		xml_node first_element_by_attribute_w(const char* attr_name, const char* attr_value) const;
 +
 +		/// Recursively-implemented depth-first find the first matching entity. 
 +		/// Use for shallow drill-downs.
 +		xml_node first_node(xml_node_type type) const;
 +
 +		/// Compile the absolute node path from root as a text string.
 +		/// \param delimiter - Delimiter character to insert between element names.
 +		/// \return path string (e.g. with '/' as delimiter, '/document/.../this'.
 +		std::string path(char delimiter = '/') const;
 +
 +		/// Search for a node by path.
 +		/// \param path - Path string; e.g. './foo/bar' (relative to node), '/foo/bar' (relative 
 +		/// to root), '../foo/bar' (pop relative position).
 +		/// \param delimiter - Delimiter character to use in tokenizing path.
 +		/// \return Matching node, or xml_node() if not found.
 +		xml_node first_element_by_path(const char* path, char delimiter = '/') const;
 +
 +		/// Recursively traverse the tree.
 +		bool traverse(xml_tree_walker& walker) const;
 +	};
 +
 +	/// Child node iterator.
 +	class xml_node_iterator: public std::iterator<std::bidirectional_iterator_tag, const xml_node>
 +	{
 +		friend class xml_node;
 +
 +	private:
 +		xml_node _prev;
 +		xml_node _wrap;
 +
 +		/// Initializing ctor
 +		explicit xml_node_iterator(const xml_node_struct* ref);
 +	public:
 +		/// Default ctor
 +		xml_node_iterator();
 +
 +		/// Initializing ctor
 +		xml_node_iterator(const xml_node& node);
 +
 +		/// Initializing ctor (for past-the-end)
 +		xml_node_iterator(const xml_node_struct* ref, const xml_node_struct* prev);
 +
 +		bool operator==(const xml_node_iterator& rhs) const;
 +		bool operator!=(const xml_node_iterator& rhs) const;
 +
 +		const xml_node& operator*() const;
 +		const xml_node* operator->() const;
 +
 +		const xml_node_iterator& operator++();
 +		xml_node_iterator operator++(int);
 +		
 +		const xml_node_iterator& operator--();
 +		xml_node_iterator operator--(int);
 +	};
 +
 +	/// Attribute iterator.
 +	class xml_attribute_iterator: public std::iterator<std::bidirectional_iterator_tag, const xml_attribute>
 +	{
 +		friend class xml_node;
 +
 +	private:
 +		xml_attribute _prev;
 +		xml_attribute _wrap;
 +
 +		/// Initializing ctor
 +		explicit xml_attribute_iterator(const xml_attribute_struct* ref);
 +	public:
 +		/// Default ctor
 +		xml_attribute_iterator();
 +
 +		/// Initializing ctor
 +		xml_attribute_iterator(const xml_attribute& attr);
 +
 +		/// Initializing ctor (for past-the-end)
 +		xml_attribute_iterator(const xml_attribute_struct* ref, const xml_attribute_struct* prev);
 +
 +		bool operator==(const xml_attribute_iterator& rhs) const;
 +		bool operator!=(const xml_attribute_iterator& rhs) const;
 +
 +		const xml_attribute& operator*() const;
 +		const xml_attribute* operator->() const;
 +
 +		const xml_attribute_iterator& operator++();
 +		xml_attribute_iterator operator++(int);
 +		
 +		const xml_attribute_iterator& operator--();
 +		xml_attribute_iterator operator--(int);
 +	};
 +
 +	/// Abstract tree walker class for xml_node::traverse().
 +	class xml_tree_walker
 +	{
 +	private:
 +		int _deep; ///< Current node depth.
 +	public:
 +		/// Default ctor
 +		xml_tree_walker();
 +
 +		/// Virtual dtor
 +		virtual ~xml_tree_walker();
 +
 +	public:
 +		/// Increment node depth.
 +		virtual void push();
 +
 +		/// Decrement node depth
 +		virtual void pop();
 +
 +		/// Access node depth
 +		virtual int depth() const;
 +	
 +	public:
 +		/// Callback when traverse on a node begins.
 +		/// \return returning false will abort the traversal.
 +		virtual bool begin(const xml_node&);
 +
 +		/// Callback when traverse on a node ends.
 +		/// \return Returning false will abort the traversal.
 +		virtual bool end(const xml_node&);
 +	};
 +
 +	/// Memory block (internal)
 +	struct xml_memory_block
 +	{
 +		xml_memory_block();
 +
 +		xml_memory_block* next;
 +		size_t size;
 +
 +		char data[memory_block_size];
 +	};
 +
 +	/// Provides a high-level interface to the XML parser.
 +	class xml_parser
 +	{
 +	private:
 +		std::vector<char>	_buffer; ///< character buffer
 +
 +		xml_memory_block	_memory; ///< Memory block
 +
 +		xml_node_struct*	_xmldoc; ///< Pointer to current XML document tree root.
 +		unsigned int		_optmsk; ///< Parser options.
 +	
 +		xml_parser(const xml_parser&);
 +		const xml_parser& operator=(const xml_parser&);
 +
 +	public:
 +		/// Constructor.
 +		/// \param optmsk - Options mask.
 +		xml_parser(unsigned int optmsk = parse_default);
 +
 +		/// Parse constructor.
 +		/// \param xmlstr - readwrite string with xml data
 +		/// \param optmsk - Options mask.
 +		/// \see parse
 +		xml_parser(char* xmlstr, unsigned int optmsk = parse_default);
 +
 +		/// Parse constructor.
 +		/// \param stream - stream with xml data
 +		/// \param optmsk - Options mask.
 +		/// \see parse
 +		xml_parser(std::istream& stream, unsigned int optmsk = parse_default);
 +
 +		/// Dtor
 +		~xml_parser();
 +
 +	public:
 +		/// Cast as xml_node (same as document).
 +		operator xml_node() const;
 +
 +		/// Returns the root wrapped by an xml_node.
 +		xml_node document() const;
 +
 +	public:
 +		/// Get parser options mask.
 +		unsigned int options() const;
 +
 +		/// Set parser options mask.
 +		unsigned int options(unsigned int optmsk);
 +
 +	public:
 +		/// Parse the given XML stream
 +		/// \param stream - stream with xml data
 +		/// \param optmsk - Options mask.
 +		void parse(std::istream& stream, unsigned int optmsk = parse_noset);
 +
 +		/// Parse the given XML string in-situ.
 +		/// \param xmlstr - readwrite string with xml data
 +		/// \param optmsk - Options mask.
 +		/// \return last position or NULL
 +		/// \rem input string is zero-segmented
 +		char* parse(char* xmlstr, unsigned int optmsk = parse_noset);
 +	};
 +
 +	/// Utility functions for xml
 +	
 +	/// Convert utf16 to utf8
 +	std::string utf8(const wchar_t* str);
 +	
 +	/// Convert utf8 to utf16
 +	std::wstring utf16(const char* str);
 +}
 +
 +/// Inline implementation
 +
 +namespace pugi
 +{
 +	namespace impl
 +	{
 +		int strcmpwild(const char*, const char*);
 +	}
 +
 +	template <typename OutputIterator> void xml_node::all_elements_by_name(const char* name, OutputIterator it) const
 +	{
 +		if (empty()) return;
 +		
 +		for (xml_node node = first_child(); node; node = node.next_sibling())
 +		{
 +			if (!strcmp(name, node.name()))
 +			{
 +				*it = node;
 +				++it;
 +			}
 +			
 +			if (node.first_child()) node.all_elements_by_name(name, it);
 +		}
 +	}
 +
 +	template <typename OutputIterator> void xml_node::all_elements_by_name_w(const char* name, OutputIterator it) const
 +	{
 +		if (empty()) return;
 +		
 +		for (xml_node node = first_child(); node; node = node.next_sibling())
 +		{
 +			if (!impl::strcmpwild(name, node.name()))
 +			{
 +				*it = node;
 +				++it;
 +			}
 +			
 +			if (node.first_child()) node.all_elements_by_name_w(name, it);
 +		}
 +	}
 +	
 +	template <typename Predicate> inline xml_attribute xml_node::find_attribute(Predicate pred) const
 +	{
 +		if (!empty())
 +			for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
 +				if (pred(attrib))
 +					return attrib;
 +		
 +		return xml_attribute();
 +	}
 +
 +	template <typename Predicate> inline xml_node xml_node::find_child(Predicate pred) const
 +	{
 +		if (!empty())
 +			for (xml_node node = first_child(); node; node = node.next_sibling())
 +				if (pred(node))
 +					return node;
 +
 +		return xml_node();
 +	}
 +
 +	template <typename Predicate> inline xml_node xml_node::find_element(Predicate pred) const
 +	{
 +		if (!empty())
 +			for (xml_node node = first_child(); node; node = node.next_sibling())
 +			{
 +				if (pred(node))
 +					return node;
 +				
 +				if (node.first_child())
 +				{
 +					xml_node found = node.find_element(pred);
 +					if (found) return found;
 +				}
 +			}
 +
 +		return xml_node();
 +	}
 +}
 +
 +#endif
 | 
