diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pugiconfig.hpp | 23 | ||||
| -rw-r--r-- | src/pugixml.cpp | 4697 | ||||
| -rw-r--r-- | src/pugixml.hpp | 2399 | ||||
| -rw-r--r-- | src/pugixpath.cpp | 3537 | 
4 files changed, 7861 insertions, 2795 deletions
| diff --git a/src/pugiconfig.hpp b/src/pugiconfig.hpp new file mode 100644 index 0000000..795a66f --- /dev/null +++ b/src/pugiconfig.hpp @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Pug Improved XML Parser - Version 0.3 +// -------------------------------------------------------- +// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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_PUGICONFIG_HPP +#define HEADER_PUGICONFIG_HPP + +// Uncomment this to disable STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable XPath +// #define PUGIXML_NO_XPATH + +#endif diff --git a/src/pugixml.cpp b/src/pugixml.cpp index cf74eed..e812710 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1,2134 +1,2569 @@ -///////////////////////////////////////////////////////////////////////////////
 -//
 -// Pug Improved XML Parser - Version 0.2
 -// --------------------------------------------------------
 -// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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).
 -//
 -///////////////////////////////////////////////////////////////////////////////
 -
 +/////////////////////////////////////////////////////////////////////////////// +// +// Pug Improved XML Parser - Version 0.3 +// -------------------------------------------------------- +// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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 "pugixml.hpp"
 -
 -#include <cstdlib>
 -
 -#include <new>
 -
 -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
 -{	
 -	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 };
 -	}
 -
 -	enum chartype
 -	{
 -		ct_parse_pcdata = 1,	// \0, &, \r, <
 -		ct_parse_attr = 2,		// \0, &, \r, ', "
 -		ct_parse_attr_ws = 4,	// \0, &, \r, ', ", \n, space, tab
 -		ct_space = 8,			// \r, \n, space, tab
 -		ct_parse_cdata = 16,	// \0, ], >, \r
 -		ct_parse_comment = 32,	// \0, -, >, \r
 -		ct_symbol = 64			// Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
 -		
 -	};
 -
 -	static unsigned char chartype_table[256] =
 -	{
 -		55, 0, 0, 0, 0, 0, 0, 0,				0, 12, 12, 0, 0, 63, 0, 0,			// 0-15
 -		0, 0, 0, 0, 0, 0, 0, 0,					0, 0, 0, 0, 0, 0, 0, 0,				// 16-31
 -		12, 0, 6, 0, 0, 0, 7, 6,				0, 0, 0, 0, 0, 96, 64, 0,			// 32-47
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 0, 1, 0, 48, 0,			// 48-63
 -		0, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,		// 64-79
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 0, 0, 16, 0, 64,		// 80-95
 -		0, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,		// 96-111
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 0, 0, 0, 0, 0,			// 112-127
 -
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64,
 -		64, 64, 64, 64, 64, 64, 64, 64,			64, 64, 64, 64, 64, 64, 64, 64
 -	};
 -	
 -	bool is_chartype(char c, chartype ct)
 -	{
 -		return !!(chartype_table[static_cast<unsigned char>(c)] & ct);
 -	}
 -}
 -
 -namespace pugi
 -{
 -	// Get the size that is needed for strutf16_utf8 applied to all s characters
 -	static 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?..
 -	static 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
 -	static size_t strutf8_utf16_size(const char* s)
 -	{
 -		size_t length = 0;
 -
 -		for (; *s; ++s)
 -		{
 -			unsigned char ch = static_cast<unsigned char>(*s);
 -
 -			if (ch < 0x80 || (ch >= 0xC0 && ch < 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
 -	static 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;
 -		
 -		struct gap
 -		{
 -			char* end;
 -			size_t size;
 -			
 -			gap(): end(0), size(0)
 -			{
 -			}
 -			
 -			// Push new gap, move s count bytes further (skipping the gap).
 -			// Collapse previous gap.
 -			void push(char*& s, size_t count)
 -			{
 -				if (end) // there was a gap already; collapse it
 -				{
 -					// Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
 -					memmove(end - size, end, s - end);
 -				}
 -				
 -				s += count; // end of current gap
 -				
 -				// "merge" two gaps
 -				end = s;
 -				size += count;
 -			}
 -			
 -			// Collapse all gaps, return past-the-end pointer
 -			char* flush(char* s)
 -			{
 -				if (end)
 -				{
 -					// Move [old_gap_end, current_pos) to [old_gap_start, ...)
 -					memmove(end - size, end, s - end);
 -
 -					return s - size;
 -				}
 -				else return s;
 -			}
 -		};
 -		
 -		static char* strconv_escape(char* s, gap& g)
 -		{
 -			char* stre = s + 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;
 -					}
 -
 -					s = strutf16_utf8(s, ucsc);
 -					
 -					g.push(s, stre - s);
 -					return stre;
 -				}
 -				case 'a':	// &a
 -				{
 -					++stre;
 -
 -					if (*stre == 'm') // &am
 -					{
 -						if (*++stre == 'p' && *++stre == ';') // &
 -						{
 -							*s++ = '&';
 -							++stre;
 -							
 -							g.push(s, stre - s);
 -							return stre;
 -						}
 -					}
 -					else if (*stre == 'p') // &ap
 -					{
 -						if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // '
 -						{
 -							*s++ = '\'';
 -							++stre;
 -
 -							g.push(s, stre - s);
 -							return stre;
 -						}
 -					}
 -					break;
 -				}
 -				case 'g': // &g
 -				{
 -					if (*++stre == 't' && *++stre == ';') // >
 -					{
 -						*s++ = '>';
 -						++stre;
 -						
 -						g.push(s, stre - s);
 -						return stre;
 -					}
 -					break;
 -				}
 -				case 'l': // &l
 -				{
 -					if (*++stre == 't' && *++stre == ';') // <
 -					{
 -						*s++ = '<';
 -						++stre;
 -						
 -						g.push(s, stre - s);
 -						return stre;
 -					}
 -					break;
 -				}
 -				case 'q': // &q
 -				{
 -					if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // "
 -					{
 -						*s++ = '"';
 -						++stre;
 -						
 -						g.push(s, stre - s);
 -						return stre;
 -					}
 -					break;
 -				}
 -			}
 -			
 -			return stre;
 -		}
 -
 -		static char* strconv_comment(char* s)
 -		{
 -			if (!*s) return 0;
 -			
 -			gap g;
 -			
 -			while (true)
 -			{
 -				while (!is_chartype(*s, ct_parse_comment)) ++s;
 -				
 -				if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
 -				{
 -					*s++ = '\n'; // replace first one with 0x0a
 -					
 -					if (*s == '\n') g.push(s, 1);
 -				}
 -				else if (*s == '-' && *(s+1) == '-' && *(s+2) == '>') // comment ends here
 -				{
 -					*g.flush(s) = 0;
 -					
 -					return s + 3;
 -				}
 -				else if (*s == 0)
 -				{
 -					return 0;
 -				}
 -				else ++s;
 -			}
 -		}
 -
 -		static char* strconv_cdata(char* s)
 -		{
 -			if (!*s) return 0;
 -			
 -			gap g;
 -			
 -			while (true)
 -			{
 -				while (!is_chartype(*s, ct_parse_cdata)) ++s;
 -				
 -				if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
 -				{
 -					*s++ = '\n'; // replace first one with 0x0a
 -					
 -					if (*s == '\n') g.push(s, 1);
 -				}
 -				else if (*s == ']' && *(s+1) == ']' && *(s+2) == '>') // CDATA ends here
 -				{
 -					*g.flush(s) = 0;
 -					
 -					return s + 1;
 -				}
 -				else if (*s == 0)
 -				{
 -					return 0;
 -				}
 -				else ++s;
 -			}
 -		}
 -		
 -		template <bool opt_escape, bool opt_eol> static char* strconv_pcdata(char* s)
 -		{
 -			if (!*s) return 0;
 -
 -			gap g;
 -			
 -			while (true)
 -			{
 -				while (!is_chartype(*s, ct_parse_pcdata)) ++s;
 -				
 -				if (opt_eol && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
 -				{
 -					*s++ = '\n'; // replace first one with 0x0a
 -					
 -					if (*s == '\n') g.push(s, 1);
 -				}
 -				else if (opt_escape && *s == '&')
 -				{
 -					s = strconv_escape(s, g);
 -				}
 -				else if (*s == '<') // PCDATA ends here
 -				{
 -					*g.flush(s) = 0;
 -					
 -					return s + 1;
 -				}
 -				else if (*s == 0)
 -				{
 -					return 0;
 -				}
 -				else ++s;
 -			}
 -		}
 -
 -		static char* strconv_pcdata(char* s, unsigned int opt_escape, unsigned int opt_eol)
 -		{
 -			if (opt_escape)
 -				return opt_eol ? strconv_pcdata<true, true>(s) : strconv_pcdata<true, false>(s);
 -			else
 -				return opt_eol ? strconv_pcdata<false, true>(s) : strconv_pcdata<false, false>(s);
 -		}
 -
 -		template <bool opt_escape, bool opt_wnorm, bool opt_wconv, bool opt_eol> static char* strconv_attr(char* s, char end_quote)
 -		{
 -			if (!*s) return 0;
 -			
 -			gap g;
 -
 -			// Trim whitespaces
 -			if (opt_wnorm)
 -			{
 -				char* str = s;
 -				
 -				while (is_chartype(*str, ct_space)) ++str;
 -				
 -				if (str != s)
 -					g.push(s, str - s);
 -			}
 -			
 -			while (true)
 -			{
 -				while (!is_chartype(*s, (opt_wnorm || opt_wconv) ? ct_parse_attr_ws : ct_parse_attr)) ++s;
 -				
 -				if (opt_escape && *s == '&')
 -				{
 -					s = strconv_escape(s, g);
 -				}
 -				else if (opt_wnorm && is_chartype(*s, ct_space))
 -				{
 -					*s++ = ' ';
 -		
 -					if (is_chartype(*s, ct_space))
 -					{
 -						char* str = s + 1;
 -						while (is_chartype(*str, ct_space)) ++str;
 -						
 -						g.push(s, str - s);
 -					}
 -				}
 -				else if (opt_wconv && is_chartype(*s, ct_space))
 -				{
 -					if (opt_eol)
 -					{
 -						if (*s == '\r')
 -						{
 -							*s++ = ' ';
 -					
 -							if (*s == '\n') g.push(s, 1);
 -						}
 -						else *s++ = ' ';
 -					}
 -					else *s++ = ' ';
 -				}
 -				else if (opt_eol && *s == '\r')
 -				{
 -					*s++ = '\n';
 -					
 -					if (*s == '\n') g.push(s, 1);
 -				}
 -				else if (*s == end_quote)
 -				{
 -					char* str = g.flush(s);
 -					
 -					if (opt_wnorm)
 -					{
 -						do *str-- = 0;
 -						while (is_chartype(*str, ct_space));
 -					}
 -					else *str = 0;
 -					
 -					return s + 1;
 -				}
 -				else if (!*s)
 -				{
 -					return 0;
 -				}
 -				else ++s;
 -			}
 -		}
 -	
 -		static void strconv_attr_setup(char* (*&func)(char*, char), unsigned int opt_escape, unsigned int opt_wnorm, unsigned int opt_wconv, unsigned int opt_eol)
 -		{
 -			if (opt_eol)
 -			{
 -				if (opt_wconv)
 -				{
 -					if (opt_escape)
 -					{
 -						if (opt_wnorm) func = &strconv_attr<true, true, true, true>;
 -						else func = &strconv_attr<true, false, true, true>;
 -					}
 -					else
 -					{
 -						if (opt_wnorm) func = &strconv_attr<false, true, true, true>;
 -						else func = &strconv_attr<false, false, true, true>;
 -					}
 -				}
 -				else
 -				{
 -					if (opt_escape)
 -					{
 -						if (opt_wnorm) func = &strconv_attr<true, true, false, true>;
 -						else func = &strconv_attr<true, false, false, true>;
 -					}
 -					else
 -					{
 -						if (opt_wnorm) func = &strconv_attr<false, true, false, true>;
 -						else func = &strconv_attr<false, false, false, true>;
 -					}
 -				}
 -			}
 -			else
 -			{
 -				if (opt_wconv)
 -				{
 -					if (opt_escape)
 -					{
 -						if (opt_wnorm) func = &strconv_attr<true, true, true, false>;
 -						else func = &strconv_attr<true, false, true, false>;
 -					}
 -					else
 -					{
 -						if (opt_wnorm) func = &strconv_attr<false, true, true, false>;
 -						else func = &strconv_attr<false, false, true, false>;
 -					}
 -				}
 -				else
 -				{
 -					if (opt_escape)
 -					{
 -						if (opt_wnorm) func = &strconv_attr<true, true, false, false>;
 -						else func = &strconv_attr<true, false, false, false>;
 -					}
 -					else
 -					{
 -						if (opt_wnorm) func = &strconv_attr<false, true, false, false>;
 -						else func = &strconv_attr<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 0; // 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 0;
 -			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(is_chartype(*s, ct_space)) ++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)
 -			{
 -			}
 -		}
 -		
 -		// 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;
 -
 -			char* (*strconv_attribute)(char*, char);
 -
 -			strconv_attr_setup(strconv_attribute, OPTSET(parse_escapes), OPTSET(parse_wnorm_attribute), OPTSET(parse_wconv_attribute), OPTSET(parse_eol));
 -
 -			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(*s == '<'); // Find the next '<'.
 -				if(*s == '<')
 -				{
 -					++s;
 -				LOC_CLASSIFY: // What kind of element?
 -					if(*s == '?') // '<?...'
 -					{
 -						++s;
 -						if(is_chartype(*s, ct_symbol) && OPTSET(parse_pi))
 -						{
 -							mark = s;
 -							SCANWHILE(is_chartype(*s, ct_symbol)); // Read PI target
 -							ENDSEG();
 -							
 -							PUSHNODE(node_pi); // Append a new node on the tree.
 -
 -							cursor->name = mark;
 -
 -							if (is_chartype(ch, ct_space))
 -							{
 -								SKIPWS();
 -
 -								mark = s;
 -							}
 -							else mark = 0;
 -
 -							SCANFOR(*s == '?' && *(s+1) == '>'); // Look for '?>'.
 -							ENDSEG();
 -
 -							cursor->value = mark;
 -
 -							POPNODE();
 -
 -							goto LOC_LEAVE;
 -						}
 -						else // Bad PI or parse_pi not set.
 -						{
 -							SCANFOR(*s == '?' && *(s+1) == '>'); // Look for '?>'.
 -							++s;
 -							goto LOC_LEAVE;
 -						}
 -					}
 -					else if(*s == '!') // '<!...'
 -					{
 -						++s;
 -						if(*s == '-') // '<!-...'
 -						{
 -							++s;
 -							if(*s == '-') // '<!--...'
 -							{
 -								++s;
 -								
 -								if (OPTSET(parse_comments))
 -								{
 -									PUSHNODE(node_comment); // Append a new node on the tree.
 -									cursor->value = s; // Save the offset.
 -								}
 -
 -								if (OPTSET(parse_eol) && OPTSET(parse_comments))
 -								{
 -									s = strconv_comment(s);
 -									
 -									if (!s) return s;
 -								}
 -								else
 -								{
 -									// Scan for terminating '-->'.
 -									SCANFOR(*s == '-' && *(s+1) == '-' && *(s+2) == '>');
 -								
 -									if (OPTSET(parse_comments))
 -										*s = 0; // Zero-terminate this segment at the first terminating '-'.
 -									
 -									s += 2; // Step over the '\0-'.
 -								}
 -								
 -								if (OPTSET(parse_comments))
 -								{
 -									POPNODE(); // Pop since this is a standalone.
 -								}
 -								
 -								goto LOC_LEAVE; // Look for any following PCDATA.
 -							}
 -						}
 -						else if(*s == '[')
 -						{
 -							// '<![CDATA[...'
 -							if(*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')
 -							{
 -								++s;
 -								if(OPTSET(parse_cdata))
 -								{
 -									PUSHNODE(node_cdata); // Append a new node on the tree.
 -									cursor->value = s; // Save the offset.
 -
 -									if (OPTSET(parse_eol))
 -									{
 -										s = strconv_cdata(s);
 -										
 -										if (!s) return s;
 -									}
 -									else
 -									{
 -										// Scan for terminating ']]>'.
 -										SCANFOR(*s == ']' && *(s+1) == ']' && *(s+2) == '>');
 -										ENDSEG(); // Zero-terminate this segment.
 -									}
 -
 -									POPNODE(); // Pop since this is a standalone.
 -								}
 -								else // Flagged for discard, but we still have to scan for the terminator.
 -								{
 -									// Scan for terminating ']]>'.
 -									SCANFOR(*s == ']' && *(s+1) == ']' && *(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(*s == '\'' || *s == '"' || *s == '[' || *s == '>');
 -							if(*s == '\'' || *s == '"') // '...SYSTEM "..."
 -							{
 -								ch = *s++;
 -								SCANFOR(*s == ch);
 -								++s;
 -								goto LOC_DOCTYPE;
 -							}
 -							if(*s == '[') // '...[...'
 -							{
 -								++s;
 -								unsigned int bd = 1; // Bracket depth counter.
 -								while(*s!=0) // Loop till we're out of all brackets.
 -								{
 -									if(*s == ']') --bd;
 -									else if(*s == '[') ++bd;
 -									if(bd == 0) break;
 -									++s;
 -								}
 -								// Note: 's' now points to end of DTD, i.e.: ']'.
 -								SCANFOR(*s == '>');
 -								continue;
 -							}
 -							// Fall-through
 -							continue;
 -						}
 -					}
 -					else if(is_chartype(*s, ct_symbol)) // '<#...'
 -					{
 -						cursor = append_node(cursor); // Append a new node to the tree.
 -
 -						cursor->name = s;
 -						SCANWHILE(is_chartype(*s, ct_symbol)); // Scan for a terminator.
 -						ENDSEG(); // Save char in 'ch', terminate & step over.
 -						if (*s!=0 && ch == '/') // '</...'
 -						{
 -							SCANFOR(*s == '>'); // Scan for '>'
 -							POPNODE(); // Pop.
 -							goto LOC_LEAVE;
 -						}
 -						else if(*s!=0 && !is_chartype(ch, ct_space))
 -						{
 -							if (ch != '>') SCANWHILE(*s != '>');
 -							if (!*s) return s;
 -							goto LOC_PCDATA; // No attributes, so scan for PCDATA.
 -						}
 -						else if(*s!=0 && is_chartype(ch, ct_space))
 -						{
 -							SKIPWS(); // Eat any whitespace.
 -						LOC_ATTRIBUTE:
 -							if(is_chartype(*s, ct_symbol)) // <... #...
 -							{
 -								xml_attribute_struct* a = append_attribute(cursor); // Make space for this attribute.
 -								a->name = s; // Save the offset.
 -								SCANWHILE(is_chartype(*s, ct_symbol)); // Scan for a terminator.
 -								ENDSEG(); // Save char in 'ch', terminate & step over.
 -								if(*s!=0 && is_chartype(ch, ct_space)) SKIPWS(); // Eat any whitespace.
 -								if(*s!=0 && (ch == '=' || *s == '=')) // '<... #=...'
 -								{
 -									if(*s == '=') ++s;
 -									SKIPWS(); // Eat any whitespace.
 -									if(*s == '\'' || *s == '"') // '<... #="...'
 -									{
 -										ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
 -										++s; // Step over the quote.
 -										a->value = s; // Save the offset.
 -
 -										s = strconv_attribute(s, ch);
 -										
 -										if (!s) return s;
 -
 -										if(*s == '>')
 -										{
 -											++s;
 -											goto LOC_PCDATA;
 -										}
 -										else if(*s == '/')
 -										{
 -											++s;
 -											POPNODE();
 -											SKIPWS(); // Eat any whitespace.
 -											goto LOC_LEAVE;
 -										}
 -										else if(is_chartype(*s, ct_space)) // This may indicate a following attribute.
 -										{
 -											SKIPWS(); // Eat any whitespace.
 -											goto LOC_ATTRIBUTE; // Go scan for additional attributes.
 -										}
 -									}
 -								}
 -								goto LOC_ATTRIBUTE;
 -							}
 -
 -							SCANWHILE(*s != '>' && *s != '/');
 -						}
 -					LOC_LEAVE:
 -						if(*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)) && *s == '<')
 -							{
 -								if(*(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.
 -
 -								s = strconv_pcdata(s, OPTSET(parse_escapes), OPTSET(parse_eol));
 -								
 -								if (!s) return s;
 -								
 -								POPNODE(); // Pop since this is a standalone.
 -							}
 -							else
 -							{
 -								SCANFOR(*s == '<'); // '...<'
 -							}
 -
 -							// We're after '<...', otherwise we would not get here
 -							if(*s == '/') goto LOC_CLOSE;
 -							else if(*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(*s == '/') // '.../'
 -						{
 -							++s;
 -							if(*s == '>') // '.../>'
 -							{
 -								POPNODE(); // Pop.
 -								goto LOC_LEAVE;
 -							}
 -						}
 -					}
 -					// Fall-through B.
 -					else if(*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 && is_chartype(*tagname, ct_symbol))
 -									{
 -										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 && is_chartype(*s, ct_symbol))
 -							{
 -								if (*s++ != *name++) return s;
 -							}
 -							if (*name) return s;
 -							
 -							POPNODE(); // Pop.
 -						}
 -						else
 -						{
 -							SCANFOR(*s == '>'); // '...>'
 -							POPNODE(); // Pop.
 -						}
 -
 -						SKIPWS();
 -												
 -						goto LOC_LEAVE;
 -					}
 -				}
 -			}
 -			return s;
 -		}
 -		
 -	private:
 -		const xml_parser_impl& operator=(const xml_parser_impl&);
 -	};
 -
 -	// Compare lhs with [rhs_begin, rhs_end)
 -	static 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.
 -	static 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;
 -	}
 -
 -	// Wildcard pattern match.
 -	static 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 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;
 -		}
 -	}
 -
 -	int strcmp(const char* lhs, const char* rhs)
 -	{
 -		return ::strcmp(lhs, rhs);
 -	}
 -
 -	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): type(type), parent(0), name(0), value(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), first_attribute(0), last_attribute(0)
 -	{
 -	}
 -
 -	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(0)
 -	{
 -	}
 -
 -	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 == 0);
 -	}
 -
 -	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
 -	{
 -		if (!empty())
 -			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 "";
 -	}
 -
 -	const char* xml_node::child_value(const char* name) const
 -	{
 -		return child(name).child_value();
 -	}
 -
 -	const char* xml_node::child_value_w(const char* name) const
 -	{
 -		return child_w(name).child_value();
 -	}
 -
 -	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.
 -	}
 -
 + +#include <stdlib.h>
 +#include <stdio.h> + +#include <new> + +#ifndef PUGIXML_NO_STL +# include <fstream> +#endif + +#ifdef _MSC_VER +#	pragma warning(disable: 4127) // conditional expression is constant +#	pragma warning(disable: 4996) // this function or variable may be unsafe +#endif + +#ifdef __BORLANDC__ +#	pragma warn -8008 // condition is always false +#	pragma warn -8066 // unreachable code +#endif + +#define STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed; } + +namespace pugi +{ +	class xml_allocator +	{ +	public: +		xml_allocator(xml_memory_block* root): _root(root) +		{ +		} + +		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; + +		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; + +				return _root->data; +			} +		} +	}; + +	/// A 'name=value' XML attribute structure. +	struct xml_attribute_struct +	{ +		/// Default ctor +		xml_attribute_struct(): name_insitu(true), value_insitu(true), document_order(0), name(0), value(0), prev_attribute(0), next_attribute(0) +		{ +		} + +		void free() +		{ +			if (!name_insitu) delete[] name; +			if (!value_insitu) delete[] value; +		} +	 +		bool		name_insitu : 1; +		bool		value_insitu : 1; +		unsigned int document_order : 30; ///< Document order value + +		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): type(type), name_insitu(true), value_insitu(true), document_order(0), parent(0), name(0), value(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), first_attribute(0), last_attribute(0) +		{ +		} + +		void free() +		{ +			if (!name_insitu) delete[] name; +			if (!value_insitu) delete[] value; + +			for (xml_node_struct* cur = first_child; cur; cur = cur->next_sibling) +				cur->free(); +			 +			for (xml_attribute_struct* cur = first_attribute; cur; cur = cur->next_attribute) +				cur->free(); +		} + +		xml_node_struct* append_node(xml_allocator& alloc, xml_node_type type = node_element) +		{ +			xml_node_struct* child = alloc.allocate<xml_node_struct>(type); +			child->parent = this; +			 +			if (last_child) +			{ +				last_child->next_sibling = child; +				child->prev_sibling = last_child; +				last_child = child; +			} +			else first_child = last_child = child; +			 +			return child; +		} + +		xml_attribute_struct* append_attribute(xml_allocator& alloc) +		{ +			xml_attribute_struct* a = alloc.allocate<xml_attribute_struct>(); + +			if (last_attribute) +			{ +				last_attribute->next_attribute = a; +				a->prev_attribute = last_attribute; +				last_attribute = a; +			} +			else first_attribute = last_attribute = a; +			 +			return a; +		} + +		unsigned int			type : 3;				///< Node type; see xml_node_type. +		bool					name_insitu : 1; +		bool					value_insitu : 1; +		unsigned int			document_order : 27;	///< Document order value + +		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 +	}; + +	struct xml_document_struct: public xml_node_struct +	{ +		xml_document_struct(): xml_node_struct(node_document), allocator(0) +		{ +		} + +		xml_allocator allocator; +	}; +} + +namespace +{	 +	using namespace pugi; + +	const unsigned char UTF8_BYTE_MASK = 0xBF; +	const unsigned char UTF8_BYTE_MARK = 0x80; +	const unsigned char UTF8_BYTE_MASK_READ = 0x3F; +	const unsigned char UTF8_FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +	enum chartype +	{ +		ct_parse_pcdata = 1,	// \0, &, \r, < +		ct_parse_attr = 2,		// \0, &, \r, ', " +		ct_parse_attr_ws = 4,	// \0, &, \r, ', ", \n, space, tab +		ct_space = 8,			// \r, \n, space, tab +		ct_parse_cdata = 16,	// \0, ], >, \r +		ct_parse_comment = 32,	// \0, -, >, \r +		ct_symbol = 64,			// Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . +		ct_start_symbol = 128	// Any symbol > 127, a-z, A-Z, _, : +	}; + +	const unsigned char chartype_table[256] = +	{ +		55,  0,   0,   0,   0,   0,   0,   0,      0,   12,  12,  0,   0,   63,  0,   0,   // 0-15 +		0,   0,   0,   0,   0,   0,   0,   0,      0,   0,   0,   0,   0,   0,   0,   0,   // 16-31 +		12,  0,   6,   0,   0,   0,   7,   6,      0,   0,   0,   0,   0,   96,  64,  0,   // 32-47 +		64,  64,  64,  64,  64,  64,  64,  64,     64,  64,  192, 0,   1,   0,   48,  0,   // 48-63 +		0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 64-79 +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0,   0,   16,  0,   192, // 80-95 +		0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 96-111 +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0, 0, 0, 0, 0,           // 112-127 + +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 128+ +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, +		192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192 +	}; +	 +	bool is_chartype(char c, chartype ct) +	{ +		return !!(chartype_table[static_cast<unsigned char>(c)] & ct); +	} + +	bool strcpy_insitu(char*& dest, bool& insitu, const char* source) +	{ +		size_t source_size = strlen(source); + +		if (dest && strlen(dest) >= source_size) +		{ +			strcpy(dest, source); +			 +			return true; +		} +		else +		{ +			char* buf; + +			try +			{ +				buf = new char[source_size + 1]; +			} +			catch (const std::bad_alloc&) +			{ +				return false; +			} + +			strcpy(buf, source); + +			if (!insitu) delete[] dest; +			 +			dest = buf; +			insitu = false; + +			return true; +		} +	} + +	// 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) +		{ +			unsigned int ch = *s; + +			if (ch < 0x80) length += 1; +			else if (ch < 0x800) length += 2; +			else if (ch < 0x10000) length += 3; +			else if (ch < 0x200000) length += 4; +		} + +		return length; +	} + +	// Write utf16 char to stream, return position after the last written char +	// \return position after last char +	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) +		{ +			unsigned char ch = static_cast<unsigned char>(*s); + +			if (ch < 0x80 || (ch >= 0xC0 && ch < 0xFC)) ++length; +		} + +		return length; +	} + +	// Read utf16 char from utf8 stream, return position after the last read 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); +	} +  #ifndef PUGIXML_NO_STL
 -	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;
 -	}
 +	template <bool quotes, bool utf8> void text_output_escaped(std::ostream& os, const char* s) +	{ +		while (*s) +		{ +			const char* prev = s; +			 +			// While *s is a usual symbol +			while (*s && *s != '&' && *s != '<' && *s != '>' && ((*s != '"' && *s != '\'') || !quotes) +					&& (*s >= 32 || *s == '\r' || *s == '\n' || *s == '\t')) +				++s; +		 +			if (prev != s) os.write(prev, static_cast<std::streamsize>(s - prev)); + +			switch (*s) +			{ +				case 0: break; +				case '&': +					os << "&"; +					++s; +					break; +				case '<': +					os << "<"; +					++s; +					break; +				case '>': +					os << ">"; +					++s; +					break; +				case '"': +					os << """; +					++s; +					break; +				case '\'': +					os << "'"; +					++s; +					break; +				default: // s is not a usual symbol +				{ +					unsigned int ch; +					 +					if (utf8) +						s = strutf8_utf16(s, ch); +					else +						ch = (unsigned char)*s++; + +					os << "&#" << ch << ";"; +				} +			} +		} +	}  #endif
 -
 -	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): _prev(prev), _wrap(ref)
 -	{
 -	}
 -
 -	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): _prev(prev), _wrap(ref)
 -	{
 -	}
 -
 -	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): _buffer(0), _xmldoc(0), _optmsk(optmsk)
 -	{
 -	}
 -
 -#ifndef PUGIXML_NO_STL
 -	xml_parser::xml_parser(std::istream& stream,unsigned int optmsk): _buffer(0), _xmldoc(0), _optmsk(optmsk)
 -	{
 -		parse(stream, _optmsk);
 -	}
 -#endif
 -
 -	xml_parser::xml_parser(char* xmlstr,unsigned int optmsk): _buffer(0), _xmldoc(0), _optmsk(optmsk)
 -	{
 -		parse(xmlstr, _optmsk);
 -	}
 -
 -	xml_parser::xml_parser(const transfer_ownership_tag& tag, char* xmlstr ,unsigned int optmsk): _buffer(0), _xmldoc(0), _optmsk(optmsk)
 -	{
 -		parse(tag, xmlstr, _optmsk);
 -	}
 -
 -	xml_parser::~xml_parser()
 -	{
 -		free();
 -	}
 -
 -	void xml_parser::free()
 -	{
 -		delete _buffer;
 -		_buffer = 0;
 -
 -		xml_memory_block* current = _memory.next;
 -
 -		while (current)
 -		{
 -			xml_memory_block* next = current->next;
 -			delete current;
 -			current = next;
 -		}
 -		
 -		_memory.next = 0;
 -		_memory.size = 0;
 -	}
 -
 -	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;
 -	}
 -
 -#ifndef PUGIXML_NO_STL
 -	void xml_parser::parse(std::istream& stream,unsigned int optmsk)
 -	{
 -		free();
 -
 -		std::streamoff length = 0, pos = stream.tellg();
 -		stream.seekg(0, std::ios_base::end);
 -		length = stream.tellg();
 -		stream.seekg(pos, std::ios_base::beg);
 -
 -		char* s = new char[length + 1];
 -		stream.read(s, length);
 -		s[length] = 0;
 -
 -		parse(transfer_ownership_tag(), s, optmsk); // Parse the input string.
 -	}
 -#endif
 -
 -	char* xml_parser::parse(char* xmlstr,unsigned int optmsk)
 -	{
 -		free();
 -
 -		if(!xmlstr) return 0;
 -
 -		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.
 -	}
 -	
 -	char* xml_parser::parse(const transfer_ownership_tag&, char* xmlstr,unsigned int optmsk)
 -	{
 -		free();
 -
 -		if(!xmlstr) return 0;
 -
 -		_buffer = xmlstr;
 -
 -		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.
 -	}
 -
 -#ifndef PUGIXML_NO_STL
 -	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;
 -	}
 -#endif
 -}
 + +	struct gap +	{ +		char* end; +		size_t size; +			 +		gap(): end(0), size(0) +		{ +		} +			 +		// Push new gap, move s count bytes further (skipping the gap). +		// Collapse previous gap. +		void push(char*& s, size_t count) +		{ +			if (end) // there was a gap already; collapse it +			{ +				// Move [old_gap_end, new_gap_start) to [old_gap_start, ...) +				std::memmove(end - size, end, s - end); +			} +				 +			s += count; // end of current gap +				 +			// "merge" two gaps +			end = s; +			size += count; +		} +			 +		// Collapse all gaps, return past-the-end pointer +		char* flush(char* s) +		{ +			if (end) +			{ +				// Move [old_gap_end, current_pos) to [old_gap_start, ...) +				std::memmove(end - size, end, s - end); + +				return s - size; +			} +			else return s; +		} +	}; +	 +	char* strconv_escape(char* s, gap& g) +	{ +		char* stre = s + 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 if (*stre == ';') +							break; +						else // cancel +							return stre; +					} + +					if (*stre != ';') return stre; +						 +					++stre; +				} +				else	// &#... (dec code) +				{ +					while (*stre >= '0' && *stre <= '9') +						ucsc = 10 * ucsc + (*stre++ - '0'); + +					if (*stre != ';') return stre; +						 +					++stre; +				} + +				s = strutf16_utf8(s, ucsc); +					 +				g.push(s, stre - s); +				return stre; +			} +			case 'a':	// &a +			{ +				++stre; + +				if (*stre == 'm') // &am +				{ +					if (*++stre == 'p' && *++stre == ';') // & +					{ +						*s++ = '&'; +						++stre; +							 +						g.push(s, stre - s); +						return stre; +					} +				} +				else if (*stre == 'p') // &ap +				{ +					if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' +					{ +						*s++ = '\''; +						++stre; + +						g.push(s, stre - s); +						return stre; +					} +				} +				break; +			} +			case 'g': // &g +			{ +				if (*++stre == 't' && *++stre == ';') // > +				{ +					*s++ = '>'; +					++stre; +					 +					g.push(s, stre - s); +					return stre; +				} +				break; +			} +			case 'l': // &l +			{ +				if (*++stre == 't' && *++stre == ';') // < +				{ +					*s++ = '<'; +					++stre; +						 +					g.push(s, stre - s); +					return stre; +				} +				break; +			} +			case 'q': // &q +			{ +				if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " +				{ +					*s++ = '"'; +					++stre; +					 +					g.push(s, stre - s); +					return stre; +				} +				break; +			} +		} +		 +		return stre; +	} + +	char* strconv_comment(char* s) +	{ +		if (!*s) return 0; +		 +		gap g; +		 +		while (true) +		{ +			while (!is_chartype(*s, ct_parse_comment)) ++s; +		 +			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair +			{ +				*s++ = '\n'; // replace first one with 0x0a +				 +				if (*s == '\n') g.push(s, 1); +			} +			else if (*s == '-' && *(s+1) == '-' && *(s+2) == '>') // comment ends here +			{ +				*g.flush(s) = 0; +				 +				return s + 3; +			} +			else if (*s == 0) +			{ +				return 0; +			} +			else ++s; +		} +	} + +	char* strconv_cdata(char* s) +	{ +		if (!*s) return 0; +			 +		gap g; +			 +		while (true) +		{ +			while (!is_chartype(*s, ct_parse_cdata)) ++s; +			 +			if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair +			{ +				*s++ = '\n'; // replace first one with 0x0a +				 +				if (*s == '\n') g.push(s, 1); +			} +			else if (*s == ']' && *(s+1) == ']' && *(s+2) == '>') // CDATA ends here +			{ +				*g.flush(s) = 0; +				 +				return s + 1; +			} +			else if (*s == 0) +			{ +				return 0; +			} +			else ++s; +		} +	} +		 +	template <bool opt_eol, bool opt_escape> char* strconv_pcdata_t(char* s) +	{ +		if (!*s) return 0; + +		gap g; +		 +		while (true) +		{ +			while (!is_chartype(*s, ct_parse_pcdata)) ++s; +				 +			if (opt_eol && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair +			{ +				*s++ = '\n'; // replace first one with 0x0a +				 +				if (*s == '\n') g.push(s, 1); +			} +			else if (opt_escape && *s == '&') +			{ +				s = strconv_escape(s, g); +			} +			else if (*s == '<') // PCDATA ends here +			{ +				*g.flush(s) = 0; +				 +				return s + 1; +			} +			else if (*s == 0) +			{ +				return s; +			} +			else ++s; +		} +	} + +	char* strconv_pcdata(char* s, unsigned int optmask) +	{ +		STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20); + +		switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes) +		{ +		case 0: return strconv_pcdata_t<0, 0>(s); +		case 1: return strconv_pcdata_t<0, 1>(s); +		case 2: return strconv_pcdata_t<1, 0>(s); +		case 3: return strconv_pcdata_t<1, 1>(s); +		default: return 0; // should not get here +		} +	} + +	template <bool opt_wconv, bool opt_wnorm, bool opt_eol, bool opt_escape> char* strconv_attribute_t(char* s, char end_quote) +	{ +		if (!*s) return 0; +			 +		gap g; + +		// Trim whitespaces +		if (opt_wnorm) +		{ +			char* str = s; +			 +			while (is_chartype(*str, ct_space)) ++str; +			 +			if (str != s) +				g.push(s, str - s); +		} +			 +		while (true) +		{ +			while (!is_chartype(*s, (opt_wnorm || opt_wconv) ? ct_parse_attr_ws : ct_parse_attr)) ++s; +			 +			if (opt_escape && *s == '&') +			{ +				s = strconv_escape(s, g); +			} +			else if (opt_wnorm && is_chartype(*s, ct_space)) +			{ +				*s++ = ' '; +	 +				if (is_chartype(*s, ct_space)) +				{ +					char* str = s + 1; +					while (is_chartype(*str, ct_space)) ++str; +					 +					g.push(s, str - s); +				} +			} +			else if (opt_wconv && is_chartype(*s, ct_space)) +			{ +				if (opt_eol) +				{ +					if (*s == '\r') +					{ +						*s++ = ' '; +				 +						if (*s == '\n') g.push(s, 1); +					} +					else *s++ = ' '; +				} +				else *s++ = ' '; +			} +			else if (opt_eol && *s == '\r') +			{ +				*s++ = '\n'; +				 +				if (*s == '\n') g.push(s, 1); +			} +			else if (*s == end_quote) +			{ +				char* str = g.flush(s); +				 +				if (opt_wnorm) +				{ +					do *str-- = 0; +					while (is_chartype(*str, ct_space)); +				} +				else *str = 0; +			 +				return s + 1; +			} +			else if (!*s) +			{ +				return 0; +			} +			else ++s; +		} +	} +	 +	char* strconv_attribute(char* s, char end_quote, unsigned int optmask) +	{ +		STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wnorm_attribute == 0x40 && parse_wconv_attribute == 0x80); +	 +		switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) +		{ +		case 0: return strconv_attribute_t <0, 0, 0, 0>(s, end_quote); +		case 1: return strconv_attribute_t <0, 0, 0, 1>(s, end_quote); +		case 2: return strconv_attribute_t <0, 0, 1, 0>(s, end_quote); +		case 3: return strconv_attribute_t <0, 0, 1, 1>(s, end_quote); +		case 4: return strconv_attribute_t <0, 1, 0, 0>(s, end_quote); +		case 5: return strconv_attribute_t <0, 1, 0, 1>(s, end_quote); +		case 6: return strconv_attribute_t <0, 1, 1, 0>(s, end_quote); +		case 7: return strconv_attribute_t <0, 1, 1, 1>(s, end_quote); +		case 8: return strconv_attribute_t <1, 0, 0, 0>(s, end_quote); +		case 9: return strconv_attribute_t <1, 0, 0, 1>(s, end_quote); +		case 10: return strconv_attribute_t<1, 0, 1, 0>(s, end_quote); +		case 11: return strconv_attribute_t<1, 0, 1, 1>(s, end_quote); +		case 12: return strconv_attribute_t<1, 1, 0, 0>(s, end_quote); +		case 13: return strconv_attribute_t<1, 1, 0, 1>(s, end_quote); +		case 14: return strconv_attribute_t<1, 1, 1, 0>(s, end_quote); +		case 15: return strconv_attribute_t<1, 1, 1, 1>(s, end_quote); +		default: return 0; // should not get here +		} +	} + +	struct xml_parser +	{ +		xml_allocator& alloc; +		 +		// Parser utilities. +		#define SKIPWS()			{ while (is_chartype(*s, ct_space)) ++s; } +		#define OPTSET(OPT)			( optmsk & OPT ) +		#define PUSHNODE(TYPE)		{ cursor = cursor->append_node(alloc,TYPE); } +		#define POPNODE()			{ cursor = cursor->parent; } +		#define SCANFOR(X)			{ while (*s != 0 && !(X)) ++s; } +		#define SCANWHILE(X)		{ while ((X)) ++s; } +		#define ENDSEG()			{ ch = *s; *s = 0; ++s; } +		#define CHECK_ERROR()		{ if (*s == 0) return false; } +		 +		xml_parser(xml_allocator& alloc): alloc(alloc) +		{ +		} +		 +		bool parse(char* s, xml_node_struct* xmldoc, unsigned int optmsk = parse_default) +		{ +			if (!s || !xmldoc) return false; + +			// UTF-8 BOM +			if ((unsigned char)*s == 0xEF && (unsigned char)*(s+1) == 0xBB && (unsigned char)*(s+2) == 0xBF) +				s += 3; +				 +			char ch = 0; +			xml_node_struct* cursor = xmldoc; +			char* mark = s; + +			while (*s != 0) +			{ +				if (*s == '<') +				{ +					++s; + +				LOC_TAG: +					if (*s == '?') // '<?...' +					{ +						++s; + +						if (!is_chartype(*s, ct_start_symbol)) // bad PI +							return false; +						else if (OPTSET(parse_pi)) +						{ +							mark = s; +							SCANWHILE(is_chartype(*s, ct_symbol)); // Read PI target +							CHECK_ERROR(); + +							if (!is_chartype(*s, ct_space) && *s != '?') // Target has to end with space or ? +								return false; + +							ENDSEG(); +							CHECK_ERROR(); + +							if (ch == '?') // nothing except target present +							{ +								if (*s != '>') return false; +								++s; + +								// stricmp / strcasecmp is not portable +								if ((mark[0] == 'x' || mark[0] == 'X') && (mark[1] == 'm' || mark[1] == 'M') +									&& (mark[2] == 'l' || mark[2] == 'L') && mark[3] == 0) +								{ +								} +								else +								{ +									PUSHNODE(node_pi); // Append a new node on the tree. + +									cursor->name = mark; + +									POPNODE(); +								} +							} +							// stricmp / strcasecmp is not portable +							else if ((mark[0] == 'x' || mark[0] == 'X') && (mark[1] == 'm' || mark[1] == 'M') +								&& (mark[2] == 'l' || mark[2] == 'L') && mark[3] == 0) +							{ +								SCANFOR(*s == '?' && *(s+1) == '>'); // Look for '?>'. +								CHECK_ERROR(); +								s += 2; +							} +							else +							{ +								PUSHNODE(node_pi); // Append a new node on the tree. + +								cursor->name = mark; + +								if (is_chartype(ch, ct_space)) +								{ +									SKIPWS(); +									CHECK_ERROR(); +	 +									mark = s; +								} +								else mark = 0; + +								SCANFOR(*s == '?' && *(s+1) == '>'); // Look for '?>'. +								CHECK_ERROR(); + +								ENDSEG(); +								CHECK_ERROR(); + +								++s; // Step over > + +								cursor->value = mark; + +								POPNODE(); +							} +						} +						else // not parsing PI +						{ +							SCANFOR(*s == '?' && *(s+1) == '>'); // Look for '?>'. +							CHECK_ERROR(); + +							s += 2; +						} +					} +					else if (*s == '!') // '<!...' +					{ +						++s; + +						if (*s == '-') // '<!-...' +						{ +							++s; + +							if (*s == '-') // '<!--...' +							{ +								++s; +								 +								if (OPTSET(parse_comments)) +								{ +									PUSHNODE(node_comment); // Append a new node on the tree. +									cursor->value = s; // Save the offset. +								} + +								if (OPTSET(parse_eol) && OPTSET(parse_comments)) +								{ +									s = strconv_comment(s); +									 +									if (!s) return false; +								} +								else +								{ +									// Scan for terminating '-->'. +									SCANFOR(*s == '-' && *(s+1) == '-' && *(s+2) == '>'); +									CHECK_ERROR(); +								 +									if (OPTSET(parse_comments)) +										*s = 0; // Zero-terminate this segment at the first terminating '-'. +									 +									s += 3; // Step over the '\0->'. +								} +								 +								if (OPTSET(parse_comments)) +								{ +									POPNODE(); // Pop since this is a standalone. +								} +							} +							else return false; +						} +						else if(*s == '[') +						{ +							// '<![CDATA[...' +							if(*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[') +							{ +								++s; +								 +								if (OPTSET(parse_cdata)) +								{ +									PUSHNODE(node_cdata); // Append a new node on the tree. +									cursor->value = s; // Save the offset. + +									if (OPTSET(parse_eol)) +									{ +										s = strconv_cdata(s); +										 +										if (!s) return false; +									} +									else +									{ +										// Scan for terminating ']]>'. +										SCANFOR(*s == ']' && *(s+1) == ']' && *(s+2) == '>'); +										CHECK_ERROR(); + +										ENDSEG(); // Zero-terminate this segment. +										CHECK_ERROR(); +									} + +									POPNODE(); // Pop since this is a standalone. +								} +								else // Flagged for discard, but we still have to scan for the terminator. +								{ +									// Scan for terminating ']]>'. +									SCANFOR(*s == ']' && *(s+1) == ']' && *(s+2) == '>'); +									CHECK_ERROR(); + +									++s; +								} + +								s += 2; // Step over the last ']>'. +							} +							else return false; +						} +						else if (*s=='D' && *++s=='O' && *++s=='C' && *++s=='T' && *++s=='Y' && *++s=='P' && *++s=='E') +						{ +							++s; + +							SKIPWS(); // Eat any whitespace. +							CHECK_ERROR(); + +						LOC_DOCTYPE: +							SCANFOR(*s == '\'' || *s == '"' || *s == '[' || *s == '>'); +							CHECK_ERROR(); + +							if (*s == '\'' || *s == '"') // '...SYSTEM "..." +							{ +								ch = *s++; +								SCANFOR(*s == ch); +								CHECK_ERROR(); + +								++s; +								goto LOC_DOCTYPE; +							} + +							if(*s == '[') // '...[...' +							{ +								++s; +								unsigned int bd = 1; // Bracket depth counter. +								while (*s!=0) // Loop till we're out of all brackets. +								{ +									if (*s == ']') --bd; +									else if (*s == '[') ++bd; +									if (bd == 0) break; +									++s; +								} +							} +							 +							SCANFOR(*s == '>'); +							CHECK_ERROR(); + +							++s; +						} +						else return false; +					} +					else if (is_chartype(*s, ct_start_symbol)) // '<#...' +					{ +						PUSHNODE(node_element); // Append a new node to the tree. + +						cursor->name = s; + +						SCANWHILE(is_chartype(*s, ct_symbol)); // Scan for a terminator. +						CHECK_ERROR(); + +						ENDSEG(); // Save char in 'ch', terminate & step over. +						CHECK_ERROR(); + +						if (ch == '/') // '<#.../' +						{ +							if (*s != '>') return false; +							 +							POPNODE(); // Pop. + +							++s; +						} +						else if (ch == '>') +						{ +							// end of tag +						} +						else if (is_chartype(ch, ct_space)) +						{ +						    while (*s) +						    { +								SKIPWS(); // Eat any whitespace. +								CHECK_ERROR(); +						 +								if (is_chartype(*s, ct_start_symbol)) // <... #... +								{ +									xml_attribute_struct* a = cursor->append_attribute(alloc); // Make space for this attribute. +									a->name = s; // Save the offset. + +									SCANWHILE(is_chartype(*s, ct_symbol)); // Scan for a terminator. +									CHECK_ERROR(); + +									ENDSEG(); // Save char in 'ch', terminate & step over. +									CHECK_ERROR(); + +									if (is_chartype(ch, ct_space)) +									{ +										SKIPWS(); // Eat any whitespace. +										CHECK_ERROR(); + +										ch = *s; +										++s; +									} +									 +									if (ch == '=') // '<... #=...' +									{ +										SKIPWS(); // Eat any whitespace. +										CHECK_ERROR(); + +										if (*s == '\'' || *s == '"') // '<... #="...' +										{ +											ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. +											++s; // Step over the quote. +											a->value = s; // Save the offset. + +											s = strconv_attribute(s, ch, optmsk); +										 +											if (!s) return false; + +											// After this line the loop continues from the start; +											// Whitespaces, / and > are ok, symbols are wrong, +											// everything else will be detected +											if (is_chartype(*s, ct_start_symbol)) return false; +										} +										else return false; +									} +									else return false; +								} +								else if (*s == '/') +								{ +									++s; + +									if (*s != '>') return false; +							 +									POPNODE(); // Pop. + +									++s; + +									break; +								} +								else if (*s == '>') +								{ +									++s; + +									break; +								} +								else return false; +							} +						} +						else return false; +					} +					else if (*s == '/') +					{ +						++s; + +						if (!cursor) return false; + +						char* name = cursor->name; +						if (!name) return false; +						 +						while (*s && is_chartype(*s, ct_symbol)) +						{ +							if (*s++ != *name++) return false; +						} + +						if (*name) return false; +							 +						POPNODE(); // Pop. + +						SKIPWS(); +						CHECK_ERROR(); + +						if (*s != '>') return false; +						++s; +					} +					else return false; +				} +				else +				{ +					mark = s; // Save this offset while searching for a terminator. + +					SKIPWS(); // Eat whitespace if no genuine PCDATA here. + +					if ((mark == s || !OPTSET(parse_ws_pcdata)) && (!*s || *s == '<')) +					{ +						continue; +					} + +					s = mark; +							 +					if (static_cast<xml_node_type>(cursor->type) != node_document) +					{ +						PUSHNODE(node_pcdata); // Append a new node on the tree. +						cursor->value = s; // Save the offset. + +						s = strconv_pcdata(s, optmsk); +								 +						if (!s) return false; +								 +						POPNODE(); // Pop since this is a standalone. +						 +						if (!*s) break; +					} +					else +					{ +						SCANFOR(*s == '<'); // '...<' +						if (!*s) break; +						 +						++s; +					} + +					// We're after '<' +					goto LOC_TAG; +				} +			} + +			if (cursor != xmldoc) return false; +			 +			return true; +		} +		 +	private: +		xml_parser(const xml_parser&); +		const xml_parser& operator=(const xml_parser&); +	}; + +	// 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; +	} + +	// 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; +		} +	} + +	xml_tree_walker::xml_tree_walker(): _depth(0) +	{ +	} +	 +	xml_tree_walker::~xml_tree_walker() +	{ +	} + +	int xml_tree_walker::depth() const +	{ +		return _depth; +	} + +	bool xml_tree_walker::begin(xml_node&) +	{ +		return true; +	} + +	bool xml_tree_walker::end(xml_node&) +	{ +		return true; +	} + +	xml_attribute::xml_attribute(): _attr(0) +	{ +	} + +	xml_attribute::xml_attribute(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 +   	{ +   		return empty(); +   	} + +	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 == 0); +	} + +	const char* xml_attribute::name() const +	{ +		return (!empty() && _attr->name) ? _attr->name : ""; +	} + +	const char* xml_attribute::value() const +	{ +		return (!empty() && _attr->value) ? _attr->value : ""; +	} + +	unsigned int xml_attribute::document_order() const +	{ +		return empty() ? 0 : _attr->document_order; +	} + +	xml_attribute& xml_attribute::operator=(const char* rhs) +	{ +		set_value(rhs); +		return *this; +	} +	 +	xml_attribute& xml_attribute::operator=(int rhs) +	{ +		char buf[128]; +		sprintf(buf, "%d", rhs); +		set_value(buf); +		return *this; +	} + +	xml_attribute& xml_attribute::operator=(double rhs) +	{ +		char buf[128]; +		sprintf(buf, "%g", rhs); +		set_value(buf); +		return *this; +	} +	 +	xml_attribute& xml_attribute::operator=(bool rhs) +	{ +		set_value(rhs ? "true" : "false"); +		return *this; +	} + +	bool xml_attribute::set_name(const char* rhs) +	{ +		if (empty()) return false; +		 +		bool insitu = _attr->name_insitu; +		bool res = strcpy_insitu(_attr->name, insitu, rhs); +		_attr->name_insitu = insitu; +		 +		return res; +	} +		 +	bool xml_attribute::set_value(const char* rhs) +	{ +		if (empty()) return false; + +		bool insitu = _attr->value_insitu; +		bool res = strcpy_insitu(_attr->value, insitu, rhs); +		_attr->value_insitu = insitu; +		 +		return res; +	} + +#ifdef __BORLANDC__ +	bool operator&&(const xml_attribute& lhs, bool rhs) +	{ +		return lhs ? rhs : false; +	} + +	bool operator||(const xml_attribute& lhs, bool rhs) +	{ +		return lhs ? true : rhs; +	} +#endif + +	xml_node::xml_node(): _root(0) +	{ +	} + +	xml_node::xml_node(xml_node_struct* p): _root(p) +	{ +	} +	 +	xml_node::operator xml_node::unspecified_bool_type() const +	{ +      	return empty() ? 0 : &xml_node::_root; +   	} + +   	bool xml_node::operator!() const +   	{ +   		return empty(); +   	} + +	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::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); +	} + +	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); +	} +	 +	xml_allocator& xml_node::get_allocator() const +	{ +		xml_node_struct* r = root()._root; + +		return static_cast<xml_document_struct*>(r)->allocator; +	} + +	const char* xml_node::name() const +	{ +		return (!empty() && _root->name) ? _root->name : ""; +	} + +	xml_node_type xml_node::type() const +	{ +		return _root ? static_cast<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::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 empty() ? xml_node() : xml_node(_root->parent); +	} + +	xml_node xml_node::root() const +	{ +		xml_node r = *this; +		while (r && r.parent()) r = r.parent(); +		return r; +	} + +	const char* xml_node::child_value() const +	{ +		if (!empty()) +			for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) +				if ((static_cast<xml_node_type>(i->type) == node_pcdata || static_cast<xml_node_type>(i->type) == node_cdata) && i->value) +					return i->value; +		return ""; +	} + +	const char* xml_node::child_value(const char* name) const +	{ +		return child(name).child_value(); +	} + +	const char* xml_node::child_value_w(const char* name) const +	{ +		return child_w(name).child_value(); +	} + +	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(); +	} + +	bool xml_node::set_name(const char* rhs) +	{ +		switch (type()) +		{ +		case node_pi: +		case node_element: +		{ +			bool insitu = _root->name_insitu; +			bool res = strcpy_insitu(_root->name, insitu, rhs); +			_root->name_insitu = insitu; +		 +			return res; +		} + +		default: +			return false; +		} +	} +		 +	bool xml_node::set_value(const char* rhs) +	{ +		switch (type()) +		{ +		case node_pi: +		case node_cdata: +		case node_pcdata: +		case node_comment: +		{ +			bool insitu = _root->value_insitu; +			bool res = strcpy_insitu(_root->value, insitu, rhs); +			_root->value_insitu = insitu; +		 +			return res; +		} + +		default: +			return false; +		} +	} + +	xml_attribute xml_node::append_attribute(const char* name) +	{ +		if (type() != node_element) return xml_attribute(); +		 +		xml_attribute a(_root->append_attribute(get_allocator())); +		a.set_name(name); +		 +		return a; +	} + +	xml_attribute xml_node::insert_attribute_before(const char* name, const xml_attribute& attr) +	{ +		if (type() != node_element || attr.empty()) return xml_attribute(); +		 +		// check that attribute belongs to *this +		xml_attribute_struct* cur = attr._attr; + +		while (cur->prev_attribute) cur = cur->prev_attribute; + +		if (cur != _root->first_attribute) return xml_attribute(); + +		xml_attribute a(get_allocator().allocate<xml_attribute_struct>()); +		a.set_name(name); + +		if (attr._attr->prev_attribute) +			attr._attr->prev_attribute->next_attribute = a._attr; +		else +			_root->first_attribute = a._attr; +		 +		a._attr->prev_attribute = attr._attr->prev_attribute; +		a._attr->next_attribute = attr._attr; +		attr._attr->prev_attribute = a._attr; +				 +		return a; +	} + +	xml_attribute xml_node::insert_attribute_after(const char* name, const xml_attribute& attr) +	{ +		if (type() != node_element || attr.empty()) return xml_attribute(); +		 +		// check that attribute belongs to *this +		xml_attribute_struct* cur = attr._attr; + +		while (cur->prev_attribute) cur = cur->prev_attribute; + +		if (cur != _root->first_attribute) return xml_attribute(); + +		xml_attribute a(get_allocator().allocate<xml_attribute_struct>()); +		a.set_name(name); + +		if (attr._attr->next_attribute) +			attr._attr->next_attribute->prev_attribute = a._attr; +		else +			_root->last_attribute = a._attr; +		 +		a._attr->next_attribute = attr._attr->next_attribute; +		a._attr->prev_attribute = attr._attr; +		attr._attr->next_attribute = a._attr; + +		return a; +	} + +	xml_node xml_node::append_child(xml_node_type type) +	{ +		if ((this->type() != node_element && this->type() != node_document) || type == node_document || type == node_null) return xml_node(); +		 +		return xml_node(_root->append_node(get_allocator(), type)); +	} + +	xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node) +	{ +		if ((this->type() != node_element && this->type() != node_document) || type == node_document || type == node_null) return xml_node(); +		if (node.parent() != *this) return xml_node(); +	 +		xml_node n(get_allocator().allocate<xml_node_struct>(type)); +		n._root->parent = _root; +		 +		if (node._root->prev_sibling) +			node._root->prev_sibling->next_sibling = n._root; +		else +			_root->first_child = n._root; +		 +		n._root->prev_sibling = node._root->prev_sibling; +		n._root->next_sibling = node._root; +		node._root->prev_sibling = n._root; + +		return n; +	} + +	xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node) +	{ +		if ((this->type() != node_element && this->type() != node_document) || type == node_document || type == node_null) return xml_node(); +		if (node.parent() != *this) return xml_node(); +	 +		xml_node n(get_allocator().allocate<xml_node_struct>(type)); +		n._root->parent = _root; +	 +		if (node._root->next_sibling) +			node._root->next_sibling->prev_sibling = n._root; +		else +			_root->last_child = n._root; +		 +		n._root->next_sibling = node._root->next_sibling; +		n._root->prev_sibling = node._root; +		node._root->next_sibling = n._root; + +		return n; +	} + +	void xml_node::remove_attribute(const char* name) +	{ +		remove_attribute(attribute(name)); +	} + +	void xml_node::remove_attribute(const xml_attribute& a) +	{ +		if (empty()) return; + +		// check that attribute belongs to *this +		xml_attribute_struct* attr = a._attr; + +		while (attr->prev_attribute) attr = attr->prev_attribute; + +		if (attr != _root->first_attribute) return; + +		if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute = a._attr->prev_attribute; +		else _root->last_attribute = a._attr->prev_attribute; +		 +		if (a._attr->prev_attribute) a._attr->prev_attribute->next_attribute = a._attr->next_attribute; +		else _root->first_attribute = a._attr->next_attribute; + +		a._attr->free(); +	} + +	void xml_node::remove_child(const char* name) +	{ +		remove_child(child(name)); +	} + +	void xml_node::remove_child(const xml_node& n) +	{ +		if (empty() || n.parent() != *this) return; + +		if (n._root->next_sibling) n._root->next_sibling->prev_sibling = n._root->prev_sibling; +		else _root->last_child = n._root->prev_sibling; +		 +		if (n._root->prev_sibling) n._root->prev_sibling->next_sibling = n._root->next_sibling; +		else _root->first_child = n._root->next_sibling; +         +        n._root->free(); +	} + +#ifndef PUGIXML_NO_STL +	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 = cursor.parent(); +			 +			std::string temp = cursor.name(); +			temp += delimiter; +			temp += path; +			path.swap(temp); +		} + +		return path; +	} +#endif + +	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) +	{ +		walker._depth = 0; +		 +		if (!walker.begin(*this)) return false; + +		xml_node cur = first_child(); +				 +		if (cur) +		{ +			do  +			{ +				if (!walker.for_each(cur)) +					return false; +						 +				if (cur.first_child()) +				{ +					++walker._depth; +					cur = cur.first_child(); +				} +				else if (cur.next_sibling()) +					cur = cur.next_sibling(); +				else +				{ +					// Borland C++ workaround +					while (!cur.next_sibling() && cur != *this && (bool)cur.parent()) +					{ +						--walker._depth; +						cur = cur.parent(); +					} +						 +					if (cur != *this) +						cur = cur.next_sibling(); +				} +			} +			while (cur && cur != *this); +		} + +		if (!walker.end(*this)) return false; +		 +		return true; +	} + +	unsigned int xml_node::document_order() const +	{ +		return empty() ? 0 : _root->document_order; +	} + +	void xml_node::precompute_document_order_impl() +	{ +		if (type() != node_document) return; + +		unsigned int current = 1; +		xml_node cur = *this; + +		for (;;) +		{ +			cur._root->document_order = current++; +			 +			for (xml_attribute a = cur.first_attribute(); a; a = a.next_attribute()) +				a._attr->document_order = current++; +					 +			if (cur.first_child()) +				cur = cur.first_child(); +			else if (cur.next_sibling()) +				cur = cur.next_sibling(); +			else +			{ +				while (cur && !cur.next_sibling()) cur = cur.parent(); +				cur = cur.next_sibling(); +				 +				if (!cur) break; +			} +		} +	} + +#ifndef PUGIXML_NO_STL +	void xml_node::print(std::ostream& os, const char* indent, unsigned int flags, unsigned int depth) +	{ +		if (empty()) return; + +		if ((flags & format_indent) != 0 && (flags & format_raw) == 0) +			for (unsigned int i = 0; i < depth; ++i) os << indent; + +		switch (type()) +		{ +		case node_document: +			for (xml_node n = first_child(); n; n = n.next_sibling()) +				n.print(os, indent, flags, depth); +			break; +			 +		case node_element: +		{ +			os << '<' << name(); + +			for (xml_attribute a = first_attribute(); a; a = a.next_attribute()) +			{ +				os << ' ' << a.name() << "=\""; + +				if (flags & format_utf8) +					text_output_escaped<true, true>(os, a.value()); +				else +					text_output_escaped<true, false>(os, a.value()); + +				os << "\""; +			} + +			if (flags & format_raw) +			{ +				if (!_root->first_child) // 0 children +					os << " />"; +				else +				{ +					os << ">"; +					for (xml_node n = first_child(); n; n = n.next_sibling()) +						n.print(os, indent, flags, depth + 1); +					os << "</" << name() << ">"; +				} +			} +			else if (!_root->first_child) // 0 children +				os << " />\n"; +			else if (_root->first_child == _root->last_child && first_child().type() == node_pcdata) +			{ +				os << ">"; +				 +				if (flags & format_utf8) +					text_output_escaped<false, true>(os, first_child().value()); +				else +					text_output_escaped<false, false>(os, first_child().value()); +					 +				os << "</" << name() << ">\n"; +			} +			else +			{ +				os << ">\n"; +				 +				for (xml_node n = first_child(); n; n = n.next_sibling()) +					n.print(os, indent, flags, depth + 1); + +				if ((flags & format_indent) != 0 && (flags & format_raw) == 0) +					for (unsigned int i = 0; i < depth; ++i) os << indent; +				 +				os << "</" << name() << ">\n"; +			} + +			break; +		} +		case node_pcdata: +			if (flags & format_utf8) +				text_output_escaped<false, true>(os, value()); +			else +				text_output_escaped<false, false>(os, value()); +			break; + +		case node_cdata: +			os << "<![CDATA[" << value() << "]]>"; +			if ((flags & format_raw) == 0) os << "\n"; +			break; + +		case node_comment: +			os << "<!--" << value() << "-->"; +			if ((flags & format_raw) == 0) os << "\n"; +			break; + +		case node_pi: +			os << "<?" << name(); +			if (value()[0]) os << ' ' << value(); +			os << "?>"; +			if ((flags & format_raw) == 0) os << "\n"; +			break; +		 +		default: +			; +		} +	} +#endif + +#ifdef __BORLANDC__ +	bool operator&&(const xml_node& lhs, bool rhs) +	{ +		return lhs ? rhs : false; +	} + +	bool operator||(const xml_node& lhs, bool rhs) +	{ +		return lhs ? true : rhs; +	} +#endif + +	xml_node_iterator::xml_node_iterator() +	{ +	} + +	xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node) +	{ +	} + +	xml_node_iterator::xml_node_iterator(xml_node_struct* ref): _wrap(ref) +	{ +	} +		 +	xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* prev): _prev(prev), _wrap(ref) +	{ +	} + +	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); +	} + +	xml_node& xml_node_iterator::operator*() +	{ +		return _wrap; +	} + +	xml_node* xml_node_iterator::operator->() +	{ +		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(xml_attribute_struct* ref): _wrap(ref) +	{ +	} +		 +	xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_attribute_struct* prev): _prev(prev), _wrap(ref) +	{ +	} + +	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); +	} + +	xml_attribute& xml_attribute_iterator::operator*() +	{ +		return _wrap; +	} + +	xml_attribute* xml_attribute_iterator::operator->() +	{ +		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_document::xml_document(): _buffer(0) +	{ +		create(); +	} + +	xml_document::~xml_document() +	{ +		free(); +	} + +	void xml_document::create() +	{ +		xml_allocator alloc(&_memory); +		 +		_root = alloc.allocate<xml_document_struct>(); // Allocate a new root. +		xml_allocator& a = static_cast<xml_document_struct*>(_root)->allocator; +		a = alloc; +	} + +	void xml_document::free() +	{ +		delete[] _buffer; +		_buffer = 0; + +		if (_root) _root->free(); + +		xml_memory_block* current = _memory.next; + +		while (current) +		{ +			xml_memory_block* next = current->next; +			delete current; +			current = next; +		} +		 +		_memory.next = 0; +		_memory.size = 0; + +		create(); +	} + +#ifndef PUGIXML_NO_STL +	bool xml_document::load(std::istream& stream, unsigned int options) +	{ +		if (!stream.good()) return false; + +		std::streamoff length, pos = stream.tellg(); +		stream.seekg(0, std::ios_base::end); +		length = stream.tellg(); +		stream.seekg(pos, std::ios_base::beg); + +		if (!stream.good()) return false; + +		char* s; + +		try +		{ +			s = new char[length + 1]; +		} +		catch (const std::bad_alloc&) +		{ +			return false; +		} + +		stream.read(s, length); +		s[length] = 0; + +		if (!stream.good()) +		{ +			delete[] s; +			return false; +		} + +		return parse(transfer_ownership_tag(), s, options); // Parse the input string. +	} +#endif + +	bool xml_document::load(const char* contents, unsigned int options) +	{ +		char* s; + +		try +		{ +			s = new char[strlen(contents) + 1]; +		} +		catch (const std::bad_alloc&) +		{ +			return false; +		} + +		strcpy(s, contents); + +		return parse(transfer_ownership_tag(), s, options); // Parse the input string. +	} + +	bool xml_document::load_file(const char* name, unsigned int options) +	{ +		FILE* file = fopen(name, "rb"); +		if (!file) return false; + +		fseek(file, 0, SEEK_END); +		long length = ftell(file); +		fseek(file, 0, SEEK_SET); + +		if (length < 0) +		{ +			fclose(file); +			return false; +		} +		 +		char* s; + +		try +		{ +			s = new char[length + 1]; +		} +		catch (const std::bad_alloc&) +		{ +			fclose(file); +			return false; +		} + +		size_t read = fread(s, (size_t)length, 1, file); +		fclose(file); + +		if (read != 1) +		{ +			delete[] s; +			return false; +		} + +		s[length] = 0; +		 +		return parse(transfer_ownership_tag(), s, options); // Parse the input string. +	} + +	bool xml_document::parse(char* xmlstr, unsigned int options) +	{ +		free(); + +		xml_allocator& alloc = static_cast<xml_document_struct*>(_root)->allocator; +		 +		xml_parser parser(alloc); +		 +		return parser.parse(xmlstr, _root, options); // Parse the input string. +	} +		 +	bool xml_document::parse(const transfer_ownership_tag&, char* xmlstr, unsigned int options) +	{ +		bool res = parse(xmlstr, options); + +		if (res) _buffer = xmlstr; + +		return res; +	} + +#ifndef PUGIXML_NO_STL +	bool xml_document::save_file(const char* name, const char* indent, unsigned int flags) +	{ +		std::ofstream out(name, std::ios::out); +		if (!out) return false; + +		if (flags & format_write_bom) +		{ +			if (flags & format_utf8) +			{ +				static const unsigned char utf8_bom[] = {0xEF, 0xBB, 0xBF}; +				out.write(reinterpret_cast<const char*>(utf8_bom), 3); +			} +		} + +		out << "<?xml version=\"1.0\"?>"; +		if (!(flags & format_raw)) out << "\n"; +		print(out, indent, flags); +		 +		return true; +	} +#endif + +	void xml_document::precompute_document_order() +	{ +		precompute_document_order_impl(); +	} + +#ifndef PUGIXML_NO_STL +	std::string as_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 as_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; +	} +#endif +} diff --git a/src/pugixml.hpp b/src/pugixml.hpp index de7939e..323994d 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -1,664 +1,1735 @@ -///////////////////////////////////////////////////////////////////////////////
 -//
 -// Pug Improved XML Parser - Version 0.2
 -// --------------------------------------------------------
 -// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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
 -
 -// Uncomment this to disable STL
 -// #define PUGIXML_NO_STL
 -
 -#ifndef PUGIXML_NO_STL
 -#	include <string>
 -#	include <istream>
 -#endif
 -
 -#include <cstddef>
 -#include <cstring>
 -
 -/// 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; ///< Do not skip PCDATA that consists only of whitespaces
 -	const unsigned int parse_ext_pcdata			= 0x00000010; ///< Do not skip PCDATA that is outside all tags (i.e. root)
 -	const unsigned int parse_escapes			= 0x00000020; ///< Parse <, >, &, ", ', &#.. sequences
 -	const unsigned int parse_wnorm_attribute	= 0x00000080; ///< Normalize spaces in attributes (convert space-like characters to spaces + merge adjacent spaces + trim leading/trailing spaces)
 -	const unsigned int parse_wconv_attribute	= 0x00000100; ///< Convert space-like characters to spaces in attributes (only if wnorm is not set)
 -	const unsigned int parse_eol				= 0x00000200; ///< Perform EOL handling
 -	const unsigned int parse_check_end_tags		= 0x00000400; ///< Check start and end tag names and return error if names mismatch
 -	const unsigned int parse_match_end_tags		= 0x00000800; ///< Try to find corresponding start tag for an end tag
 -	///< Set all flags, except parse_ws_pcdata, parse_trim_attribute, parse_pi and parse_comments
 -	const unsigned int parse_default			= parse_cdata | parse_ext_pcdata | parse_escapes | parse_wconv_attribute | parse_eol | parse_check_end_tags;
 -	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 | parse_wconv_attribute |
 -												parse_check_end_tags | parse_ws_pcdata | parse_eol;
 -
 -	/// 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;
 -
 -		/// Return PCDATA/CDATA that is child of specified child node. If none, return empty string.
 -		const char* child_value(const char* name) const;
 -
 -		/// Return PCDATA/CDATA that is child of specified child node. If none, return empty string.
 -		/// Enable wildcard matching.
 -		const char* child_value_w(const char* name) 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;
 -
 -#ifndef PUGIXML_NO_STL
 -		/// 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;
 -#endif
 -
 -		/// 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
 -#ifndef PUGIXML_NO_STL
 -	: public std::iterator<std::bidirectional_iterator_tag, const xml_node>
 -#endif
 -	{
 -		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
 -#ifndef PUGIXML_NO_STL
 -	: public std::iterator<std::bidirectional_iterator_tag, const xml_attribute>
 -#endif
 -	{
 -		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];
 -	};
 -
 -	struct transfer_ownership_tag {};
 -
 -	/// Provides a high-level interface to the XML parser.
 -	class xml_parser
 -	{
 -	private:
 -		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&);
 -
 -		void free();	///< free memory
 -
 -	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 that gains ownership.
 -		/// \param xmlstr - readwrite string with xml data
 -		/// \param optmsk - Options mask.
 -		/// \see parse
 -		xml_parser(const transfer_ownership_tag&, char* xmlstr, unsigned int optmsk = parse_default);
 -
 -#ifndef PUGIXML_NO_STL
 -		/// Parse constructor.
 -		/// \param stream - stream with xml data
 -		/// \param optmsk - Options mask.
 -		/// \see parse
 -		xml_parser(std::istream& stream, unsigned int optmsk = parse_default);
 -#endif
 -
 -		/// 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:
 -#ifndef PUGIXML_NO_STL
 -		/// 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);
 -#endif
 -
 -		/// 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);
 -		
 -		/// Parse the given XML string in-situ (gains ownership).
 -		/// \param xmlstr - readwrite string with xml data
 -		/// \param optmsk - Options mask.
 -		/// \return last position or NULL
 -		/// \rem input string is zero-segmented
 -		char* parse(const transfer_ownership_tag&, char* xmlstr, unsigned int optmsk = parse_noset);
 -	};
 -
 -	/// Utility functions for xml
 -	
 -#ifndef PUGIXML_NO_STL
 -	/// Convert utf16 to utf8
 -	std::string utf8(const wchar_t* str);
 -	
 -	/// Convert utf8 to utf16
 -	std::wstring utf16(const char* str);
 -#endif
 -}
 -
 -/// 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
 +/////////////////////////////////////////////////////////////////////////////// +// +// Pug Improved XML Parser - Version 0.3 +// -------------------------------------------------------- +// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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 + +#include "pugiconfig.hpp" + +#ifndef PUGIXML_NO_STL +#	include <string> +#	include <istream> +#	include <exception> +#endif + +// No XPath without STL +#ifdef PUGIXML_NO_STL +#	ifndef PUGIXML_NO_XPATH +#		define PUGIXML_NO_XPATH +#	endif +#endif + +#include <cstddef> +#include <cstring> + +/// The PugiXML Parser namespace. +namespace pugi +{ +	/// Tree node classification. +	enum xml_node_type +	{ +		node_null,			///< 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. '<?...?>' +	}; + +	// Parsing options + +	/** +	 * Memory block size, used for fast allocator. Memory for DOM tree is allocated in blocks of +	 * memory_block_size + 4. +	 * This value affects size of xml_memory class. +	 */ +	const size_t memory_block_size = 32768; + +	/** +	 * Minimal parsing mode. Equivalent to turning all other flags off. This set of flags means +	 * that pugixml does not add pi/cdata sections or comments to DOM tree and does not perform +	 * any conversions for input data, meaning fastest parsing. +	 */ +	const unsigned int parse_minimal			= 0x0000; + +	/** +	 * This flag determines if processing instructions (nodes with type node_pi; such nodes have the +	 * form of <? target content ?> or <? target ?> in XML) are to be put in DOM tree. If this flag is off, +	 * they are not put in the tree, but are still parsed and checked for correctness. +	 * +	 * The corresponding node in DOM tree will have type node_pi, name "target" and value "content", +	 * if any. +	 * +	 * Note that <?xml ...?> (document declaration) is not considered to be a PI. +	 * +	 * This flag is off by default. +	 */ +	const unsigned int parse_pi					= 0x0001; + +	/** +	 * This flag determines if comments (nodes with type node_comment; such nodes have the form of +	 * <!-- content --> in XML) are to be put in DOM tree. If this flag is off, they are not put in +	 * the tree, but are still parsed and checked for correctness. +	 * +	 * The corresponding node in DOM tree will have type node_comment, empty name and value "content". +	 * +	 * This flag is off by default. +	 */ +	const unsigned int parse_comments			= 0x0002; + +	/** +	 * This flag determines if CDATA sections (nodes with type node_cdata; such nodes have the form +	 * of <![CDATA[[content]]> in XML) are to be put in DOM tree. If this flag is off, they are not +	 * put in the tree, but are still parsed and checked for correctness. +	 * +	 * The corresponding node in DOM tree will have type node_cdata, empty name and value "content". +	 * +	 * This flag is on by default. +	 */ +	const unsigned int parse_cdata				= 0x0004; + +	/** +	 * This flag determines if nodes with PCDATA (regular text) that consist only of whitespace +	 * characters are to be put in DOM tree. Often whitespace-only data is not significant for the +	 * application, and the cost of allocating and storing such nodes (both memory and speed-wise) +	 * can be significant. For example, after parsing XML string "<node> <a/> </node>", <node> element +	 * will have 3 children when parse_ws_pcdata is set (child with type node_pcdata and value=" ", +	 * child with type node_element and name "a", and another child with type node_pcdata and +	 * value=" "), and only 1 child when parse_ws_pcdata is not set. +	 *  +	 * This flag is off by default. +	 */ +	const unsigned int parse_ws_pcdata			= 0x0008; + +	/** +	 * This flag determines if character and entity references are to be expanded during the parsing +	 * process. Character references are &#...; or &#x...; (... is Unicode numeric representation of +     * character in either decimal (&#...;) or hexadecimal (&#x...;) form), entity references are &...; +     * Note that as pugixml does not handle DTD, the only allowed entities are predefined ones -  +     * &lt;, &gt;, &amp;, &apos; and &quot;. If character/entity reference can not be expanded, it is +     * leaved as is, so you can do additional processing later. +     * Reference expansion is performed in attribute values and PCDATA content. +     * +     * This flag is on by default. +     */ +	const unsigned int parse_escapes			= 0x0010; + +	/** +	 * This flag determines if EOL handling (that is, replacing sequences 0x0d 0x0a by a single 0x0a +	 * character, and replacing all standalone 0x0d characters by 0x0a) is to be performed on input +	 * data (that is, comments contents, PCDATA/CDATA contents and attribute values). +	 * +	 * This flag is on by default. +	 */ +	const unsigned int parse_eol				= 0x0020; +	 + 	/** + 	 * This flag determines if attribute value normalization should be performed for all attributes, + 	 * assuming that their type is not CDATA. This means, that: + 	 * 1. Whitespace characters (new line, tab and space) are replaced with space (' ') + 	 * 2. Afterwards sequences of spaces are replaced with a single space + 	 * 3. Leading/trailing whitespace characters are trimmed + 	 *  + 	 * This flag is off by default. + 	 */ + 	const unsigned int parse_wnorm_attribute	= 0x0040; + + 	/** + 	 * This flag determines if attribute value normalization should be performed for all attributes, + 	 * assuming that their type is CDATA. This means, that whitespace characters (new line, tab and + 	 * space) are replaced with space (' '). Note, that the actions performed while this flag is on + 	 * are also performed if parse_wnorm_attribute is on, so this flag has no effect if + 	 * parse_wnorm_attribute flag is set. + 	 *  + 	 * This flag is on by default. + 	 */ + 	const unsigned int parse_wconv_attribute	= 0x0080; +	 +	/** +     * This is the default set of flags. It includes parsing CDATA sections (comments/PIs are not +     * parsed), performing character and entity reference expansion, replacing whitespace characters +     * with spaces in attribute values and performing EOL handling. Note, that PCDATA sections +     * consisting only of whitespace characters are not parsed (by default) for performance reasons. +     */ +	const unsigned int parse_default			= parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + +	// Formatting flags +	 +	/** +	 * Indent the nodes that are written to output stream with as many indentation strings as deep +	 * the node is in DOM tree. +	 * +	 * This flag is on by default. +	 */ +	const unsigned int format_indent	= 0x01; +	 +	/** +	 * This flag determines how the non-printable symbols are written to output stream - they are +	 * either considered UTF-8 and are written as UTF-8 character, escaped with &#...;, or they are +	 * considered to be ASCII and each ASCII character is escaped separately. +	 * +	 * This flag is on by default. +	 */ +	const unsigned int format_utf8		= 0x02; +	 +	/** +	 * This flag determines if UTF-8 BOM is to be written to output stream. +	 * +	 * This flag is off by default. +	 */ +	const unsigned int format_write_bom	= 0x04; +	 +	/** +	 * If this flag is on, no indentation is performed and no line breaks are written to output file. +	 * This means that the data is written to output stream as is. +	 * +	 * This flag is off by default. +	 */ +	const unsigned int format_raw		= 0x08; +	 +	/** +	 * This is the default set of formatting flags. It includes indenting nodes depending on their +	 * depth in DOM tree and considering input data to be UTF-8. +	 */ +	const unsigned int format_default	= format_indent | format_utf8; +		 +	// Forward declarations +	struct xml_attribute_struct; +	struct xml_node_struct; + +	class xml_allocator; + +	class xml_node_iterator; +	class xml_attribute_iterator; + +	class xml_tree_walker; +	 +	class xml_node; + +	#ifndef PUGIXML_NO_XPATH +	class xpath_node; +	class xpath_node_set; +	class xpath_ast_node; +	class xpath_allocator; +	 +	/** +	 * A class that holds compiled XPath query and allows to evaluate query result +	 */ +	class xpath_query +	{ +	private: +		// Noncopyable semantics +		xpath_query(const xpath_query&); +		xpath_query& operator=(const xpath_query&); + +		xpath_allocator* m_alloc; +		xpath_ast_node* m_root; + +		void compile(const char* query); + +	public: +		/** +		 * Ctor from string with XPath expression. +		 * Throws xpath_exception on compilation error, std::bad_alloc on out of memory error. +		 * +		 * \param query - string with XPath expression +		 */ +		explicit xpath_query(const char* query); + +		/** +		 * Dtor +		 */ +		~xpath_query(); +		 +		/** +		 * Evaluate expression as boolean value for the context node \a n. +		 * If expression does not directly evaluate to boolean, the expression result is converted +		 * as through boolean() XPath function call. +		 * Throws std::bad_alloc on out of memory error. +		 * +		 * \param n - context node +		 * \return evaluation result +		 */ +		bool evaluate_boolean(const xml_node& n); +		 +		/** +		 * Evaluate expression as double value for the context node \a n. +		 * If expression does not directly evaluate to double, the expression result is converted +		 * as through number() XPath function call. +		 * Throws std::bad_alloc on out of memory error. +		 * +		 * \param n - context node +		 * \return evaluation result +		 */ +		double evaluate_number(const xml_node& n); +		 +		/** +		 * Evaluate expression as string value for the context node \a n. +		 * If expression does not directly evaluate to string, the expression result is converted +		 * as through string() XPath function call. +		 * Throws std::bad_alloc on out of memory error. +		 * +		 * \param n - context node +		 * \return evaluation result +		 */ +		std::string evaluate_string(const xml_node& n); +		 +		/** +		 * Evaluate expression as node set for the context node \a n. +		 * If expression does not directly evaluate to node set, function returns empty node set. +		 * Throws std::bad_alloc on out of memory error. +		 * +		 * \param n - context node +		 * \return evaluation result +		 */ +		xpath_node_set evaluate_node_set(const xml_node& n); +	}; +	#endif +	 +	/** +	 * A light-weight wrapper for manipulating attributes in DOM tree. +	 * Note: xml_attribute does not allocate any memory for the attribute it wraps; it only wraps a +	 * pointer to existing attribute. +	 */ +	class xml_attribute +	{ +		friend class xml_attribute_iterator; +		friend class xml_node; + +	private: +		xml_attribute_struct* _attr; +	 +    	/// \internal Safe bool type +    	typedef xml_attribute_struct* xml_attribute::*unspecified_bool_type; + +		/// \internal Initializing ctor +		explicit xml_attribute(xml_attribute_struct* attr); + +	public: +		/** +		 * Default ctor. Constructs an empty attribute. +		 */ +		xml_attribute(); +		 +	public: +    	/** +    	 * Safe bool conversion. +    	 * Allows xml_node to be used in a context where boolean variable is expected, such as 'if (node)'. +    	 */ +    	operator unspecified_bool_type() const; + +    	// Borland C++ workaround +    	bool operator!() const; + +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator==(const xml_attribute& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator!=(const xml_attribute& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator<(const xml_attribute& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator>(const xml_attribute& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator<=(const xml_attribute& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator>=(const xml_attribute& r) const; + +	public: +    	/** +    	 * Get next attribute in attribute list of node that contains the attribute. +    	 * +    	 * \return next attribute, if any; empty attribute otherwise +    	 */ +    	xml_attribute next_attribute() const; + +    	/** +    	 * Get previous attribute in attribute list of node that contains the attribute. +    	 * +    	 * \return previous attribute, if any; empty attribute otherwise +    	 */ +    	xml_attribute previous_attribute() const; + +		/** +		 * Cast attribute value as int. +		 * +		 * \return attribute value as int, or 0 if conversion did not succeed or attribute is empty +		 */ +		int as_int() const; + +		/** +		 * Cast attribute value as double. +		 * +		 * \return attribute value as double, or 0.0 if conversion did not succeed or attribute is empty +		 */ +		double as_double() const; +	 +		/** +		 * Cast attribute value as float. +		 * +		 * \return attribute value as float, or 0.0f if conversion did not succeed or attribute is empty +		 */ +		float as_float() const; + +		/** +		 * Cast attribute value as bool. Returns true for attributes with values that start with '1', +		 * 't', 'T', 'y', 'Y', returns false for other attributes. +		 * +		 * \return attribute value as bool, or false if conversion did not succeed or attribute is empty +		 */ +		bool as_bool() const; + +		/// \internal Document order or 0 if not set +		unsigned int document_order() const; + +	public: +		/** +         * Set attribute value to \a rhs. +         * +         * \param rhs - new attribute value +         * \return self +         */ +		xml_attribute& operator=(const char* rhs); +	 +		/** +         * Set attribute value to \a rhs. +         * +         * \param rhs - new attribute value +         * \return self +         */ +		xml_attribute& operator=(int rhs); +	 +		/** +         * Set attribute value to \a rhs. +         * +         * \param rhs - new attribute value +         * \return self +         */ +		xml_attribute& operator=(double rhs); +		 +		/** +         * Set attribute value to either 'true' or 'false' (depends on whether \a rhs is true or false). +         * +         * \param rhs - new attribute value +         * \return self +         */ +		xml_attribute& operator=(bool rhs); + +		/** +		 * Set attribute name to \a rhs. +		 * +		 * \param rhs - new attribute name +		 * \return success flag (call fails if attribute is empty or there is not enough memory) +		 */ +		bool set_name(const char* rhs); +		 +		/** +		 * Set attribute value to \a rhs. +		 * +		 * \param rhs - new attribute value +		 * \return success flag (call fails if attribute is empty or there is not enough memory) +		 */ +		bool set_value(const char* rhs); + +	public: +		/** +		 * Check if attribute is empty. +		 * +		 * \return true if attribute is empty, false otherwise +		 */ +		bool empty() const; + +	public: +		/** +		 * Get attribute name. +		 * +		 * \return attribute name, or "" if attribute is empty +		 */ +		const char* name() const; + +		/** +		 * Get attribute value. +		 * +		 * \return attribute value, or "" if attribute is empty +		 */ +		const char* value() const; +	}; + +#ifdef __BORLANDC__ +	// Borland C++ workaround +	bool operator&&(const xml_attribute& lhs, bool rhs); +	bool operator||(const xml_attribute& lhs, bool rhs); +#endif + +	/** +	 * A light-weight wrapper for manipulating nodes in DOM tree. +	 * Note: xml_node does not allocate any memory for the node it wraps; it only wraps a pointer to +	 * existing node. +	 */ +	class xml_node +	{ +		friend class xml_node_iterator; + +	protected: +		xml_node_struct* _root; + +    	/// \internal Safe bool type +    	typedef xml_node_struct* xml_node::*unspecified_bool_type; + +		/// \internal Initializing ctor +		explicit xml_node(xml_node_struct* p); + +		/// \internal Precompute document order (valid only for document node) +		void precompute_document_order_impl(); + +		/// \internal Get allocator +		xml_allocator& get_allocator() const; + +	public: +		/** +		 * Default ctor. Constructs an empty node. +		 */ +		xml_node(); + +	public: +    	/** +    	 * Safe bool conversion. +    	 * Allows xml_node to be used in a context where boolean variable is expected, such as 'if (node)'. +    	 */ +		operator unspecified_bool_type() const; + +		// Borland C++ workaround +		bool operator!() const; +	 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator==(const xml_node& r) const; + +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator!=(const xml_node& r) const; + +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator<(const xml_node& r) const; + +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator>(const xml_node& r) const; + +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator<=(const xml_node& r) const; +		 +		/** +		 * Compare wrapped pointer to the attribute to the pointer that is wrapped by \a r. +		 * +		 * \param r - value to compare to +		 * \return comparison result +		 */ +		bool operator>=(const xml_node& r) const; + +	public: +		/** +		 * Node iterator type (for child nodes). +		 * \see xml_node_iterator +		 */ +		typedef xml_node_iterator iterator; + +		/** +		 * Node iterator type (for child nodes). +		 * \see xml_attribute_iterator +		 */ +		typedef xml_attribute_iterator attribute_iterator; + +		/** +		 * Access the begin iterator for this node's collection of child nodes. +		 * +		 * \return iterator that points to the first child node, or past-the-end iterator if node is empty or has no children +		 */ +		iterator begin() const; +	 +		/** +		 * Access the end iterator for this node's collection of child nodes. +		 * +		 * \return past-the-end iterator for child list +		 */ +		iterator end() const; +	 +		/** +		 * Access the begin iterator for this node's collection of attributes. +		 * +		 * \return iterator that points to the first attribute, or past-the-end iterator if node is empty or has no attributes +		 */ +		attribute_iterator attributes_begin() const; +	 +		/** +		 * Access the end iterator for this node's collection of attributes. +		 * +		 * \return past-the-end iterator for attribute list +		 */ +		attribute_iterator attributes_end() const; + +	public: +		/** +		 * Check if node is empty. +		 * +		 * \return true if node is empty, false otherwise +		 */ +		bool empty() const; + +	public: +		/** +		 * Get node type +		 * +		 * \return node type; node_null for empty nodes +		 */ +		xml_node_type type() const; + +		/** +		 * Get node name (element name for element nodes, PI target for PI) +		 * +		 * \return node name, if any; "" otherwise +		 */ +		const char* name() const; + +		/** +		 * Get node value (comment/PI/PCDATA/CDATA contents, depending on node type) +		 * +		 * \return node value, if any; "" otherwise +		 */ +		const char* value() const; +	 +		/** +		 * Get child with the specified name +		 * +		 * \param name - child name +		 * \return child with the specified name, if any; empty node otherwise +		 */ +		xml_node child(const char* name) const; + +		/** +		 * Get child with the name that matches specified pattern +		 * +		 * \param name - child name pattern +		 * \return child with the name that matches pattern, if any; empty node otherwise +		 */ +		xml_node child_w(const char* name) const; + +		/** +		 * Get attribute with the specified name +		 * +		 * \param name - attribute name +		 * \return attribute with the specified name, if any; empty attribute otherwise +		 */ +		xml_attribute attribute(const char* name) const; + +		/** +		 * Get attribute with the name that matches specified pattern +		 * +		 * \param name - attribute name pattern +		 * \return attribute with the name that matches pattern, if any; empty attribute otherwise +		 */ +		xml_attribute attribute_w(const char* name) const; + +		/** +		 * Get first of following sibling nodes with the specified name +		 * +		 * \param name - sibling name +		 * \return node with the specified name, if any; empty node otherwise +		 */ +		xml_node next_sibling(const char* name) const; + +		/** +		 * Get first of the following sibling nodes with the name that matches specified pattern +		 * +		 * \param name - sibling name pattern +		 * \return node with the name that matches pattern, if any; empty node otherwise +		 */ +		xml_node next_sibling_w(const char* name) const; + +		/** +		 * Get following sibling +		 * +		 * \return following sibling node, if any; empty node otherwise +		 */ +		xml_node next_sibling() const; + +		/** +		 * Get first of preceding sibling nodes with the specified name +		 * +		 * \param name - sibling name +		 * \return node with the specified name, if any; empty node otherwise +		 */ +		xml_node previous_sibling(const char* name) const; + +		/** +		 * Get first of the preceding sibling nodes with the name that matches specified pattern +		 * +		 * \param name - sibling name pattern +		 * \return node with the name that matches pattern, if any; empty node otherwise +		 */ +		xml_node previous_sibling_w(const char* name) const; + +		/** +		 * Get preceding sibling +		 * +		 * \return preceding sibling node, if any; empty node otherwise +		 */ +		xml_node previous_sibling() const; + +		/** +		 * Get parent node +		 * +		 * \return parent node if any; empty node otherwise +		 */ +		xml_node parent() const; + +		/** +		 * Get root of DOM tree this node belongs to. +		 * +		 * \return tree root +		 */ +		xml_node root() const; + +		/** +		 * Get child value of current node; that is, value of the first child node of type PCDATA/CDATA +		 * +		 * \return child value of current node, if any; "" otherwise +		 */ +		const char* child_value() const; + +		/** +		 * Get child value of child with specified name. \see child_value +		 * node.child_value(name) is equivalent to node.child(name).child_value() +		 * +		 * \param name - child name +		 * \return child value of specified child node, if any; "" otherwise +		 */ +		const char* child_value(const char* name) const; + +		/** +		 * Get child value of child with name that matches the specified pattern. \see child_value +		 * node.child_value_w(name) is equivalent to node.child_w(name).child_value() +		 * +		 * \param name - child name pattern +		 * \return child value of specified child node, if any; "" otherwise +		 */ +		const char* child_value_w(const char* name) const; + +	public:	 +		/** +		 * Set node name to \a rhs (for PI/element nodes). \see name +		 * +		 * \param rhs - new node name +		 * \return success flag (call fails if node is of the wrong type or there is not enough memory) +		 */ +		bool set_name(const char* rhs); +		 +		/** +		 * Set node value to \a rhs (for PI/PCDATA/CDATA/comment nodes). \see value +		 * +		 * \param rhs - new node value +		 * \return success flag (call fails if node is of the wrong type or there is not enough memory) +		 */ +		bool set_value(const char* rhs); + +		/** +		 * Add attribute with specified name (for element nodes) +		 * +		 * \param name - attribute name +		 * \return added attribute, or empty attribute if there was an error (wrong node type) +		 */ +		xml_attribute append_attribute(const char* name); + +		/** +		 * Insert attribute with specified name after \a attr (for element nodes) +		 * +		 * \param name - attribute name +		 * \param attr - attribute to insert a new one after +		 * \return inserted attribute, or empty attribute if there was an error (wrong node type, or attr does not belong to node) +		 */ +		xml_attribute insert_attribute_after(const char* name, const xml_attribute& attr); + +		/** +		 * Insert attribute with specified name before \a attr (for element nodes) +		 * +		 * \param name - attribute name +		 * \param attr - attribute to insert a new one before +		 * \return inserted attribute, or empty attribute if there was an error (wrong node type, or attr does not belong to node) +		 */ +		xml_attribute insert_attribute_before(const char* name, const xml_attribute& attr); + +		/** +		 * Add child node with specified type (for element nodes) +		 * +		 * \param type - node type +		 * \return added node, or empty node if there was an error (wrong node type) +		 */ +		xml_node append_child(xml_node_type type = node_element); + +		/** +		 * Insert child node with specified type after \a node (for element nodes) +		 * +		 * \param type - node type +		 * \param node - node to insert a new one after +		 * \return inserted node, or empty node if there was an error (wrong node type, or \a node is not a child of this node) +		 */ +		xml_node insert_child_after(xml_node_type type, const xml_node& node); + +		/** +		 * Insert child node with specified type before \a node (for element nodes) +		 * +		 * \param type - node type +		 * \param node - node to insert a new one before +		 * \return inserted node, or empty node if there was an error (wrong node type, or \a node is not a child of this node) +		 */ +		xml_node insert_child_before(xml_node_type type, const xml_node& node); + +		/** +		 * Remove specified attribute +		 * +		 * \param a - attribute to be removed +		 */ +		void remove_attribute(const xml_attribute& a); + +		/** +		 * Remove attribute with the specified name, if any +		 * +		 * \param name - attribute name +		 */ +		void remove_attribute(const char* name); + +		/** +		 * Remove specified child +		 * +		 * \param n - child node to be removed +		 */ +		void remove_child(const xml_node& n); + +		/** +		 * Remove child with the specified name, if any +		 * +		 * \param name - child name +		 */ +		void remove_child(const char* name); + +	public: +		/** +		 * Get first attribute +		 * +		 * \return first attribute, if any; empty attribute otherwise +		 */ +		xml_attribute first_attribute() const; + +		/** +		 * Get last attribute +		 * +		 * \return last attribute, if any; empty attribute otherwise +		 */ +        xml_attribute last_attribute() const; + +		/** +		 * Get all elements from subtree with given name +		 * +		 * \param name - node name +		 * \param it - output iterator (for example, std::back_insert_iterator (result of std::back_inserter)) +		 */ +		template <typename OutputIterator> void all_elements_by_name(const char* name, OutputIterator it) const; + +		/** +		 * Get all elements from subtree with name that matches given pattern +		 * +		 * \param name - node name pattern +		 * \param it - output iterator (for example, std::back_insert_iterator (result of std::back_inserter)) +		 */ +		template <typename OutputIterator> void all_elements_by_name_w(const char* name, OutputIterator it) const; + +		/** +		 * Get first child +		 * +		 * \return first child, if any; empty node otherwise +		 */ +		xml_node first_child() const; + +		/** +		 * Get last child +		 * +		 * \return last child, if any; empty node otherwise +		 */ +        xml_node last_child() const; +		 +		/** +		 * Find attribute using predicate +		 * +		 * \param pred - predicate, that takes xml_attribute and returns bool +		 * \return first attribute for which predicate returned true, or empty attribute +		 */ +		template <typename Predicate> xml_attribute find_attribute(Predicate pred) const; + +		/** +		 * Find child node using predicate +		 * +		 * \param pred - predicate, that takes xml_node and returns bool +		 * \return first child node for which predicate returned true, or empty node +		 */ +		template <typename Predicate> xml_node find_child(Predicate pred) const; + +		/** +		 * Find node from subtree using predicate +		 * +		 * \param pred - predicate, that takes xml_node and returns bool +		 * \return first node from subtree for which predicate returned true, or empty node +		 */ +		template <typename Predicate> xml_node find_node(Predicate pred) const; + +#ifndef PUGIXML_NO_STL +		/** +		 * Get the absolute node path from root as a text string. +		 * +		 * \param delimiter - delimiter character to insert between element names +		 * \return path string (e.g. '/bookstore/book/author'). +		 */ +		std::string path(char delimiter = '/') const; +#endif + +		/** +		 * Search for a node by path. +		 * \param path - path string; e.g. './foo/bar' (relative to node), '/foo/bar' (relative  +		 * to root), '../foo/bar'. +		 * \param delimiter - delimiter character to use while tokenizing path +		 * \return matching node, if any; empty node otherwise +		 */ +		xml_node first_element_by_path(const char* path, char delimiter = '/') const; + +		/** +		 * Recursively traverse subtree with xml_tree_walker +		 * \see xml_tree_walker::begin +		 * \see xml_tree_walker::for_each +		 * \see xml_tree_walker::end +		 * +		 * \param walker - tree walker to traverse subtree with +		 * \return traversal result +		 */ +		bool traverse(xml_tree_walker& walker); +	 +	#ifndef PUGIXML_NO_XPATH +		/** +		 * Select single node by evaluating XPath query +		 *  +		 * \param query - query string +		 * \return first node from the resulting node set by document order, or empty node if none found +		 */ +		xpath_node select_single_node(const char* query) const; + +		/** +		 * Select single node by evaluating XPath query +		 * +		 * \param query - compiled query +		 * \return first node from the resulting node set by document order, or empty node if none found +		 */ +		xpath_node select_single_node(xpath_query& query) const; + +		/** +		 * Select node set by evaluating XPath query +		 * +		 * \param query - query string +		 * \return resulting node set +		 */ +		xpath_node_set select_nodes(const char* query) const; + +		/** +		 * Select node set by evaluating XPath query +		 * +		 * \param query - compiled query +		 * \return resulting node set +		 */ +		xpath_node_set select_nodes(xpath_query& query) const; +	#endif +		 +		/// \internal Document order or 0 if not set +		unsigned int document_order() const; + +	#ifndef PUGIXML_NO_STL +		/** +		 * Print subtree to stream +		 * +		 * \param os - output stream +		 * \param indent - indentation string +		 * \param flags - formatting flags +		 * \param depth - starting depth (used for indentation) +		 */ +		void print(std::ostream& os, const char* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0); +	#endif +	}; + +#ifdef __BORLANDC__ +	// Borland C++ workaround +	bool operator&&(const xml_node& lhs, bool rhs); +	bool operator||(const xml_node& lhs, bool rhs); +#endif + +	/** +	 * Child node iterator. +	 * It's a bidirectional iterator with value type 'xml_node'. +	 */ +	class xml_node_iterator +#ifndef PUGIXML_NO_STL +	: public std::iterator<std::bidirectional_iterator_tag, xml_node> +#endif +	{ +		friend class xml_node; + +	private: +		xml_node _prev; +		xml_node _wrap; + +		/// \internal Initializing ctor +		explicit xml_node_iterator(xml_node_struct* ref); + +	public: +		/** +		 * Default ctor +		 */ +		xml_node_iterator(); + +		/** +		 * Initializing ctor +		 * +		 * \param node - node that iterator will point at +		 */ +		xml_node_iterator(const xml_node& node); + +		/** +		 * Initializing ctor (for past-the-end) +		 * +		 * \param ref - should be 0 +		 * \param prev - previous node +		 */ +		xml_node_iterator(xml_node_struct* ref, xml_node_struct* prev); + +		/** +		 * Check if this iterator is equal to \a rhs +		 * +		 * \param rhs - other iterator +		 * \return comparison result +		 */ +		bool operator==(const xml_node_iterator& rhs) const; +		 +		/** +		 * Check if this iterator is not equal to \a rhs +		 * +		 * \param rhs - other iterator +		 * \return comparison result +		 */ +		bool operator!=(const xml_node_iterator& rhs) const; + +		/** +		 * Dereferencing operator +		 * +		 * \return reference to the node iterator points at +		 */ +		xml_node& operator*(); + +		/** +		 * Member access operator +		 * +		 * \return poitner to the node iterator points at +		 */ +		xml_node* operator->(); + +		/** +		 * Pre-increment operator +		 * +		 * \return self +		 */ +		const xml_node_iterator& operator++(); + +		/** +		 * Post-increment operator +		 * +		 * \return old value +		 */ +		xml_node_iterator operator++(int); +		 +		/** +		 * Pre-decrement operator +		 * +		 * \return self +		 */ +		const xml_node_iterator& operator--(); +		 +		/** +		 * Post-decrement operator +		 * +		 * \return old value +		 */ +		xml_node_iterator operator--(int); +	}; + +	/** +	 * Attribute iterator. +	 * It's a bidirectional iterator with value type 'xml_attribute'. +	 */ +	class xml_attribute_iterator +#ifndef PUGIXML_NO_STL +	: public std::iterator<std::bidirectional_iterator_tag, xml_attribute> +#endif +	{ +		friend class xml_node; + +	private: +		xml_attribute _prev; +		xml_attribute _wrap; + +		/// \internal Initializing ctor +		explicit xml_attribute_iterator(xml_attribute_struct* ref); + +	public: +		/** +		 * Default ctor +		 */ +		xml_attribute_iterator(); + +		/** +		 * Initializing ctor +		 * +		 * \param node - node that iterator will point at +		 */ +		xml_attribute_iterator(const xml_attribute& node); + +		/** +		 * Initializing ctor (for past-the-end) +		 * +		 * \param ref - should be 0 +		 * \param prev - previous node +		 */ +		xml_attribute_iterator(xml_attribute_struct* ref, xml_attribute_struct* prev); + +		/** +		 * Check if this iterator is equal to \a rhs +		 * +		 * \param rhs - other iterator +		 * \return comparison result +		 */ +		bool operator==(const xml_attribute_iterator& rhs) const; +		 +		/** +		 * Check if this iterator is not equal to \a rhs +		 * +		 * \param rhs - other iterator +		 * \return comparison result +		 */ +		bool operator!=(const xml_attribute_iterator& rhs) const; + +		/** +		 * Dereferencing operator +		 * +		 * \return reference to the node iterator points at +		 */ +		xml_attribute& operator*(); + +		/** +		 * Member access operator +		 * +		 * \return poitner to the node iterator points at +		 */ +		xml_attribute* operator->(); + +		/** +		 * Pre-increment operator +		 * +		 * \return self +		 */ +		const xml_attribute_iterator& operator++(); + +		/** +		 * Post-increment operator +		 * +		 * \return old value +		 */ +		xml_attribute_iterator operator++(int); +		 +		/** +		 * Pre-decrement operator +		 * +		 * \return self +		 */ +		const xml_attribute_iterator& operator--(); +		 +		/** +		 * Post-decrement operator +		 * +		 * \return old value +		 */ +		xml_attribute_iterator operator--(int); +	}; + +	/** +	 * Abstract tree walker class +	 * \see xml_node::traverse +	 */ +	class xml_tree_walker +	{ +		friend class xml_node; + +	private: +		int _depth; +	 +	protected: +		/** +		 * Get node depth +		 *  +		 * \return node depth +		 */ +		int depth() const; +	 +	public: +		/** +		 * Default ctor +		 */ +		xml_tree_walker(); + +		/** +		 * Virtual dtor +		 */ +		virtual ~xml_tree_walker(); + +	public: +		/** +		 * Callback that is called when traversal of node begins. +		 * +		 * \return returning false will abort the traversal +		 */ +		virtual bool begin(xml_node&); + +		/** +		 * Callback that is called for each node traversed +		 * +		 * \return returning false will abort the traversal +		 */ +		virtual bool for_each(xml_node&) = 0; + +		/** +		 * Callback that is called when traversal of node ends. +		 * +		 * \return returning false will abort the traversal +		 */ +		virtual bool end(xml_node&); +	}; + +	/// \internal Memory block +	struct xml_memory_block +	{ +		xml_memory_block(); + +		xml_memory_block* next; +		size_t size; + +		char data[memory_block_size]; +	}; + +	/** +	 * Struct used to distinguish parsing with ownership transfer from parsing without it. +	 * \see xml_document::parse +	 */ +	struct transfer_ownership_tag {}; + +	/** +	 * Document class (DOM tree root). +	 * This class has noncopyable semantics (private copy ctor/assignment operator). +	 */ +	class xml_document: public xml_node +	{ +	private: +		char*				_buffer; + +		xml_memory_block	_memory; +		 +		xml_document(const xml_document&); +		const xml_document& operator=(const xml_document&); + +		void create(); +		void free(); + +	public: +		/** +		 * Default ctor, makes empty document +		 */ +		xml_document(); + +		/** +		 * Dtor +		 */ +		~xml_document(); + +	public: +#ifndef PUGIXML_NO_STL +		/** +		 * Load document from stream. +		 * +		 * \param stream - stream with xml data +		 * \param options - parsing options +		 * \return success flag +		 */ +		bool load(std::istream& stream, unsigned int options = parse_default); +#endif + +		/** +		 * Load document from string. +		 * +		 * \param contents - input string +		 * \param options - parsing options +		 * \return success flag +		 */ +		bool load(const char* contents, unsigned int options = parse_default); + +		/** +		 * Load document from file +		 * +		 * \param name - file name +		 * \param options - parsing options +		 * \return success flag +		 */ +		bool load_file(const char* name, unsigned int options = parse_default); + +		/** +		 * Parse the given XML string in-situ. +		 * The string is modified; you should ensure that string data will persist throughout the +		 * document's lifetime. Although, document does not gain ownership over the string, so you +		 * should free the memory occupied by it manually. +		 * +		 * \param xmlstr - readwrite string with xml data +		 * \param options - parsing options +		 * \return success flag +		 */ +		bool parse(char* xmlstr, unsigned int options = parse_default); +		 +		/** +		 * Parse the given XML string in-situ (gains ownership). +		 * The string is modified; document gains ownership over the string, so you don't have to worry +		 * about it's lifetime. +		 * Call example: doc.parse(transfer_ownership_tag(), string, options); +		 * +		 * \param xmlstr - readwrite string with xml data +		 * \param options - parsing options +		 * \return success flag +		 */ +		bool parse(const transfer_ownership_tag&, char* xmlstr, unsigned int options = parse_default); +		 +#ifndef PUGIXML_NO_STL +		/** +		 * Save XML to file +		 * +		 * \param name - file name +		 * \param indent - indentation string +		 * \param flags - formatting flags +		 * \return success flag +		 */ +		bool save_file(const char* name, const char* indent = "\t", unsigned int flags = format_default); +#endif + +		/** +		 * Compute document order for the whole tree +		 * Sometimes this makes evaluation of XPath queries faster. +		 */ +		void precompute_document_order(); +	}; + +#ifndef PUGIXML_NO_XPATH +	/** +	 * XPath exception class. +	 */ +	class xpath_exception: public std::exception +	{ +	private: +		const char* m_message; + +	public: +		/** +		 * Construct exception from static error string +		 * +		 * \param message - error string +		 */ +		explicit xpath_exception(const char* message); + +		/** +		 * Return error message +		 * +		 * \return error message +		 */ +		virtual const char* what() const throw(); +	}; +	 +	/** +	 * XPath node class. +	 *  +	 * XPath defines node to be either xml_node or xml_attribute in pugixml terminology, so xpath_node +	 * is either xml_node or xml_attribute. +	 */ +	class xpath_node +	{ +	private: +		xml_node m_node; +		xml_attribute m_attribute; +	 +    	/// \internal Safe bool type +    	typedef xml_node xpath_node::*unspecified_bool_type; + +	public: +		/** +		 * Construct empty XPath node +		 */ +		xpath_node(); +		 +		/** +		 * Construct XPath node from XML node +		 * +		 * \param node - XML node +		 */ +		xpath_node(const xml_node& node); + +		/** +		 * Construct XPath node from XML attribute +		 * +		 * \param attribute - XML attribute +		 * \param parent - attribute's parent node +		 */ +		xpath_node(const xml_attribute& attribute, const xml_node& parent); + +		/** +		 * Get XML node, if any +		 * +		 * \return contained XML node, empty node otherwise +		 */ +		xml_node node() const; +		 +		/** +		 * Get XML attribute, if any +		 * +		 * \return contained XML attribute, if any, empty attribute otherwise +		 */ +		xml_attribute attribute() const; +		 +		/** +		 * Get parent of contained XML attribute, if any +		 * +		 * \return parent of contained XML attribute, if any, empty node otherwise +		 */ +		xml_node parent() const; + +    	/** +    	 * Safe bool conversion. +    	 * Allows xpath_node to be used in a context where boolean variable is expected, such as 'if (node)'. +    	 */ +		operator unspecified_bool_type() const; +		 +		/** +		 * Compares two XPath nodes +		 * +		 * \param n - XPath node to compare to +		 * \return comparison result +		 */ +		bool operator==(const xpath_node& n) const; +		 +		/** +		 * Compares two XPath nodes +		 * +		 * \param n - XPath node to compare to +		 * \return comparison result +		 */ +		bool operator!=(const xpath_node& n) const; +	}; + +	/** +	 * Not necessarily ordered constant collection of XPath nodes +	 */ +	class xpath_node_set +	{ +		friend class xpath_ast_node; +		 +	public: +		/// Collection type +		enum type_t +		{ +			type_unsorted,			///< Not ordered +			type_sorted,			///< Sorted by document order (ascending) +			type_sorted_reverse		///< Sorted by document order (descending) +		}; +		 +		/// Constant iterator type +		typedef const xpath_node* const_iterator; +	 +	private: +		type_t m_type; +		 +		xpath_node m_storage; +		 +		xpath_node* m_begin; +		xpath_node* m_end; +		xpath_node* m_eos; +		 +		bool m_using_storage; +		 +		typedef xpath_node* iterator; + +		iterator mut_begin(); +		iterator mut_end(); +		 +		void push_back(const xpath_node& n); +		 +		template <typename Iterator> void append(Iterator begin, Iterator end); +		 +		void truncate(iterator it); + +		void remove_duplicates(); + +	public: +		/** +		 * Default ctor +		 * Constructs empty set +		 */ +		xpath_node_set(); + +		/** +         * Dtor +         */ +		~xpath_node_set(); +		 +		/** +		 * Copy ctor +		 * +		 * \param ns - set to copy +		 */ +		xpath_node_set(const xpath_node_set& ns); + +		/** +		 * Assignment operator +		 * +		 * \param ns - set to assign +		 * \return self +		 */ +		xpath_node_set& operator=(const xpath_node_set& ns); +		 +		/** +		 * Get collection type +		 * +		 * \return collection type +		 */ +		type_t type() const; +		 +		/** +		 * Get collection size +		 * +		 * \return collection size +		 */ +		size_t size() const; +		 +		/** +		 * Get begin constant iterator for collection +		 * +		 * \return begin constant iterator +		 */ +		const_iterator begin() const; +		 +		/** +		 * Get end iterator for collection +		 * +		 * \return end iterator +		 */ +		const_iterator end() const; +		 +		/** +		 * Sort the collection in ascending/descending order by document order +		 * +		 * \param reverse - whether to sort in ascending (false) or descending (true) order +		 */ +		void sort(bool reverse = false); +		 +		/** +		 * Get first node in the collection by document order +		 * +		 * \return first node by document order +		 */ +		xpath_node first() const; +		 +		/** +		 * Return true if collection is empty +		 * +		 * \return true if collection is empty, false otherwise +		 */ +		bool empty() const; +	}; +#endif + +#ifndef PUGIXML_NO_STL +	/** +	 * Convert utf16 to utf8 +	 * +	 * \param str - input UTF16 string +	 * \return output UTF8 string +	 */ +	std::string as_utf8(const wchar_t* str); +	 +	/** +	 * Convert utf8 to utf16 +	 * +	 * \param str - input UTF8 string +	 * \return output UTF16 string +	 */ +	std::wstring as_utf16(const char* str); +#endif +} + +// 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 (node.type() == node_element) +			{ +				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 (node.type() == node_element) +			{ +				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_node(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 diff --git a/src/pugixpath.cpp b/src/pugixpath.cpp new file mode 100644 index 0000000..75b5844 --- /dev/null +++ b/src/pugixpath.cpp @@ -0,0 +1,3537 @@ +///////////////////////////////////////////////////////////////////////////////
 +//
 +// Pug Improved XML Parser - Version 0.3
 +// --------------------------------------------------------
 +// Copyright (C) 2006-2007, by Arseny Kapoulkine (arseny.kapoulkine@gmail.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 "pugixml.hpp"
 +
 +#ifndef PUGIXML_NO_XPATH
 +
 +#include <algorithm>
 +
 +#include <cassert>
 +
 +#include <stdio.h>
 +#include <math.h>
 +#include <float.h>
 +#include <ctype.h>
 +
 +#if defined(_MSC_VER)
 +#	pragma warning(disable: 4127) // conditional expression is constant
 +#	pragma warning(disable: 4702) // unreachable code
 +#	pragma warning(disable: 4996) // this function or variable may be unsafe
 +#endif
 +
 +namespace
 +{
 +	using namespace pugi;
 +		
 +	enum chartype
 +	{
 +		ct_space = 1,			// \r, \n, space, tab
 +		ct_start_symbol = 2,	// Any symbol > 127, a-z, A-Z, _, :
 +		ct_digit = 4,			// 0-9
 +		ct_symbol = 8			// Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
 +	};
 +	
 +	const unsigned char chartype_table[256] =
 +	{
 +		0,  0,  0,  0,  0,  0,  0,  0,     0,  1,  1,  0,  0,  1,  0,  0,     // 0-15
 +		0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0,     // 16-31
 +		1,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  8,  8,  0,     // 32-47
 +		12, 12, 12, 12, 12, 12, 12, 12,    12, 12, 10, 0,  0,  0,  0,  0,     // 48-63
 +		0,  10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,    // 64-79
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 0,  0,  0,  0,  10,    // 80-95
 +		0,  10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,    // 96-111
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 0,  0,  0,  0,  0,     // 112-127
 +
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,    // 128+
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10,
 +		10, 10, 10, 10, 10, 10, 10, 10,    10, 10, 10, 10, 10, 10, 10, 10
 +	};
 +	
 +	bool is_chartype(char c, chartype ct)
 +	{
 +		return !!(chartype_table[static_cast<unsigned char>(c)] & ct);
 +	}
 +
 +	bool starts_with(const std::string& s, const char* pattern)
 +	{
 +		return s.compare(0, strlen(pattern), pattern) == 0;
 +	}
 +
 +	std::string string_value(const xpath_node& na)
 +	{
 +		if (na.attribute())
 +			return na.attribute().value();
 +		else
 +		{
 +			const xml_node& n = na.node();
 +
 +			switch (n.type())
 +			{
 +			case node_pcdata:
 +			case node_cdata:
 +			case node_comment:
 +			case node_pi:
 +				return n.value();
 +			
 +			case node_document:
 +			case node_element:
 +			{
 +				std::string result;
 +
 +				xml_node c = n.first_child();
 +				
 +				while (c)
 +				{
 +					if (c.type() == node_pcdata || c.type() == node_cdata)
 +						result += c.value();
 +				
 +					if (c.first_child())
 +						c = c.first_child();
 +					else if (c.next_sibling())
 +						c = c.next_sibling();
 +					else
 +					{
 +						while (c && c != n) c = c.parent();
 +						
 +						if (c == n) break;
 +						
 +						c = c.next_sibling();
 +					}
 +				}
 +				
 +				return result;
 +			}
 +			
 +			default:
 +				return "";
 +			}
 +		}
 +	}
 +	
 +	struct document_order_comparator
 +	{
 +		bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
 +		{
 +			unsigned int lo = lhs.attribute() ? lhs.attribute().document_order() : lhs.node().document_order();
 +			unsigned int ro = rhs.attribute() ? rhs.attribute().document_order() : rhs.node().document_order();
 +			
 +			if (lo != 0 && ro != 0)
 +				return lo < ro;
 +
 +			xml_node ln = lhs.node(), rn = rhs.node();
 +
 +			if (lhs.attribute() && rhs.attribute())
 +			{
 +				if (lhs.parent() == rhs.parent()) return lhs.attribute() < rhs.attribute();
 +				
 +				ln = lhs.parent();
 +				rn = rhs.parent();
 +			}
 +			else if (lhs.attribute())
 +			{
 +				if (lhs.parent() == rhs.node()) return false;
 +				
 +				ln = lhs.parent();
 +			}
 +			else if (rhs.attribute())
 +			{
 +				if (rhs.parent() == lhs.node()) return true;
 +				
 +				rn = rhs.parent();
 +			}
 +
 +			if (ln == rn) return false;
 +				
 +			xml_node lp = ln, rp = rn;
 +				
 +			while (lp != rp)
 +			{
 +				ln = lp;
 +				lp = lp.parent();
 +					
 +				if (lp != rp)
 +				{
 +					rn = rp;
 +					rp = rp.parent();
 +				}
 +			}
 +				
 +			if (!lp) // no common parent - ???
 +				return false;
 +			else // lp is parent, ln & rn are distinct siblings
 +			{
 +				for (; ln; ln = ln.next_sibling());
 +					if (ln == rn)
 +						return true;
 +			
 +				return false;
 +			}
 +		}
 +	};
 +
 +	struct duplicate_comparator
 +	{
 +		bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
 +		{
 +			if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
 +			else return rhs.attribute() ? false : lhs.node() < rhs.node();
 +		}
 +	};
 +	
 +	/* From trio
 +	 *
 +	 * Endian-agnostic indexing macro.
 +	 *
 +	 * The value of internalEndianMagic, when converted into a 64-bit
 +	 * integer, becomes 0x0706050403020100 (we could have used a 64-bit
 +	 * integer value instead of a double, but not all platforms supports
 +	 * that type). The value is automatically encoded with the correct
 +	 * endianess by the compiler, which means that we can support any
 +	 * kind of endianess. The individual bytes are then used as an index
 +	 * for the IEEE 754 bit-patterns and masks.
 +	 */
 +	#define DOUBLE_INDEX(x) (((unsigned char *)&internal_endian_magic)[7-(x)])
 +
 +	static const double internal_endian_magic = 7.949928895127363e-275;
 +
 +	static const unsigned char ieee_754_exponent_mask[] = { 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 +
 +	static const unsigned char ieee_754_mantissa_mask[] = { 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 +
 +	static const unsigned char ieee_754_qnan_array[] = { 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 +	
 +	bool is_special(double value, bool& has_mantissa)
 +	{
 +		bool is_special_quantity = true;
 +
 +		has_mantissa = false;
 +
 +		for (unsigned int i = 0; i < sizeof(double); ++i)
 +		{
 +			unsigned char current = ((unsigned char *)&value)[DOUBLE_INDEX(i)];
 +			
 +			is_special_quantity = is_special_quantity && (current & ieee_754_exponent_mask[i]) == ieee_754_exponent_mask[i];
 +			has_mantissa = has_mantissa || (current & ieee_754_mantissa_mask[i]) != 0;
 +		}
 +		
 +		return is_special_quantity;
 +	}
 +
 +	double gen_nan()
 +	{
 +#if FLT_RADIX == 2 && DBL_MAX_EXP == 1024 && DBL_MANT_DIG == 53
 +		// IEEE 754
 +		
 +		double result = 0;
 +
 +		for (unsigned int i = 0; i < sizeof(double); ++i)
 +		{
 +			((unsigned char *)&result)[DOUBLE_INDEX(i)] = ieee_754_qnan_array[i];
 +		}
 +		
 +		return result;
 +#else
 +		const volatile double zero = 0.0;
 +		return zero / zero;
 +#endif
 +	}
 +	
 +	bool is_nan(double value)
 +	{
 +#if defined(__USE_ISOC99)
 +		return isnan(value);
 +#elif defined(_MSC_VER) || defined(__BORLANDC__)
 +		return !!_isnan(value);
 +#elif FLT_RADIX == 2 && DBL_MAX_EXP == 1024 && DBL_MANT_DIG == 53
 +		// IEEE 754
 +
 +		bool has_mantissa;
 +		
 +		bool is_special_quantity = is_special(value, has_mantissa);
 +  
 +		return (is_special_quantity && has_mantissa);
 +#else
 +		return value != value;
 +#endif
 +	}
 +	
 +	bool is_inf(double value)
 +	{
 +#if defined(__USE_ISOC99)
 +		return !isfinite(value);
 +#elif defined(_MSC_VER) || defined(__BORLANDC__)
 +		return !_finite(value);
 +#elif FLT_RADIX == 2 && DBL_MAX_EXP == 1024 && DBL_MANT_DIG == 53
 +		// IEEE 754
 +  
 +		bool has_mantissa;
 +		
 +		bool is_special_quantity = is_special(value, has_mantissa);
 +  
 +		return (is_special_quantity && !has_mantissa);
 +#else
 +		return value + 1 == value && value - 1 == value;
 +#endif
 +	}
 +	
 +	bool convert_number_to_boolean(double value)
 +	{
 +		return (value != 0 && !is_nan(value));
 +	}
 +	
 +	const char* convert_number_to_string(double value)
 +	{
 +		if (is_nan(value)) return "NaN";
 +		else if (is_inf(value)) return value < 0 ? "-Infinity" : "Infinity";
 +		
 +		static char buf[100];
 +		
 +		if (value == (int)value) sprintf(buf, "%d", (int)value);
 +		else
 +		{
 +			sprintf(buf, "%f", value);
 +			
 +			// trim trailing zeros after decimal point
 +			if (strchr(buf, '.'))
 +			{
 +				char* ptr = buf + strlen(buf) - 1;
 +				for (; *ptr == '0'; --ptr) ;
 +				*(ptr+1) = 0;
 +			}
 +		}
 +		
 +		return buf;
 +	}
 +	
 +	double convert_string_to_number(const char* string)
 +	{
 +		while (is_chartype(*string, ct_space)) ++string;
 +		
 +		double sign = 1;
 +		
 +		if (*string == '-')
 +		{
 +			sign = -1;
 +			++string;
 +		}
 +		
 +		double r = 0;
 +		
 +		if (!*string) return gen_nan();
 +		
 +		while (is_chartype(*string, ct_digit))
 +		{
 +			r = r * 10 + (*string - '0');
 +			++string;
 +		}
 +		
 +		if (*string)
 +		{
 +			if (is_chartype(*string, ct_space))
 +			{
 +				while (is_chartype(*string, ct_space)) ++string;
 +				if (*string) return gen_nan();
 +			}
 +			
 +			if (*string != '.') return gen_nan();
 +			
 +			++string;
 +			
 +			double power = 0.1;
 +			
 +			while (is_chartype(*string, ct_digit))
 +			{
 +				r += power * (*string - '0');
 +				power /= 10;
 +				++string;
 +			}
 +			
 +			while (is_chartype(*string, ct_space)) ++string;
 +			if (*string) return gen_nan();
 +		}
 +		
 +		return r * sign;
 +	}
 +	
 +	double ieee754_round(double value)
 +	{
 +		return value == value ? floor(value + 0.5) : value;
 +	}
 +	
 +	const char* local_name(const char* name)
 +	{
 +		const char* p = strchr(name, ':');
 +		
 +		return p ? p + 1 : name;
 +	}
 +	
 +	const char* namespace_uri(const xml_node& node)
 +	{
 +		const char* pos = strchr(node.name(), ':');
 +		
 +		std::string ns = "xmlns";
 +		
 +		if (pos)
 +		{
 +			ns += ':';
 +			ns.append(node.name(), pos);
 +		}
 +		
 +		xml_node p = node.parent();
 +		
 +		while (p)
 +		{
 +			xml_attribute a = p.attribute(ns.c_str());
 +			
 +			if (a) return a.value();
 +			
 +			p = p.parent();
 +		}
 +		
 +		return "";
 +	}
 +
 +	const char* namespace_uri(const xml_attribute& attr, const xml_node& parent)
 +	{
 +		const char* pos = strchr(attr.name(), ':');
 +		
 +		// Default namespace does not apply to attributes
 +		if (!pos) return "";
 +		
 +		std::string ns = "xmlns:";
 +		ns.append(attr.name(), pos);
 +		
 +		xml_node p = parent;
 +		
 +		while (p)
 +		{
 +			xml_attribute a = p.attribute(ns.c_str());
 +			
 +			if (a) return a.value();
 +			
 +			p = p.parent();
 +		}
 +		
 +		return "";
 +	}
 +
 +	template <class T> struct equal_to
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs == rhs;
 +		}
 +	};
 +
 +	template <class T> struct not_equal_to
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs != rhs;
 +		}
 +	};
 +
 +	template <class T> struct greater
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs > rhs;
 +		}
 +	};
 +	
 +	template <class T> struct less
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs < rhs;
 +		}
 +	};
 +
 +	template <class T> struct greater_equal
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs >= rhs;
 +		}
 +	};
 +
 +	template <class T> struct less_equal
 +	{
 +		bool operator()(const T& lhs, const T& rhs) const
 +		{
 +			return lhs <= rhs;
 +		}
 +	};
 +}
 +
 +namespace pugi
 +{
 +	xpath_exception::xpath_exception(const char* message): m_message(message)
 +	{
 +	}
 +	
 +	const char* xpath_exception::what() const throw()
 +	{
 +		return m_message;
 +	}
 +	
 +	const size_t xpath_memory_block_size = 4096;		///< Memory block size, 4 kb
 +
 +	class xpath_allocator
 +	{
 +		struct memory_block
 +		{	
 +			memory_block(): next(0), size(0)
 +			{
 +			}
 +	
 +			memory_block* next;
 +			size_t size;
 +	
 +			char data[xpath_memory_block_size];
 +		};
 +		
 +		memory_block* m_root;
 +		
 +	public:
 +		xpath_allocator(): m_root(0)
 +		{
 +			m_root = new memory_block;
 +		}
 +		
 +		~xpath_allocator()
 +		{
 +			while (m_root)
 +			{
 +				memory_block* cur = m_root->next;
 +				delete m_root;
 +				m_root = cur;
 +			}
 +		}
 +
 +		void* alloc(size_t size)
 +		{
 +			if (m_root->size + size <= xpath_memory_block_size)
 +			{
 +				void* buf = m_root->data + m_root->size;
 +				m_root->size += size;
 +				return buf;
 +			}
 +			else
 +			{
 +				memory_block* block;
 +				
 +				if (size > xpath_memory_block_size)
 +					block = static_cast<memory_block*>(operator new(size + sizeof(memory_block) - xpath_memory_block_size));
 +				else
 +					block = new memory_block;
 +					
 +				block->next = m_root;
 +				block->size = size;
 +				
 +				m_root = block;
 +				
 +				return block->data;
 +			}
 +		}
 +	};
 +
 +	xpath_node::xpath_node()
 +	{
 +	}
 +		
 +	xpath_node::xpath_node(const xml_node& node): m_node(node)
 +	{
 +	}
 +		
 +	xpath_node::xpath_node(const xml_attribute& attribute, const xml_node& parent): m_node(parent), m_attribute(attribute)
 +	{
 +	}
 +
 +	xml_node xpath_node::node() const
 +	{
 +		return m_attribute ? xml_node() : m_node;
 +	}
 +		
 +	xml_attribute xpath_node::attribute() const
 +	{
 +		return m_attribute;
 +	}
 +	
 +	xml_node xpath_node::parent() const
 +	{
 +		return m_attribute ? m_node : m_node.parent();
 +	}
 +
 +	xpath_node::operator xpath_node::unspecified_bool_type() const
 +	{
 +		return (m_node || m_attribute) ? &xpath_node::m_node : 0;
 +	}
 +	
 +	bool xpath_node::operator==(const xpath_node& n) const
 +	{
 +		return m_node == n.m_node && m_attribute == n.m_attribute;
 +	}
 +	
 +	bool xpath_node::operator!=(const xpath_node& n) const
 +	{
 +		return m_node != n.m_node || m_attribute != n.m_attribute;
 +	}
 +
 +	xpath_node_set::xpath_node_set(): m_type(type_unsorted), m_begin(&m_storage), m_end(&m_storage), m_eos(&m_storage + 1), m_using_storage(true)
 +	{
 +	}
 +
 +	xpath_node_set::~xpath_node_set()
 +	{
 +		if (!m_using_storage) delete[] m_begin;
 +	}
 +		
 +	xpath_node_set::xpath_node_set(const xpath_node_set& ns): m_type(type_unsorted), m_begin(&m_storage), m_end(&m_storage), m_eos(&m_storage + 1), m_using_storage(true)
 +	{
 +		*this = ns;
 +	}
 +	
 +	xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
 +	{
 +		if (!m_using_storage) delete[] m_begin;
 +		
 +		m_begin = m_end = m_eos = 0;
 +		
 +		if (ns.size() == 1)
 +		{
 +			m_storage = *ns.m_begin;
 +			m_begin = &m_storage;
 +			m_end = m_eos = &m_storage + 1;
 +			m_using_storage = true;
 +		}
 +		else
 +		{
 +			m_using_storage = false;
 +			append(ns.begin(), ns.end());
 +		}
 +		
 +		return *this;
 +	}	
 +
 +	xpath_node_set::type_t xpath_node_set::type() const
 +	{
 +		return m_type;
 +	}
 +		
 +	size_t xpath_node_set::size() const
 +	{
 +		return m_end - m_begin;
 +	}
 +		
 +	bool xpath_node_set::empty() const
 +	{
 +		return size() == 0;
 +	}
 +		
 +	xpath_node_set::iterator xpath_node_set::mut_begin()
 +	{
 +		return m_begin;
 +	}
 +	
 +	xpath_node_set::const_iterator xpath_node_set::begin() const
 +	{
 +		return m_begin;
 +	}
 +		
 +	xpath_node_set::iterator xpath_node_set::mut_end()
 +	{
 +		return m_end;
 +	}
 +	
 +	xpath_node_set::const_iterator xpath_node_set::end() const
 +	{
 +		return m_end;
 +	}
 +	
 +	void xpath_node_set::sort(bool reverse)
 +	{
 +		std::sort(m_begin, m_end, document_order_comparator());
 +		
 +		if (reverse)
 +			std::reverse(m_begin, m_end);
 +			
 +		m_type = reverse ? type_sorted_reverse : type_sorted;
 +	}
 +
 +	void xpath_node_set::push_back(const xpath_node& n)
 +	{
 +		if (m_end == m_eos)
 +			append(&n, &n + 1);
 +		else
 +		{
 +			*m_end = n;
 +			++m_end;
 +		}
 +	}
 +
 +	template <typename Iterator> void xpath_node_set::append(Iterator begin, Iterator end)
 +	{
 +		size_t count = std::distance(begin, end);
 +		size_t size = m_end - m_begin;
 +		size_t capacity = m_eos - m_begin;
 +		
 +		if (capacity < size + count)
 +		{
 +			if (capacity < 2) capacity = 2;
 +			
 +			while (capacity < size + count) capacity += capacity / 2;
 +			
 +			xpath_node* storage = new xpath_node[capacity];
 +			std::copy(m_begin, m_end, storage);
 +			
 +			if (!m_using_storage) delete[] m_begin;
 +			
 +			m_using_storage = false;
 +			
 +			m_begin = storage;
 +			m_end = storage + size;
 +			m_eos = storage + capacity;
 +		}
 +		
 +		std::copy(begin, end, m_end);
 +		m_end += count;
 +	}
 +
 +	void xpath_node_set::truncate(iterator it)
 +	{
 +		m_end = it;
 +	}
 +
 +	xpath_node xpath_node_set::first() const
 +	{
 +		switch (m_type)
 +		{
 +		case type_sorted: return *m_begin;
 +		case type_sorted_reverse: return *(m_end - 1);
 +		case type_unsorted: return *std::min_element(begin(), end(), document_order_comparator());
 +		default: return xpath_node();
 +		}
 +	}
 +
 +	void xpath_node_set::remove_duplicates()
 +	{
 +		if (m_type == type_unsorted)
 +		{
 +			std::sort(m_begin, m_end, duplicate_comparator());
 +		}
 +		
 +		truncate(std::unique(m_begin, m_end));
 +	}
 +
 +	struct xpath_context
 +	{
 +		xml_node root;
 +		xpath_node n;
 +		size_t position, size;
 +	};
 +
 +	enum lexeme_t
 +	{
 +		lex_none = 0,
 +		lex_equal,
 +		lex_not_equal,
 +		lex_less,
 +		lex_greater,
 +		lex_less_or_equal,
 +		lex_greater_or_equal,
 +		lex_plus,
 +		lex_minus,
 +		lex_multiply,
 +		lex_union,
 +		lex_var_ref,
 +		lex_open_brace,
 +		lex_close_brace,
 +		lex_quoted_string,
 +		lex_number,
 +		lex_slash,
 +		lex_double_slash,
 +		lex_open_square_brace,
 +		lex_close_square_brace,
 +		lex_string,
 +		lex_comma,
 +		lex_axis_attribute,
 +		lex_dot,
 +		lex_double_dot
 +	};
 +
 +	class xpath_lexer
 +	{
 +	private:
 +		const char* m_cur;
 +
 +		char* m_cur_lexeme_contents;
 +		size_t m_clc_size;
 +		size_t m_clc_capacity;
 +
 +		lexeme_t m_cur_lexeme;
 +
 +		void contents_clear()
 +		{
 +			m_clc_size = 0;
 +		}
 +
 +		void contents_push(char c)
 +		{
 +			if (m_clc_size == m_clc_capacity)
 +			{
 +				if (!m_clc_capacity) m_clc_capacity = 16;
 +				else m_clc_capacity *= 2;
 +
 +				char* s = new char[m_clc_capacity + 1];
 +				if (m_cur_lexeme_contents) strcpy(s, m_cur_lexeme_contents);
 +				
 +				delete[] m_cur_lexeme_contents;
 +				m_cur_lexeme_contents = s;
 +			}
 +
 +			m_cur_lexeme_contents[m_clc_size++] = c;
 +			m_cur_lexeme_contents[m_clc_size] = 0;
 +		}
 +
 +	public:
 +		explicit xpath_lexer(const char* query): m_cur(query)
 +		{
 +			m_clc_capacity = m_clc_size = 0;
 +			m_cur_lexeme_contents = 0;
 +
 +			next();
 +		}
 +		
 +		~xpath_lexer()
 +		{
 +			delete[] m_cur_lexeme_contents;
 +		}
 +		
 +		const char* state() const
 +		{
 +			return m_cur;
 +		}
 +		
 +		void reset(const char* state)
 +		{
 +			m_cur = state;
 +			next();
 +		}
 +
 +		void next()
 +		{
 +			contents_clear();
 +
 +			while (is_chartype(*m_cur, ct_space)) ++m_cur;
 +
 +			switch (*m_cur)
 +			{
 +			case 0:
 +				m_cur_lexeme = lex_none;
 +				break;
 +			
 +			case '>':
 +				if (*(m_cur+1) == '=')
 +				{
 +					m_cur += 2;
 +					m_cur_lexeme = lex_greater_or_equal;
 +				}
 +				else
 +				{
 +					m_cur += 1;
 +					m_cur_lexeme = lex_greater;
 +				}
 +				break;
 +
 +			case '<':
 +				if (*(m_cur+1) == '=')
 +				{
 +					m_cur += 2;
 +					m_cur_lexeme = lex_less_or_equal;
 +				}
 +				else
 +				{
 +					m_cur += 1;
 +					m_cur_lexeme = lex_less;
 +				}
 +				break;
 +
 +			case '!':
 +				if (*(m_cur+1) == '=')
 +				{
 +					m_cur += 2;
 +					m_cur_lexeme = lex_not_equal;
 +				}
 +				else
 +				{
 +					m_cur_lexeme = lex_none;
 +				}
 +				break;
 +
 +			case '=':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_equal;
 +
 +				break;
 +			
 +			case '+':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_plus;
 +
 +				break;
 +
 +			case '-':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_minus;
 +
 +				break;
 +
 +			case '*':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_multiply;
 +
 +				break;
 +
 +			case '|':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_union;
 +
 +				break;
 +
 +			case '$':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_var_ref;
 +
 +				break;
 +			
 +			case '(':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_open_brace;
 +
 +				break;
 +
 +			case ')':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_close_brace;
 +
 +				break;
 +			
 +			case '[':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_open_square_brace;
 +
 +				break;
 +
 +			case ']':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_close_square_brace;
 +
 +				break;
 +
 +			case ',':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_comma;
 +
 +				break;
 +
 +			case '/':
 +				if (*(m_cur+1) == '/')
 +				{
 +					m_cur += 2;
 +					m_cur_lexeme = lex_double_slash;
 +				}
 +				else
 +				{
 +					m_cur += 1;
 +					m_cur_lexeme = lex_slash;
 +				}
 +				break;
 +		
 +			case '.':
 +				if (*(m_cur+1) == '.')
 +				{
 +					m_cur += 2;
 +					m_cur_lexeme = lex_double_dot;
 +				}
 +				else if (is_chartype(*(m_cur+1), ct_digit))
 +				{
 +					contents_push('0');
 +					contents_push('.');
 +
 +					++m_cur;
 +
 +					while (is_chartype(*m_cur, ct_digit))
 +						contents_push(*m_cur++);
 +					
 +					m_cur_lexeme = lex_number;
 +				}
 +				else
 +				{
 +					m_cur += 1;
 +					m_cur_lexeme = lex_dot;
 +				}
 +				break;
 +
 +			case '@':
 +				m_cur += 1;
 +				m_cur_lexeme = lex_axis_attribute;
 +
 +				break;
 +
 +			case '"':
 +			case '\'':
 +			{
 +				char terminator = *m_cur;
 +
 +				++m_cur;
 +
 +				while (*m_cur && *m_cur != terminator)
 +					contents_push(*m_cur++);
 +				
 +				if (!*m_cur)
 +					m_cur_lexeme = lex_none;
 +				else
 +				{
 +					m_cur += 1;
 +					m_cur_lexeme = lex_quoted_string;
 +				}
 +
 +				break;
 +			}
 +
 +			default:
 +				if (is_chartype(*m_cur, ct_digit))
 +				{
 +					while (is_chartype(*m_cur, ct_digit))
 +						contents_push(*m_cur++);
 +				
 +					if (*m_cur == '.' && is_chartype(*(m_cur+1), ct_digit))
 +					{
 +						contents_push(*m_cur++);
 +
 +						while (is_chartype(*m_cur, ct_digit))
 +							contents_push(*m_cur++);
 +					}
 +
 +					m_cur_lexeme = lex_number;
 +				}
 +				else if (is_chartype(*m_cur, ct_start_symbol))
 +				{
 +					while (is_chartype(*m_cur, ct_symbol))
 +						contents_push(*m_cur++);
 +				
 +					while (is_chartype(*m_cur, ct_space)) ++m_cur;
 +
 +					m_cur_lexeme = lex_string;
 +				}
 +			}
 +		}
 +
 +		lexeme_t current() const
 +		{
 +			return m_cur_lexeme;
 +		}
 +
 +		const char* contents() const
 +		{
 +			return m_cur_lexeme_contents;
 +		}
 +	};
 +
 +	enum ast_type_t
 +	{
 +		ast_none,
 +		ast_op_or,						// left or right
 +		ast_op_and,						// left and right
 +		ast_op_equal,					// left = right
 +		ast_op_not_equal, 				// left != right
 +		ast_op_less,					// left < right
 +		ast_op_greater,					// left > right
 +		ast_op_less_or_equal,			// left <= right
 +		ast_op_greater_or_equal,		// left >= right
 +		ast_op_add,						// left + right
 +		ast_op_subtract,				// left - right
 +		ast_op_multiply,				// left * right
 +		ast_op_divide,					// left / right
 +		ast_op_mod,						// left % right
 +		ast_op_negate,					// left - right
 +		ast_op_union,					// left | right
 +		ast_predicate,					// apply predicate to set; next points to next predicate
 +		ast_filter,						// select * from left where right
 +		ast_filter_posinv,				// select * from left where right; proximity position invariant
 +		ast_variable,					// variable value
 +		ast_string_constant,			// string constant
 +		ast_number_constant,			// number constant
 +		ast_func_last,					// last()
 +		ast_func_position,				// position()
 +		ast_func_count,					// count(left)
 +		ast_func_id,					// id(left)
 +		ast_func_local_name_0,			// local-name()
 +		ast_func_local_name_1,			// local-name(left)
 +		ast_func_namespace_uri_0,		// namespace-uri()
 +		ast_func_namespace_uri_1,		// namespace-uri(left)
 +		ast_func_name_0,				// name()
 +		ast_func_name_1,				// name(left)
 +		ast_func_string_0,				// string()
 +		ast_func_string_1,				// string(left)
 +		ast_func_concat,				// concat(left, right, siblings)
 +		ast_func_starts_with,			// starts_with(left, right)
 +		ast_func_contains,				// contains(left, right)
 +		ast_func_substring_before,		// substring-before(left, right)
 +		ast_func_substring_after,		// substring-after(left, right)
 +		ast_func_substring_2,			// substring(left, right)
 +		ast_func_substring_3,			// substring(left, right, third)
 +		ast_func_string_length_0,		// string-length()
 +		ast_func_string_length_1,		// string-length(left)
 +		ast_func_normalize_space_0,		// normalize-space()
 +		ast_func_normalize_space_1,		// normalize-space(left)
 +		ast_func_translate,				// translate(left, right, third)
 +		ast_func_boolean,				// boolean(left)
 +		ast_func_not,					// not(left)
 +		ast_func_true,					// true()
 +		ast_func_false,					// false()
 +		ast_func_lang,					// lang(left)
 +		ast_func_number_0,				// number()
 +		ast_func_number_1,				// number(left)
 +		ast_func_sum,					// sum(left)
 +		ast_func_floor,					// floor(left)
 +		ast_func_ceiling,				// ceiling(left)
 +		ast_func_round,					// round(left)
 +		ast_step,						// process set left with step
 +		ast_step_root					// select root node
 +	};
 +
 +	enum ast_rettype_t
 +	{
 +		ast_type_none,
 +		ast_type_node_set,
 +		ast_type_number,
 +		ast_type_string,
 +		ast_type_boolean
 +	};
 +
 +	enum axis_t
 +	{
 +		axis_ancestor,
 +		axis_ancestor_or_self,
 +		axis_attribute,
 +		axis_child,
 +		axis_descendant,
 +		axis_descendant_or_self,
 +		axis_following,
 +		axis_following_sibling,
 +		axis_namespace,
 +		axis_parent,
 +		axis_preceding,
 +		axis_preceding_sibling,
 +		axis_self
 +	};
 +	
 +	enum nodetest_t
 +	{
 +		nodetest_name,
 +		nodetest_type_node,
 +		nodetest_type_comment,
 +		nodetest_type_pi,
 +		nodetest_type_text,
 +		nodetest_pi,
 +		nodetest_all,
 +		nodetest_all_in_namespace
 +	};
 +		
 +	class xpath_ast_node
 +	{
 +	private:
 +		ast_type_t m_type;
 +		
 +		ast_rettype_t m_rettype;
 +
 +		// tree node structure
 +		xpath_ast_node* m_left;
 +		xpath_ast_node*	m_right;
 +		xpath_ast_node* m_third;
 +		xpath_ast_node* m_next;
 +
 +		// variable name for ast_variable
 +		// string value for ast_constant
 +		// node test for ast_step (node name/namespace/node type/pi target)
 +		const char* m_contents;
 +
 +		// for t_step / t_predicate
 +		axis_t m_axis;
 +		nodetest_t m_test;
 +		
 +		xpath_ast_node(const xpath_ast_node&);
 +		xpath_ast_node& operator=(const xpath_ast_node&);
 +
 +		template <class Cbool, class Cdouble, class Cstring> bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c)
 +		{
 +			if (lhs->rettype() != ast_type_node_set && rhs->rettype() != ast_type_node_set)
 +			{
 +				if (lhs->rettype() == ast_type_boolean || rhs->rettype() == ast_type_boolean)
 +					return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
 +				else if (lhs->rettype() == ast_type_number || rhs->rettype() == ast_type_number)
 +					return Cdouble()(lhs->eval_number(c), rhs->eval_number(c));
 +				else if (lhs->rettype() == ast_type_string || rhs->rettype() == ast_type_string)
 +					return Cstring()(lhs->eval_string(c), rhs->eval_string(c));
 +				else
 +				{
 +					assert(!"Wrong types");
 +					return false;
 +				}
 +			}
 +			else if (lhs->rettype() == ast_type_node_set && rhs->rettype() == ast_type_node_set)
 +			{
 +				xpath_node_set ls = lhs->eval_node_set(c);
 +				xpath_node_set rs = rhs->eval_node_set(c);
 +				
 +				for (xpath_node_set::const_iterator li = ls.begin(); li != ls.end(); ++li)
 +				for (xpath_node_set::const_iterator ri = rs.begin(); ri != rs.end(); ++ri)
 +				{
 +					if (Cstring()(string_value(*li), string_value(*ri)))
 +						return true;
 +				}
 +				
 +				return false;
 +			}
 +			else if (lhs->rettype() != ast_type_node_set && rhs->rettype() == ast_type_node_set)
 +			{
 +				if (lhs->rettype() == ast_type_boolean)
 +					return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
 +				else if (lhs->rettype() == ast_type_number)
 +				{
 +					double l = lhs->eval_number(c);
 +					xpath_node_set rs = rhs->eval_node_set(c);
 +					
 +					for (xpath_node_set::const_iterator ri = rs.begin(); ri != rs.end(); ++ri)
 +					{
 +						if (Cdouble()(l, convert_string_to_number(string_value(*ri).c_str())) == true)
 +							return true;
 +					}
 +					
 +					return false;
 +				}
 +				else if (lhs->rettype() == ast_type_string)
 +				{
 +					std::string l = lhs->eval_string(c);
 +					xpath_node_set rs = rhs->eval_node_set(c);
 +
 +					for (xpath_node_set::const_iterator ri = rs.begin(); ri != rs.end(); ++ri)
 +					{
 +						if (Cstring()(l, string_value(*ri)) == true)
 +							return true;
 +					}
 +					
 +					return false;
 +				}
 +				else
 +				{
 +					assert(!"Wrong types");
 +					return false;
 +				}
 +			}
 +			else if (lhs->rettype() == ast_type_node_set && rhs->rettype() != ast_type_node_set)
 +			{
 +				if (rhs->rettype() == ast_type_boolean)
 +					return Cbool()(lhs->eval_boolean(c), rhs->eval_boolean(c));
 +				else if (rhs->rettype() == ast_type_number)
 +				{
 +					xpath_node_set ls = lhs->eval_node_set(c);
 +					double r = rhs->eval_number(c);
 +
 +					for (xpath_node_set::const_iterator li = ls.begin(); li != ls.end(); ++li)
 +					{
 +						if (Cdouble()(convert_string_to_number(string_value(*li).c_str()), r) == true)
 +							return true;
 +					}
 +					
 +					return false;
 +				}
 +				else if (rhs->rettype() == ast_type_string)
 +				{
 +					xpath_node_set ls = lhs->eval_node_set(c);
 +					std::string r = rhs->eval_string(c);
 +
 +					for (xpath_node_set::const_iterator li = ls.begin(); li != ls.end(); ++li)
 +					{
 +						if (Cstring()(string_value(*li), r) == true)
 +							return true;
 +					}
 +					
 +					return false;
 +				}
 +				else
 +				{
 +					assert(!"Wrong types");
 +					return false;
 +				}
 +			}
 +			else
 +			{
 +				assert(!"Wrong types");
 +				return false;
 +			}
 +		}
 +
 +		template <class Cdouble> bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, xpath_context& c)
 +		{
 +			if (lhs->rettype() != ast_type_node_set && rhs->rettype() != ast_type_node_set)
 +				return Cdouble()(lhs->eval_number(c), rhs->eval_number(c));
 +			else if (lhs->rettype() == ast_type_node_set && rhs->rettype() == ast_type_node_set)
 +			{
 +				xpath_node_set ls = lhs->eval_node_set(c);
 +				xpath_node_set rs = rhs->eval_node_set(c);
 +				
 +				for (xpath_node_set::const_iterator li = ls.begin(); li != ls.end(); ++li)
 +				{
 +					double l = convert_string_to_number(string_value(*li).c_str());
 +					
 +					for (xpath_node_set::const_iterator ri = rs.begin(); ri != rs.end(); ++ri)
 +					{
 +						if (Cdouble()(l, convert_string_to_number(string_value(*ri).c_str())) == true)
 +							return true;
 +					}
 +				}
 +				
 +				return false;
 +			}
 +			else if (lhs->rettype() != ast_type_node_set && rhs->rettype() == ast_type_node_set)
 +			{
 +				double l = lhs->eval_number(c);
 +				xpath_node_set rs = rhs->eval_node_set(c);
 +					
 +				for (xpath_node_set::const_iterator ri = rs.begin(); ri != rs.end(); ++ri)
 +				{
 +					if (Cdouble()(l, convert_string_to_number(string_value(*ri).c_str())) == true)
 +						return true;
 +				}
 +				
 +				return false;
 +			}
 +			else if (lhs->rettype() == ast_type_node_set && rhs->rettype() != ast_type_node_set)
 +			{
 +				xpath_node_set ls = lhs->eval_node_set(c);
 +				double r = rhs->eval_number(c);
 +
 +				for (xpath_node_set::const_iterator li = ls.begin(); li != ls.end(); ++li)
 +				{
 +					if (Cdouble()(convert_string_to_number(string_value(*li).c_str()), r) == true)
 +						return true;
 +				}
 +				
 +				return false;
 +			}
 +			else
 +			{
 +				assert(!"Wrong types");
 +				return false;
 +			}
 +		}
 +		
 +		void apply_predicate(xpath_node_set& ns, size_t first, xpath_ast_node* expr, const xpath_context& context)
 +		{
 +			xpath_context c;
 +			c.root = context.root;
 +			
 +			size_t i = 0;
 +			size_t size = ns.size() - first;
 +				
 +			xpath_node_set::iterator last = ns.mut_begin() + first;
 +				
 +			// remove_if... or well, sort of
 +			for (xpath_node_set::iterator it = last; it != ns.end(); ++it, ++i)
 +			{
 +				c.n = *it;
 +				c.position = i + 1;
 +				c.size = size;
 +				
 +				if (expr->rettype() == ast_type_number)
 +				{
 +					if (expr->eval_number(c) == i + 1)
 +						*last++ = *it;
 +				}
 +				else if (expr->eval_boolean(c))
 +					*last++ = *it;
 +			}
 +			
 +			ns.truncate(last);
 +		}
 +
 +		void apply_predicates(xpath_node_set& ns, size_t first, const xpath_context& context)
 +		{
 +			if (ns.size() <= first) return;
 +			
 +			for (xpath_ast_node* pred = m_right; pred; pred = pred->m_next)
 +			{
 +				apply_predicate(ns, first, pred->m_left, context);
 +			}
 +		}
 +
 +		void step_push(xpath_node_set& ns, const xml_attribute& a, const xml_node& parent)
 +		{
 +			// There are no attribute nodes corresponding to attributes that declare namespaces
 +			// That is, "xmlns:..." or "xmlns"
 +			if (!strncmp(a.name(), "xmlns", 5) && (a.name()[5] == 0 || a.name()[5] == ':')) return;
 +			
 +			switch (m_test)
 +			{
 +			case nodetest_name:
 +				if (!strcmp(a.name(), m_contents)) ns.push_back(xpath_node(a, parent));
 +				break;
 +				
 +			case nodetest_type_node:
 +			case nodetest_all:
 +				ns.push_back(xpath_node(a, parent));
 +				break;
 +				
 +			case nodetest_all_in_namespace:
 +				if (!strncmp(a.name(), m_contents, strlen(m_contents)) && a.name()[strlen(m_contents)] == ':')
 +					ns.push_back(xpath_node(a, parent));
 +				break;
 +			
 +			default:
 +				;
 +			}
 +		}
 +		
 +		void step_push(xpath_node_set& ns, const xml_node& n)
 +		{
 +			switch (m_test)
 +			{
 +			case nodetest_name:
 +				if (n.type() == node_element && !strcmp(n.name(), m_contents)) ns.push_back(n);
 +				break;
 +				
 +			case nodetest_type_node:
 +				ns.push_back(n);
 +				break;
 +				
 +			case nodetest_type_comment:
 +				if (n.type() == node_comment)
 +					ns.push_back(n);
 +				break;
 +				
 +			case nodetest_type_text:
 +				if (n.type() == node_pcdata || n.type() == node_cdata)
 +					ns.push_back(n);
 +				break;
 +				
 +			case nodetest_type_pi:
 +				if (n.type() == node_pi)
 +					ns.push_back(n);
 +				break;
 +									
 +			case nodetest_pi:
 +				if (n.type() == node_pi && !strcmp(n.name(), m_contents))
 +					ns.push_back(n);
 +				break;
 +				
 +			case nodetest_all:
 +				if (n.type() == node_element)
 +					ns.push_back(n);
 +				break;
 +				
 +			case nodetest_all_in_namespace:
 +				if (n.type() == node_element && !strncmp(n.name(), m_contents, strlen(m_contents)) &&
 +					n.name()[strlen(m_contents)] == ':')
 +					ns.push_back(n);
 +				break;
 +			} 
 +		}
 +
 +		template <axis_t axis> void step_fill(xpath_node_set& ns, const xml_node& n)
 +		{
 +			switch (axis)
 +			{
 +			case axis_attribute:
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted : xpath_node_set::type_unsorted;
 +				
 +				for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
 +					step_push(ns, a, n);
 +				
 +				break;
 +			
 +			case axis_child:
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted : xpath_node_set::type_unsorted;
 +
 +				for (xml_node c = n.first_child(); c; c = c.next_sibling())
 +					step_push(ns, c);
 +					
 +				break;
 +			
 +			case axis_descendant:
 +			case axis_descendant_or_self:
 +			{
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted : xpath_node_set::type_unsorted;
 +				
 +				if (axis == axis_descendant_or_self)
 +					step_push(ns, n);
 +					
 +				xml_node cur = n.first_child();
 +				
 +				if (cur)
 +				{
 +					do 
 +					{
 +						step_push(ns, cur);
 +						
 +						if (cur.first_child())
 +							cur = cur.first_child();
 +						else if (cur.next_sibling())
 +							cur = cur.next_sibling();
 +						else
 +						{
 +							// Borland C++ workaround
 +							while (!cur.next_sibling() && cur != n && (bool)cur.parent())
 +								cur = cur.parent();
 +						
 +							if (cur != n)
 +								cur = cur.next_sibling();
 +						}
 +					}
 +					while (cur && cur != n);
 +				}
 +				
 +				break;
 +			}
 +			
 +			case axis_following_sibling:
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted : xpath_node_set::type_unsorted;
 +
 +				for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
 +					step_push(ns, c);
 +				
 +				break;
 +			
 +			case axis_preceding_sibling:
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_unsorted;
 +
 +				for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
 +					step_push(ns, c);
 +				
 +				break;
 +			
 +			case axis_following:
 +			{
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted : xpath_node_set::type_unsorted;
 +				
 +				xml_node cur = n;
 +				
 +				for (;;)
 +				{
 +					if (cur.first_child())
 +						cur = cur.first_child();
 +					else if (cur.next_sibling())
 +						cur = cur.next_sibling();
 +					else
 +					{
 +						while (cur && !cur.next_sibling()) cur = cur.parent();
 +						cur = cur.next_sibling();
 +						
 +						if (!cur) break;
 +					}
 +					
 +					step_push(ns, cur);
 +				}
 +
 +				break;
 +			}
 +
 +			case axis_preceding:
 +			{
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_unsorted;
 +
 +				xml_node cur = n;
 +				
 +				while (cur && !cur.previous_sibling()) cur = cur.parent();
 +				cur = cur.previous_sibling();
 +				
 +				if (cur)
 +				{
 +					for (;;)
 +					{
 +						if (cur.last_child())
 +							cur = cur.last_child();
 +						else
 +						{
 +							// leaf node
 +							step_push(ns, cur);
 +							
 +							if (cur.previous_sibling())
 +								cur = cur.previous_sibling();
 +							else
 +							{
 +								do 
 +								{
 +									cur = cur.parent();
 +									if (!cur) break;
 +								
 +									step_push(ns, cur);
 +								}
 +								while (!cur.previous_sibling());
 +													
 +								cur = cur.previous_sibling();
 +								
 +								if (!cur) break;
 +							}
 +						}
 +					}
 +				}
 +				
 +				break;
 +			}
 +			
 +			case axis_ancestor:
 +			case axis_ancestor_or_self:
 +			{
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_unsorted;
 +
 +				if (axis == axis_ancestor_or_self)
 +					step_push(ns, n);
 +
 +				xml_node cur = n.parent();
 +				
 +				while (cur)
 +				{
 +					step_push(ns, cur);
 +					
 +					cur = cur.parent();
 +				}
 +				
 +				break;
 +			}
 +				
 +			default:
 +				assert(!"Unimplemented axis");
 +			}
 +		}
 +		
 +		template <axis_t axis> void step_fill(xpath_node_set& ns, const xml_attribute& a, const xml_node& p)
 +		{
 +			switch (axis)
 +			{
 +			case axis_ancestor:
 +			case axis_ancestor_or_self:
 +			{
 +				ns.m_type = ns.empty() ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_unsorted;
 +
 +				if (axis == axis_ancestor_or_self)
 +					step_push(ns, a, p);
 +
 +				xml_node cur = p;
 +				
 +				while (cur)
 +				{
 +					step_push(ns, cur);
 +					
 +					cur = cur.parent();
 +				}
 +				
 +				break;
 +			}
 +			
 +			default:
 +				assert(!"Unimplemented axis");
 +			}
 +		}
 +		
 +		template <axis_t axis> void step_do(xpath_node_set& ns, xpath_context& c)
 +		{
 +			switch (axis)
 +			{
 +			case axis_parent:
 +				if (m_left)
 +				{
 +					xpath_node_set s = m_left->eval_node_set(c);
 +					
 +					for (xpath_node_set::const_iterator it = s.begin(); it != s.end(); ++it)
 +					{
 +						xml_node p = it->parent();
 +						if (p)
 +						{
 +							size_t s = ns.size();
 +							
 +							step_push(ns, p);
 +							
 +							apply_predicates(ns, s, c);
 +						}
 +					}
 +				}
 +				else
 +				{
 +					xml_node p = c.n.parent();
 +					if (p)
 +					{
 +						step_push(ns, p);
 +						
 +						apply_predicates(ns, 0, c);
 +					}
 +				}
 +
 +				break;
 +				
 +			case axis_self:
 +				if (m_left)
 +				{
 +					xpath_node_set s = m_left->eval_node_set(c);
 +					
 +					for (xpath_node_set::const_iterator it = s.begin(); it != s.end(); ++it)
 +					{
 +						size_t s = ns.size();
 +						
 +						if (it->attribute()) step_push(ns, it->attribute(), it->parent());
 +						else step_push(ns, it->node());
 +						
 +						apply_predicates(ns, s, c);
 +					}
 +				}
 +				else
 +				{
 +					if (c.n.node()) step_push(ns, c.n.node());
 +					else step_push(ns, c.n.attribute(), c.n.parent());
 +					
 +					apply_predicates(ns, 0, c);
 +				}
 +
 +				break;
 +				
 +			case axis_namespace:
 +				break;
 +				
 +			case axis_ancestor:
 +			case axis_ancestor_or_self:
 +				if (m_left)
 +				{
 +					xpath_node_set s = m_left->eval_node_set(c);
 +							
 +					for (xpath_node_set::const_iterator it = s.begin(); it != s.end(); ++it)
 +					{
 +						size_t s = ns.size();
 +						
 +						if (it->node())
 +							step_fill<axis>(ns, it->node());
 +						else
 +							step_fill<axis>(ns, it->attribute(), it->parent());
 +							
 +						apply_predicates(ns, s, c);
 +					}
 +				}
 +				else
 +				{
 +					if (c.n.node()) step_fill<axis>(ns, c.n.node());
 +					else step_fill<axis>(ns, c.n.attribute(), c.n.parent());
 +					
 +					apply_predicates(ns, 0, c);
 +				}
 +				
 +				break;
 +		
 +			case axis_following:
 +			case axis_following_sibling:
 +			case axis_preceding:
 +			case axis_preceding_sibling:
 +			case axis_attribute:
 +			case axis_child:
 +			case axis_descendant:
 +			case axis_descendant_or_self:
 +				if (m_left)
 +				{
 +					xpath_node_set s = m_left->eval_node_set(c);
 +					
 +					for (xpath_node_set::const_iterator it = s.begin(); it != s.end(); ++it)
 +					{
 +						size_t s = ns.size();
 +						
 +						if (it->node())
 +							step_fill<axis>(ns, it->node());
 +						
 +						apply_predicates(ns, s, c);
 +					}
 +				}
 +				else if (c.n.node())
 +				{
 +					step_fill<axis>(ns, c.n.node());
 +					
 +					apply_predicates(ns, 0, c);
 +				}
 +				
 +				break;
 +			
 +			default:
 +				assert(!"Unimplemented axis");
 +			}
 +		}
 +		
 +		void set_contents(const char* value, xpath_allocator& a)
 +		{
 +			if (value)
 +			{
 +				char* c = static_cast<char*>(a.alloc(strlen(value) + 1));
 +				strcpy(c, value);
 +				m_contents = c;
 +			}
 +			else m_contents = 0;
 +		}
 +	public:
 +		xpath_ast_node(ast_type_t type, const char* contents, xpath_allocator& a): m_type(type), m_rettype(ast_type_none), m_contents(0)
 +		{
 +			set_contents(contents, a);
 +		}
 +		
 +		xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, axis_t axis): m_type(type),
 +			m_rettype(ast_type_none), m_left(left), m_right(right), m_third(0), m_next(0), m_contents(0),
 +			m_axis(axis)
 +		{
 +		}
 +
 +		xpath_ast_node(ast_type_t type, xpath_ast_node* left = 0, xpath_ast_node* right = 0, xpath_ast_node* third = 0): m_type(type),
 +			m_rettype(ast_type_none), m_left(left), m_right(right), m_third(third), m_next(0), m_contents(0)
 +		{
 +		}
 +
 +		xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char* contents, xpath_allocator& a):
 +			m_type(type), m_rettype(ast_type_none), m_left(left), m_right(0), m_third(0), m_next(0),
 +			m_contents(0), m_axis(axis), m_test(test)
 +		{
 +			set_contents(contents, a);
 +		}
 +
 +		void set_next(xpath_ast_node* value)
 +		{
 +			m_next = value;
 +		}
 +
 +		void set_right(xpath_ast_node* value)
 +		{
 +			m_right = value;
 +		}
 +
 +		bool eval_boolean(xpath_context& c)
 +		{
 +			switch (m_type)
 +			{
 +			case ast_op_or:
 +				if (m_left->eval_boolean(c)) return true;
 +				else return m_right->eval_boolean(c);
 +				
 +			case ast_op_and:
 +				if (!m_left->eval_boolean(c)) return false;
 +				else return m_right->eval_boolean(c);
 +				
 +			case ast_op_equal:
 +				return compare_eq<equal_to<bool>, equal_to<double>, equal_to<std::string> >(m_left, m_right, c);
 +
 +			case ast_op_not_equal:
 +				return compare_eq<not_equal_to<bool>, not_equal_to<double>, not_equal_to<std::string> >(m_left, m_right, c);
 +	
 +			case ast_op_less:
 +				return compare_rel<less<double> >(m_left, m_right, c);
 +			
 +			case ast_op_greater:
 +				return compare_rel<greater<double> >(m_left, m_right, c);
 +
 +			case ast_op_less_or_equal:
 +				return compare_rel<less_equal<double> >(m_left, m_right, c);
 +			
 +			case ast_op_greater_or_equal:
 +				return compare_rel<greater_equal<double> >(m_left, m_right, c);
 +
 +			case ast_func_starts_with:
 +				return starts_with(m_left->eval_string(c), m_right->eval_string(c).c_str());
 +
 +			case ast_func_contains:
 +				return m_left->eval_string(c).find(m_right->eval_string(c)) != std::string::npos;
 +
 +			case ast_func_boolean:
 +				return m_left->eval_boolean(c);
 +				
 +			case ast_func_not:
 +				return !m_left->eval_boolean(c);
 +				
 +			case ast_func_true:
 +				return true;
 +				
 +			case ast_func_false:
 +				return false;
 +
 +			case ast_func_lang:
 +			{
 +				if (c.n.attribute()) return false;
 +				
 +				std::string lang = m_left->eval_string(c);
 +				
 +				xml_node n = c.n.node();
 +				
 +				while (n.type() != node_document)
 +				{
 +					xml_attribute a = n.attribute("xml:lang");
 +					
 +					if (a)
 +					{
 +						const char* value = a.value();
 +						
 +						// strnicmp / strncasecmp is not portable
 +						for (std::string::iterator it = lang.begin(); it != lang.end(); ++it)
 +						{
 +							if (tolower(*it) != tolower(*value)) return false;
 +							++value;
 +						}
 +						
 +						return *value == 0 || *value == '-';
 +					}
 +				}
 +				
 +				return false;
 +			}
 +
 +			default:
 +			{
 +				switch (m_rettype)
 +				{
 +				case ast_type_number:
 +					return convert_number_to_boolean(eval_number(c));
 +					
 +				case ast_type_string:
 +					return !eval_string(c).empty();
 +					
 +				case ast_type_node_set:				
 +					return !eval_node_set(c).empty();
 +					
 +				default:
 +					assert(!"Wrong expression for ret type boolean");
 +					return false;
 +				}
 +			}
 +			}
 +		}
 +
 +		double eval_number(xpath_context& c)
 +		{
 +			switch (m_type)
 +			{
 +			case ast_op_add:
 +				return m_left->eval_number(c) + m_right->eval_number(c);
 +				
 +			case ast_op_subtract:
 +				return m_left->eval_number(c) - m_right->eval_number(c);
 +
 +			case ast_op_multiply:
 +				return m_left->eval_number(c) * m_right->eval_number(c);
 +
 +			case ast_op_divide:
 +				return m_left->eval_number(c) / m_right->eval_number(c);
 +
 +			case ast_op_mod:
 +				return fmod(m_left->eval_number(c), m_right->eval_number(c));
 +
 +			case ast_op_negate:
 +				return -m_left->eval_number(c);
 +
 +			case ast_number_constant:
 +				return convert_string_to_number(m_contents);
 +
 +			case ast_func_last:
 +				return (double)c.size;
 +			
 +			case ast_func_position:
 +				return (double)c.position;
 +
 +			case ast_func_count:
 +				return (double)m_left->eval_node_set(c).size();
 +			
 +			case ast_func_string_length_0:
 +				return (double)string_value(c.n).size();
 +			
 +			case ast_func_string_length_1:
 +				return (double)m_left->eval_string(c).size();
 +			
 +			case ast_func_number_0:
 +				return convert_string_to_number(string_value(c.n).c_str());
 +			
 +			case ast_func_number_1:
 +				return m_left->eval_number(c);
 +
 +			case ast_func_sum:
 +			{
 +				double r = 0;
 +				
 +				xpath_node_set ns = m_left->eval_node_set(c);
 +				
 +				for (xpath_node_set::const_iterator it = ns.begin(); it != ns.end(); ++it)
 +					r += convert_string_to_number(string_value(*it).c_str());
 +			
 +				return r;
 +			}
 +
 +			case ast_func_floor:
 +			{
 +				double r = m_left->eval_number(c);
 +				
 +				return r == r ? floor(r) : r;
 +			}
 +
 +			case ast_func_ceiling:
 +			{
 +				double r = m_left->eval_number(c);
 +				
 +				return r == r ? ceil(r) : r;
 +			}
 +
 +			case ast_func_round:
 +				// correct except for negative zero (it returns positive zero instead of negative)
 +				return ieee754_round(m_left->eval_number(c));
 +			
 +			default:
 +			{
 +				switch (m_rettype)
 +				{
 +				case ast_type_boolean:
 +					return eval_boolean(c) ? 1 : 0;
 +					
 +				case ast_type_string:
 +					return convert_string_to_number(eval_string(c).c_str());
 +					
 +				case ast_type_node_set:
 +					return convert_string_to_number(eval_string(c).c_str());
 +					
 +				default:
 +					assert(!"Wrong expression for ret type number");
 +					return 0;
 +				}
 +				
 +			}
 +			}
 +		}
 +		
 +		std::string eval_string(xpath_context& c)
 +		{
 +			switch (m_type)
 +			{
 +			case ast_string_constant:
 +				return m_contents;
 +			
 +			case ast_func_local_name_0:
 +			{
 +				xpath_node na = c.n;
 +				
 +				if (na.attribute()) return local_name(na.attribute().name());
 +				else return local_name(na.node().name());
 +			}
 +
 +			case ast_func_local_name_1:
 +			{
 +				xpath_node_set ns = m_left->eval_node_set(c);
 +				if (ns.empty()) return "";
 +				
 +				xpath_node na = ns.first();
 +				
 +				if (na.attribute()) return local_name(na.attribute().name());
 +				else return local_name(na.node().name());
 +			}
 +
 +			case ast_func_name_0:
 +			{
 +				xpath_node na = c.n;
 +				
 +				if (na.attribute()) return na.attribute().name();
 +				else return na.node().name();
 +			}
 +
 +			case ast_func_name_1:
 +			{
 +				xpath_node_set ns = m_left->eval_node_set(c);
 +				if (ns.empty()) return "";
 +				
 +				xpath_node na = ns.first();
 +				
 +				if (na.attribute()) return na.attribute().name();
 +				else return na.node().name();
 +			}
 +
 +			case ast_func_namespace_uri_0:
 +			{
 +				xpath_node na = c.n;
 +				
 +				if (na.attribute()) return namespace_uri(na.attribute(), na.parent());
 +				else return namespace_uri(na.node());
 +			}
 +
 +			case ast_func_namespace_uri_1:
 +			{
 +				xpath_node_set ns = m_left->eval_node_set(c);
 +				if (ns.empty()) return "";
 +				
 +				xpath_node na = ns.first();
 +				
 +				if (na.attribute()) return namespace_uri(na.attribute(), na.parent());
 +				else return namespace_uri(na.node());
 +			}
 +
 +			case ast_func_string_0:
 +				return string_value(c.n);
 +
 +			case ast_func_string_1:
 +				return m_left->eval_string(c);
 +
 +			case ast_func_concat:
 +			{
 +				std::string r = m_left->eval_string(c);
 +				
 +				for (xpath_ast_node* n = m_right; n; n = n->m_next)
 +					r += n->eval_string(c);
 +			
 +				return r;
 +			}
 +
 +			case ast_func_substring_before:
 +			{
 +				std::string s = m_left->eval_string(c);
 +				std::string::size_type pos = s.find(m_right->eval_string(c));
 +				
 +				if (pos == std::string::npos) return "";
 +				else return std::string(s.begin(), s.begin() + pos);
 +			}
 +			
 +			case ast_func_substring_after:
 +			{
 +				std::string s = m_left->eval_string(c);
 +				std::string p = m_right->eval_string(c);
 +				
 +				std::string::size_type pos = s.find(p);
 +				
 +				if (pos == std::string::npos) return "";
 +				else return std::string(s.begin() + pos + p.length(), s.end());
 +			}
 +
 +			case ast_func_substring_2:
 +			{
 +				std::string s = m_left->eval_string(c);
 +				double first = ieee754_round(m_right->eval_number(c));
 +				
 +				if (first != first) return ""; // NaN
 +				else if (first >= s.length() + 1) return "";
 +				
 +				size_t pos = first < 1 ? 1 : (size_t)first;
 +				
 +				return s.substr(pos - 1);
 +			}
 +			
 +			case ast_func_substring_3:
 +			{
 +				std::string s = m_left->eval_string(c);
 +				double first = ieee754_round(m_right->eval_number(c));
 +				double last = first + ieee754_round(m_third->eval_number(c));
 +				
 +				if (first != first || last != last) return "";
 +				else if (first >= s.length() + 1) return "";
 +				else if (first >= last) return "";
 +				
 +				size_t pos = first < 1 ? 1 : (size_t)first;
 +				size_t end = last >= s.length() + 1 ? s.length() + 1 : (size_t)last;
 +				
 +				return s.substr(pos - 1, std::min(end - pos, s.length() - pos + 1));
 +			}
 +
 +			case ast_func_normalize_space_0:
 +			case ast_func_normalize_space_1:
 +			{
 +				std::string s = m_type == ast_func_normalize_space_0 ? string_value(c.n) : m_left->eval_string(c);
 +				
 +				std::string r;
 +				r.reserve(s.size());
 +				
 +				for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
 +				{
 +					if (is_chartype(*it, ct_space))
 +					{
 +						if (!r.empty() && r[r.size() - 1] != ' ')
 +							r += ' ';
 +					}
 +					else r += *it;
 +				}
 +				
 +				std::string::size_type pos = r.find_last_not_of(' ');
 +				if (pos == std::string::npos) r = "";
 +				else r.erase(r.begin() + pos + 1, r.end());
 +			
 +				return r;
 +			}
 +
 +			case ast_func_translate:
 +			{
 +				std::string s = m_left->eval_string(c);
 +				std::string from = m_right->eval_string(c);
 +				std::string to = m_third->eval_string(c);
 +				
 +				for (std::string::iterator it = s.begin(); it != s.end(); )
 +				{
 +					std::string::size_type pos = from.find(*it);
 +					
 +					if (pos != std::string::npos && pos >= to.length())
 +						it = s.erase(it);
 +					else if (pos != std::string::npos)
 +						*it = to[pos];
 +				}
 +				
 +				return s;
 +			}
 +
 +			default:
 +			{
 +				switch (m_rettype)
 +				{
 +				case ast_type_boolean:
 +					return eval_boolean(c) ? "true" : "false";
 +					
 +				case ast_type_number:
 +					return convert_number_to_string(eval_number(c));
 +					
 +				case ast_type_node_set:
 +				{
 +					xpath_node_set ns = eval_node_set(c);
 +					return ns.empty() ? std::string("") : string_value(ns.first());
 +				}
 +				
 +				default:
 +					assert(!"Wrong expression for ret type string");
 +					return "";
 +				}
 +			}
 +			}
 +		}
 +
 +		xpath_node_set eval_node_set(xpath_context& c)
 +		{
 +			switch (m_type)
 +			{
 +			case ast_op_union:
 +			{
 +				xpath_node_set ls = m_left->eval_node_set(c);
 +				xpath_node_set rs = m_right->eval_node_set(c);
 +				
 +				ls.append(rs.begin(), rs.end());
 +				
 +				ls.remove_duplicates();
 +				
 +				return ls;
 +			}
 +
 +			case ast_filter:
 +			{
 +				xpath_node_set set = m_left->eval_node_set(c);
 +				set.sort();
 +				
 +				xpath_context oc = c;
 +			
 +				size_t i = 0;
 +				
 +				xpath_node_set::iterator last = set.mut_begin();
 +				
 +				// remove_if... or well, sort of
 +				for (xpath_node_set::const_iterator it = set.begin(); it != set.end(); ++it, ++i)
 +				{
 +					c.n = *it;
 +					c.position = i + 1;
 +					c.size = set.size();
 +				
 +					if (m_right->rettype() == ast_type_number)
 +					{
 +						if (m_right->eval_number(c) == i + 1)
 +							*last++ = *it;
 +					}
 +					else if (m_right->eval_boolean(c))
 +						*last++ = *it;
 +				}
 +			
 +				c = oc;
 +				
 +				set.truncate(last);
 +			
 +				return set;
 +			}
 +			
 +			case ast_filter_posinv:
 +			{
 +				xpath_node_set set = m_left->eval_node_set(c);
 +				
 +				xpath_context oc = c;
 +			
 +				size_t i = 0;
 +				
 +				xpath_node_set::iterator last = set.mut_begin();
 +				
 +				// remove_if... or well, sort of
 +				for (xpath_node_set::const_iterator it = set.begin(); it != set.end(); ++it, ++i)
 +				{
 +					c.n = *it;
 +					c.position = i + 1;
 +					c.size = set.size();
 +				
 +					if (m_right->eval_boolean(c))
 +						*last++ = *it;
 +				}
 +			
 +				c = oc;
 +				
 +				set.truncate(last);
 +			
 +				return set;
 +			}
 +
 +			case ast_func_id:
 +				return xpath_node_set();
 +			
 +			case ast_step:
 +			{
 +				xpath_node_set ns;
 +				
 +				switch (m_axis)
 +				{
 +				case axis_ancestor:
 +					step_do<axis_ancestor>(ns, c);
 +					break;
 +					
 +				case axis_ancestor_or_self:
 +					step_do<axis_ancestor_or_self>(ns, c);
 +					break;
 +
 +				case axis_attribute:
 +					step_do<axis_attribute>(ns, c);
 +					break;
 +
 +				case axis_child:
 +					step_do<axis_child>(ns, c);
 +					break;
 +				
 +				case axis_descendant:
 +					step_do<axis_descendant>(ns, c);
 +					break;
 +
 +				case axis_descendant_or_self:
 +					step_do<axis_descendant_or_self>(ns, c);
 +					break;
 +
 +				case axis_following:
 +					step_do<axis_following>(ns, c);
 +					break;
 +				
 +				case axis_following_sibling:
 +					step_do<axis_following_sibling>(ns, c);
 +					break;
 +				
 +				case axis_namespace:
 +					step_do<axis_namespace>(ns, c);
 +					break;
 +				
 +				case axis_parent:
 +					step_do<axis_parent>(ns, c);
 +					break;
 +				
 +				case axis_preceding:
 +					step_do<axis_preceding>(ns, c);
 +					break;
 +
 +				case axis_preceding_sibling:
 +					step_do<axis_preceding_sibling>(ns, c);
 +					break;
 +				
 +				case axis_self:
 +					step_do<axis_self>(ns, c);
 +					break;
 +
 +				default:
 +					assert(!"Axis not implemented");
 +					return xpath_node_set();
 +				}
 +				
 +				ns.remove_duplicates();
 +				
 +				return ns;
 +			}
 +
 +			case ast_step_root:
 +			{
 +				xpath_node_set ns;
 +			
 +				ns.push_back(c.root);
 +				
 +				apply_predicates(ns, 0, c);
 +			
 +				return ns;
 +			}
 +
 +			default:
 +				assert(!"Wrong expression for ret type node set");
 +				return xpath_node_set();
 +			}
 +		}
 +		
 +		bool contains(ast_type_t type)
 +		{
 +			if (m_type == type) return true;
 +			
 +			switch (m_type)
 +			{
 +			case ast_op_or:
 +			case ast_op_and:
 +			case ast_op_equal:
 +			case ast_op_not_equal:
 +			case ast_op_less:
 +			case ast_op_greater:
 +			case ast_op_less_or_equal:
 +			case ast_op_greater_or_equal:
 +			case ast_op_add:
 +			case ast_op_subtract:
 +			case ast_op_multiply:
 +			case ast_op_divide:
 +			case ast_op_mod:
 +			case ast_op_negate:
 +				return m_left->contains(type) || m_right->contains(type);
 +			
 +			case ast_op_union:
 +			case ast_predicate:
 +			case ast_filter:
 +			case ast_filter_posinv:
 +				return false;
 +			
 +			case ast_variable:
 +				throw xpath_exception("Semantics error: variables are not supported");
 +				
 +			case ast_string_constant:
 +			case ast_number_constant:
 +			case ast_func_last:
 +			case ast_func_position:
 +				return false;
 +		
 +			case ast_func_count:
 +			case ast_func_id:
 +			case ast_func_local_name_0:
 +			case ast_func_local_name_1:
 +			case ast_func_namespace_uri_0:
 +			case ast_func_namespace_uri_1:
 +			case ast_func_name_0:
 +			case ast_func_name_1:
 +			case ast_func_string_0:
 +			case ast_func_string_1:
 +				if (m_left) return m_left->contains(type);
 +				return false;
 +				
 +			case ast_func_concat:
 +				if (m_left->contains(type)) return true;
 +				
 +				for (xpath_ast_node* n = m_right; n; n = n->m_next)
 +					if (n->contains(type)) return true;
 +					
 +				return false;
 +		
 +			case ast_func_starts_with:
 +			case ast_func_contains:
 +			case ast_func_substring_before:
 +			case ast_func_substring_after:
 +			case ast_func_substring_2:
 +			case ast_func_substring_3:
 +			case ast_func_string_length_0:
 +			case ast_func_string_length_1:
 +			case ast_func_normalize_space_0:
 +			case ast_func_normalize_space_1:
 +			case ast_func_translate:
 +			case ast_func_boolean:
 +			case ast_func_not:
 +			case ast_func_true:
 +			case ast_func_false:
 +			case ast_func_lang:
 +			case ast_func_number_0:
 +			case ast_func_number_1:
 +			case ast_func_sum:
 +			case ast_func_floor:
 +			case ast_func_ceiling:
 +			case ast_func_round:
 +				if (m_left && m_left->contains(type)) return true;
 +				if (m_right && m_right->contains(type)) return true;
 +				if (m_third && m_third->contains(type)) return true;
 +
 +				return false;
 +		
 +			case ast_step:
 +			case ast_step_root:
 +				return false;
 +			
 +			default:
 +				throw xpath_exception("Unknown semantics error");
 +			}
 +		}
 +
 +		void check_semantics()
 +		{
 +			switch (m_type)
 +			{
 +			case ast_op_or:
 +			case ast_op_and:
 +			case ast_op_equal:
 +			case ast_op_not_equal:
 +			case ast_op_less:
 +			case ast_op_greater:
 +			case ast_op_less_or_equal:
 +			case ast_op_greater_or_equal:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				m_rettype = ast_type_boolean;
 +				break;
 +				
 +			case ast_op_add:
 +			case ast_op_subtract:
 +			case ast_op_multiply:
 +			case ast_op_divide:
 +			case ast_op_mod:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				m_rettype = ast_type_number;
 +				break;
 +				
 +			case ast_op_negate:
 +				m_left->check_semantics();
 +				m_rettype = ast_type_number;
 +				break;
 +			
 +			case ast_op_union:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				if (m_left->rettype() != ast_type_node_set || m_right->rettype() != ast_type_node_set)
 +					throw xpath_exception("Semantics error: union operator has to be applied to node sets");
 +				m_rettype = ast_type_node_set;
 +				break;
 +			
 +			case ast_filter:
 +			case ast_filter_posinv:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				if (m_left->rettype() != ast_type_node_set)
 +					throw xpath_exception("Semantics error: predicate has to be applied to node set");
 +				m_rettype = ast_type_node_set;
 +				
 +				if (!m_right->contains(ast_func_position) && m_right->rettype() != ast_type_number)
 +					m_type = ast_filter_posinv;
 +				break;
 +			
 +			case ast_predicate:
 +				m_left->check_semantics();
 +				m_rettype = ast_type_node_set;
 +				break;
 +
 +			case ast_variable:
 +				throw xpath_exception("Semantics error: variable are not supported");
 +				
 +			case ast_string_constant:
 +				m_rettype = ast_type_string;
 +				break;
 +				
 +			case ast_number_constant:
 +				m_rettype = ast_type_number;
 +				break;
 +				
 +			case ast_func_last:
 +			case ast_func_position:
 +				m_rettype = ast_type_number;
 +				break;
 +		
 +			case ast_func_count:
 +				m_left->check_semantics();
 +				if (m_left->rettype() != ast_type_node_set)
 +					throw xpath_exception("Semantics error: count() has to be applied to node set");
 +				m_rettype = ast_type_number;
 +				break;
 +				
 +			case ast_func_id:
 +				m_left->check_semantics();
 +				m_rettype = ast_type_node_set;
 +				break;
 +				
 +			case ast_func_local_name_0:
 +			case ast_func_local_name_1:
 +			case ast_func_namespace_uri_0:
 +			case ast_func_namespace_uri_1:
 +			case ast_func_name_0:
 +			case ast_func_name_1:
 +				if (m_left)
 +				{
 +					m_left->check_semantics();
 +					if (m_left->rettype() != ast_type_node_set)
 +						throw xpath_exception("Semantics error: function has to be applied to node set");
 +				}
 +				m_rettype = ast_type_string;
 +				break;
 +				
 +			case ast_func_string_0:
 +			case ast_func_string_1:
 +				if (m_left) m_left->check_semantics();
 +				m_rettype = ast_type_string;
 +				break;
 +				
 +			case ast_func_concat:
 +				m_left->check_semantics();
 +				
 +				for (xpath_ast_node* n = m_right; n; n = n->m_next)
 +					n->check_semantics();
 +					
 +				m_rettype = ast_type_string;
 +				break;
 +		
 +			case ast_func_starts_with:
 +			case ast_func_contains:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				m_rettype = ast_type_boolean;
 +				break;
 +				
 +			case ast_func_substring_before:
 +			case ast_func_substring_after:
 +			case ast_func_substring_2:
 +			case ast_func_substring_3:
 +				m_left->check_semantics();
 +				m_right->check_semantics();
 +				if (m_third) m_third->check_semantics();
 +				m_rettype = ast_type_string;
 +				break;
 +		
 +			case ast_func_string_length_0:
 +			case ast_func_string_length_1:
 +				if (m_left) m_left->check_semantics();
 +				m_rettype = ast_type_number;
 +				break;
 +				
 +			case ast_func_normalize_space_0:
 +			case ast_func_normalize_space_1:
 +			case ast_func_translate:
 +				if (m_left) m_left->check_semantics();
 +				if (m_right) m_right->check_semantics();
 +				if (m_third) m_third->check_semantics();
 +				m_rettype = ast_type_string;
 +				break;
 +		
 +			case ast_func_boolean:
 +			case ast_func_not:
 +			case ast_func_true:
 +			case ast_func_false:
 +			case ast_func_lang:
 +				if (m_left) m_left->check_semantics();
 +				m_rettype = ast_type_boolean;
 +				break;
 +			
 +			case ast_func_number_0:
 +			case ast_func_number_1:
 +				if (m_left) m_left->check_semantics();
 +				m_rettype = ast_type_number;
 +				break;
 +		
 +			case ast_func_sum:
 +				m_left->check_semantics();
 +				if (m_left->rettype() != ast_type_node_set)
 +					throw xpath_exception("Semantics error: sum() has to be applied to node set");
 +				m_rettype = ast_type_number;
 +				break;
 +				
 +			case ast_func_floor:
 +			case ast_func_ceiling:
 +			case ast_func_round:
 +				if (m_left) m_left->check_semantics();
 +				m_rettype = ast_type_number;
 +				break;
 +		
 +			case ast_step:
 +				if (m_left)
 +				{
 +					m_left->check_semantics();
 +					if (m_left->rettype() != ast_type_node_set)
 +						throw xpath_exception("Semantics error: step has to be applied to node set");
 +				}
 +				
 +				for (xpath_ast_node* n = m_right; n; n = n->m_next)
 +					n->check_semantics();
 +				
 +				m_rettype = ast_type_node_set;
 +				break;
 +				
 +			case ast_step_root:
 +				m_rettype = ast_type_node_set;
 +				break;
 +			
 +			default:
 +				throw xpath_exception("Unknown semantics error");
 +			}
 +		}
 +		
 +		ast_rettype_t rettype() const
 +		{
 +			return m_rettype;
 +		}
 +		
 +		void* operator new(size_t size, xpath_allocator& a)
 +		{
 +			return a.alloc(size);
 +		}
 +		
 +		void operator delete(void*, xpath_allocator&)
 +		{
 +		}
 +	};
 +
 +	class xpath_parser
 +	{
 +	private:
 +	    xpath_allocator& m_alloc;
 +	    xpath_lexer m_lexer;
 +
 +		xpath_parser(const xpath_parser&);
 +		xpath_parser& operator=(const xpath_parser&);
 +	    
 +	    // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
 +	    xpath_ast_node* parse_primary_expression()
 +	    {
 +	    	switch (m_lexer.current())
 +	    	{
 +	    	case lex_var_ref:
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		if (m_lexer.current() != lex_string)
 +	    			throw xpath_exception("incorrect variable reference");
 +
 +				xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_variable, m_lexer.contents(), m_alloc);
 +				m_lexer.next();
 +
 +				return n;
 +			}
 +
 +			case lex_open_brace:
 +			{
 +				m_lexer.next();
 +
 +				xpath_ast_node* n = parse_expression();
 +
 +				if (m_lexer.current() != lex_close_brace)
 +					throw xpath_exception("unmatched braces");
 +
 +				m_lexer.next();
 +
 +				return n;
 +			}
 +
 +			case lex_quoted_string:
 +			{
 +				xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_string_constant, m_lexer.contents(), m_alloc);
 +				m_lexer.next();
 +
 +				return n;
 +			}
 +
 +			case lex_number:
 +			{
 +				xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_number_constant, m_lexer.contents(), m_alloc);
 +				m_lexer.next();
 +
 +				return n;
 +			}
 +
 +			case lex_string:
 +			{
 +				xpath_ast_node* args[4];
 +				size_t argc = 0;
 +				
 +				std::string function = m_lexer.contents();
 +				m_lexer.next();
 +				
 +				bool func_concat = (function == "concat");
 +				xpath_ast_node* last_concat = 0;
 +				
 +				if (m_lexer.current() != lex_open_brace)
 +					throw xpath_exception("Unrecognized function call");
 +				m_lexer.next();
 +
 +				if (m_lexer.current() != lex_close_brace)
 +					args[argc++] = parse_expression();
 +
 +				while (m_lexer.current() != lex_close_brace)
 +				{
 +					if (m_lexer.current() != lex_comma)
 +						throw xpath_exception("no comma between function arguments");
 +					m_lexer.next();
 +					
 +					xpath_ast_node* n = parse_expression();
 +					
 +					if (func_concat)
 +					{
 +						if (argc < 2) args[argc++] = last_concat = n;
 +						else
 +						{
 +							last_concat->set_next(n);
 +							last_concat = n;
 +						}
 +					}
 +					else if (argc >= 4)
 +						throw xpath_exception("Too many function arguments");
 +					else
 +						args[argc++] = n;
 +				}
 +				
 +				m_lexer.next();
 +				
 +				ast_type_t type = ast_none;
 +				
 +				switch (function[0])
 +				{
 +				case 'b':
 +				{
 +					if (function == "boolean" && argc == 1)
 +						type = ast_func_boolean;
 +						
 +					break;
 +				}
 +				
 +				case 'c':
 +				{
 +					if (function == "count" && argc == 1)
 +						type = ast_func_count;
 +					else if (function == "contains" && argc == 2)
 +						type = ast_func_contains;
 +					else if (function == "concat" && argc == 2)
 +					{
 +						// set_next was done earlier
 +						return new (m_alloc) xpath_ast_node(ast_func_concat, args[0], args[1]);
 +					}
 +					else if (function == "ceiling" && argc == 1)
 +						type = ast_func_ceiling;
 +						
 +					break;
 +				}
 +				
 +				case 'f':
 +				{
 +					if (function == "false" && argc == 0)
 +						type = ast_func_false;
 +					else if (function == "floor" && argc == 1)
 +						type = ast_func_floor;
 +						
 +					break;
 +				}
 +				
 +				case 'i':
 +				{
 +					if (function == "id" && argc == 1)
 +						type = ast_func_id;
 +						
 +					break;
 +				}
 +				
 +				case 'l':
 +				{
 +					if (function == "last" && argc == 0)
 +						type = ast_func_last;
 +					else if (function == "lang" && argc == 1)
 +						type = ast_func_lang;
 +					else if (function == "local-name" && argc <= 1)
 +						type = argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1;
 +				
 +					break;
 +				}
 +				
 +				case 'n':
 +				{
 +					if (function == "name" && argc <= 1)
 +						type = argc == 0 ? ast_func_name_0 : ast_func_name_1;
 +					else if (function == "namespace-uri" && argc <= 1)
 +						type = argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1;
 +					else if (function == "normalize-space" && argc <= 1)
 +						type = argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1;
 +					else if (function == "not" && argc == 1)
 +						type = ast_func_not;
 +					else if (function == "number" && argc <= 1)
 +						type = argc == 0 ? ast_func_number_0 : ast_func_number_1;
 +				
 +					break;
 +				}
 +				
 +				case 'p':
 +				{
 +					if (function == "position" && argc == 0)
 +						type = ast_func_position;
 +					
 +					break;
 +				}
 +				
 +				case 'r':
 +				{
 +					if (function == "round" && argc == 1)
 +						type = ast_func_round;
 +
 +					break;
 +				}
 +				
 +				case 's':
 +				{
 +					if (function == "string" && argc <= 1)
 +						type = argc == 0 ? ast_func_string_0 : ast_func_string_1;
 +					else if (function == "string-length" && argc <= 1)
 +						type = argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1;
 +					else if (function == "starts-with" && argc == 2)
 +						type = ast_func_starts_with;
 +					else if (function == "substring-before" && argc == 2)
 +						type = ast_func_substring_before;
 +					else if (function == "substring-after" && argc == 2)
 +						type = ast_func_substring_after;
 +					else if (function == "substring" && (argc == 2 || argc == 3))
 +						type = argc == 2 ? ast_func_substring_2 : ast_func_substring_3;
 +					else if (function == "sum" && argc == 1)
 +						type = ast_func_sum;
 +
 +					break;
 +				}
 +				
 +				case 't':
 +				{
 +					if (function == "translate" && argc == 3)
 +						type = ast_func_translate;
 +					else if (function == "true" && argc == 0)
 +						type = ast_func_true;
 +						
 +					break;
 +				}
 +				
 +				}
 +				
 +				if (type != ast_none)
 +				{
 +					switch (argc)
 +					{
 +					case 0: return new (m_alloc) xpath_ast_node(type);
 +					case 1: return new (m_alloc) xpath_ast_node(type, args[0]);
 +					case 2: return new (m_alloc) xpath_ast_node(type, args[0], args[1]);
 +					case 3: return new (m_alloc) xpath_ast_node(type, args[0], args[1], args[2]);
 +					}
 +				}
 +				
 +				throw xpath_exception("Unrecognized function or wrong parameter count");
 +			}
 +
 +	    	default:
 +	    		throw xpath_exception("unrecognizable primary expression");
 +	    	}
 +	    }
 +	    
 +	    // FilterExpr ::= PrimaryExpr | FilterExpr Predicate
 +	    // Predicate ::= '[' PredicateExpr ']'
 +	    // PredicateExpr ::= Expr
 +	    xpath_ast_node* parse_filter_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_primary_expression();
 +
 +	    	while (m_lexer.current() == lex_open_square_brace)
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(ast_filter, n, parse_expression(), axis_child);
 +
 +	    		if (m_lexer.current() != lex_close_square_brace)
 +	    			throw xpath_exception("Unmatched square brace");
 +	    	
 +	    		m_lexer.next();
 +	    	}
 +	    	
 +	    	return n;
 +	    }
 +	    
 +	    // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
 +	    // AxisSpecifier ::= AxisName '::' | '@'?
 +	    // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
 +	    // NameTest ::= '*' | NCName ':' '*' | QName
 +	    // AbbreviatedStep ::= '.' | '..'
 +	    xpath_ast_node* parse_step(xpath_ast_node* set)
 +	    {
 +			axis_t axis;
 +			
 +			if (m_lexer.current() == lex_axis_attribute)
 +			{
 +				axis = axis_attribute;
 +				
 +				m_lexer.next();
 +			}
 +			else if (m_lexer.current() == lex_dot)
 +			{
 +				m_lexer.next();
 +				
 +				return new (m_alloc) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0, m_alloc);
 +			}
 +			else if (m_lexer.current() == lex_double_dot)
 +			{
 +				m_lexer.next();
 +				
 +				return new (m_alloc) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0, m_alloc);
 +			}
 +			else // implied child axis
 +				axis = axis_child;
 +	    
 +			nodetest_t nt_type;
 +			std::string nt_name;
 +			
 +			if (m_lexer.current() == lex_string)
 +			{
 +				// node name test
 +				nt_name = m_lexer.contents();
 +				m_lexer.next();
 +				
 +				// possible axis name here - check.
 +				if (nt_name.find("::") == std::string::npos && m_lexer.current() == lex_string && m_lexer.contents()[0] == ':' && m_lexer.contents()[1] == ':')
 +				{
 +					nt_name += m_lexer.contents();
 +					m_lexer.next();
 +				}
 +				
 +				// possible namespace test
 +				if (m_lexer.current() == lex_string && m_lexer.contents()[0] == ':')
 +				{
 +					std::string::size_type colon_pos = nt_name.find(':');
 +					
 +					// either there is no : in current string or there is, but it's :: and there's nothing more
 +					if (colon_pos == std::string::npos ||
 +						(colon_pos + 1 < nt_name.size() && nt_name[colon_pos + 1] == ':' &&
 +						 nt_name.find(':', colon_pos + 2) == std::string::npos))
 +					{
 +						nt_name += m_lexer.contents();
 +						m_lexer.next();
 +					}
 +				}
 +				
 +				bool axis_specified = true;
 +				
 +				switch (nt_name[0])
 +				{
 +				case 'a':
 +					if (starts_with(nt_name, "ancestor::")) axis = axis_ancestor;
 +					else if (starts_with(nt_name, "ancestor-or-self::")) axis = axis_ancestor_or_self;
 +					else if (starts_with(nt_name, "attribute::")) axis = axis_attribute;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 'c':
 +					if (starts_with(nt_name, "child::")) axis = axis_child;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 'd':
 +					if (starts_with(nt_name, "descendant::")) axis = axis_descendant;
 +					else if (starts_with(nt_name, "descendant-or-self::")) axis = axis_descendant_or_self;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 'f':
 +					if (starts_with(nt_name, "following::")) axis = axis_following;
 +					else if (starts_with(nt_name, "following-sibling::")) axis = axis_following_sibling;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 'n':
 +					if (starts_with(nt_name, "namespace::")) axis = axis_namespace;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 'p':
 +					if (starts_with(nt_name, "parent::")) axis = axis_parent;
 +					else if (starts_with(nt_name, "preceding::")) axis = axis_preceding;
 +					else if (starts_with(nt_name, "preceding-sibling::")) axis = axis_preceding_sibling;
 +					else axis_specified = false;
 +					
 +					break;
 +				
 +				case 's':
 +					if (starts_with(nt_name, "self::")) axis = axis_ancestor_or_self;
 +					else axis_specified = false;
 +					
 +					break;
 +
 +				default:
 +					axis_specified = false;
 +				}
 +				
 +				if (axis_specified)
 +				{
 +					nt_name.erase(0, nt_name.find("::") + 2);
 +				}
 +				
 +				if (nt_name.empty() && m_lexer.current() == lex_string)
 +				{
 +					nt_name += m_lexer.contents();
 +					m_lexer.next();
 +				}
 +
 +				// node type test or processing-instruction
 +				if (m_lexer.current() == lex_open_brace)
 +				{
 +					m_lexer.next();
 +					
 +					if (m_lexer.current() == lex_close_brace)
 +					{
 +						m_lexer.next();
 +						
 +						if (nt_name == "node")
 +							nt_type = nodetest_type_node;
 +						else if (nt_name == "text")
 +							nt_type = nodetest_type_text;
 +						else if (nt_name == "comment")
 +							nt_type = nodetest_type_comment;
 +						else if (nt_name == "processing-instruction")
 +							nt_type = nodetest_type_pi;
 +						else
 +							throw xpath_exception("Unrecognized node type");
 +						
 +						nt_name.clear();
 +					}
 +					else if (nt_name == "processing-instruction")
 +					{
 +						if (m_lexer.current() != lex_quoted_string)
 +							throw xpath_exception("Only literals are allowed as arguments to processing-instruction()");
 +					
 +						nt_type = nodetest_pi;
 +						nt_name = m_lexer.contents();
 +						m_lexer.next();
 +						
 +						if (m_lexer.current() != lex_close_brace)
 +							throw xpath_exception("Unmatched brace near processing-instruction()");
 +						m_lexer.next();
 +					}
 +					else
 +						throw xpath_exception("Unmatched brace near node type test");
 +
 +				}
 +				// namespace *
 +				else if (m_lexer.current() == lex_multiply)
 +				{
 +					// Only strings of form 'namespace:*' are permitted
 +					if (nt_name.empty())
 +						nt_type = nodetest_all;
 +					else
 +					{
 +						if (nt_name.find(':') != nt_name.size() - 1)
 +							throw xpath_exception("Wrong namespace-like node test");
 +						
 +						nt_name.erase(nt_name.size() - 1);
 +						
 +						nt_type = nodetest_all_in_namespace;
 +					}
 +					
 +					m_lexer.next();
 +				}
 +				else nt_type = nodetest_name;
 +			}
 +			else if (m_lexer.current() == lex_multiply)
 +			{
 +				nt_type = nodetest_all;
 +				m_lexer.next();
 +			}
 +			else throw xpath_exception("Unrecognized node test");
 +			
 +			xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_step, set, axis, nt_type, nt_name.c_str(), m_alloc);
 +			
 +			xpath_ast_node* last = 0;
 +			
 +			while (m_lexer.current() == lex_open_square_brace)
 +			{
 +				m_lexer.next();
 +				
 +				xpath_ast_node* pred = new (m_alloc) xpath_ast_node(ast_predicate, parse_expression(), 0, axis);
 +				
 +				if (m_lexer.current() != lex_close_square_brace)
 +	    			throw xpath_exception("unmatched square brace");
 +				m_lexer.next();
 +				
 +				if (last) last->set_next(pred);
 +				else n->set_right(pred);
 +				
 +				last = pred;
 +			}
 +			
 +			return n;
 +	    }
 +	    
 +	    // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
 +	    xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)
 +	    {
 +			xpath_ast_node* n = parse_step(set);
 +			
 +			while (m_lexer.current() == lex_slash || m_lexer.current() == lex_double_slash)
 +			{
 +				lexeme_t l = m_lexer.current();
 +				m_lexer.next();
 +
 +				if (l == lex_double_slash)
 +					n = new (m_alloc) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0, m_alloc);
 +				
 +				n = parse_step(n);
 +			}
 +			
 +			return n;
 +	    }
 +	    
 +	    // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
 +	    // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
 +	    xpath_ast_node* parse_location_path()
 +	    {
 +			if (m_lexer.current() == lex_slash)
 +			{
 +				// Save state for next lexeme - that is, whatever follows '/'
 +				const char* state = m_lexer.state();
 +				
 +				m_lexer.next();
 +				
 +				xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_step_root);
 +				
 +				try
 +				{
 +					n = parse_relative_location_path(n);
 +				}
 +				catch (const xpath_exception&)
 +				{
 +					m_lexer.reset(state);
 +				}
 +				
 +				return n;
 +			}
 +			else if (m_lexer.current() == lex_double_slash)
 +			{
 +				m_lexer.next();
 +				
 +				xpath_ast_node* n = new (m_alloc) xpath_ast_node(ast_step_root);
 +				n = new (m_alloc) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0, m_alloc);
 +				
 +				return parse_relative_location_path(n);
 +			}
 +			else
 +			{
 +				return parse_relative_location_path(0);
 +			}
 +	    }
 +	    
 +	    // PathExpr ::= LocationPath
 +	    //				| FilterExpr
 +	    //				| FilterExpr '/' RelativeLocationPath
 +	    //				| FilterExpr '//' RelativeLocationPath
 +	    xpath_ast_node* parse_path_expression()
 +	    {
 +			// Clarification.
 +			// PathExpr begins with either LocationPath or FilterExpr.
 +			// FilterExpr begins with PrimaryExpr
 +			// PrimaryExpr begins with '$' in case of it being a variable reference,
 +			// '(' in case of it being an expression, string literal, number constant or
 +			// function call.
 +
 +			if (m_lexer.current() == lex_var_ref || m_lexer.current() == lex_open_brace || 
 +				m_lexer.current() == lex_quoted_string || m_lexer.current() == lex_number ||
 +				m_lexer.current() == lex_string)
 +	    	{
 +	    		if (m_lexer.current() == lex_string)
 +	    		{
 +	    			// This is either a function call, or not - if not, we shall proceed with location path
 +	    			const char* state = m_lexer.state();
 +	    			
 +	    			while (*state && *state <= 32) ++state;
 +	    			
 +	    			if (*state != '(') return parse_location_path();
 +	    		}
 +	    		
 +	    		xpath_ast_node* n = parse_filter_expression();
 +
 +	    		if (m_lexer.current() == lex_slash || m_lexer.current() == lex_double_slash)
 +	    		{
 +					lexeme_t l = m_lexer.current();
 +	    			m_lexer.next();
 +	    			
 +					if (l == lex_double_slash)
 +						n = new (m_alloc) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0, m_alloc);
 +	
 +	    			// select from location path
 +	    			return parse_relative_location_path(n);
 +	    		}
 +
 +	    		return n;
 +	    	}
 +	    	else return parse_location_path();
 +	    }
 +
 +	    // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
 +	    xpath_ast_node* parse_union_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_path_expression();
 +
 +	    	while (m_lexer.current() == lex_union)
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(ast_op_union, n, parse_union_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +
 +	    // UnaryExpr ::= UnionExpr | '-' UnaryExpr
 +	    xpath_ast_node* parse_unary_expression()
 +	    {
 +	    	if (m_lexer.current() == lex_minus)
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		return new (m_alloc) xpath_ast_node(ast_op_negate, parse_unary_expression());
 +	    	}
 +	    	else return parse_union_expression();
 +	    }
 +	    
 +	    // MultiplicativeExpr ::= UnaryExpr
 +	    //						  | MultiplicativeExpr '*' UnaryExpr
 +	    //						  | MultiplicativeExpr 'div' UnaryExpr
 +	    //						  | MultiplicativeExpr 'mod' UnaryExpr
 +	    xpath_ast_node* parse_multiplicative_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_unary_expression();
 +
 +	    	while (m_lexer.current() == lex_multiply || (m_lexer.current() == lex_string &&
 +	    		   (!strcmp(m_lexer.contents(), "mod") || !strcmp(m_lexer.contents(), "div"))))
 +	    	{
 +	    		ast_type_t op = m_lexer.current() == lex_multiply ? ast_op_multiply :
 +	    			!strcmp(m_lexer.contents(), "div") ? ast_op_divide : ast_op_mod;
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(op, n, parse_unary_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +
 +	    // AdditiveExpr ::= MultiplicativeExpr
 +	    //					| AdditiveExpr '+' MultiplicativeExpr
 +	    //					| AdditiveExpr '-' MultiplicativeExpr
 +	    xpath_ast_node* parse_additive_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_multiplicative_expression();
 +
 +	    	while (m_lexer.current() == lex_plus || m_lexer.current() == lex_minus)
 +	    	{
 +	    		lexeme_t l = m_lexer.current();
 +
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, n, parse_multiplicative_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +
 +	    // RelationalExpr ::= AdditiveExpr
 +	    //					  | RelationalExpr '<' AdditiveExpr
 +	    //					  | RelationalExpr '>' AdditiveExpr
 +	    //					  | RelationalExpr '<=' AdditiveExpr
 +	    //					  | RelationalExpr '>=' AdditiveExpr
 +	    xpath_ast_node* parse_relational_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_additive_expression();
 +
 +	    	while (m_lexer.current() == lex_less || m_lexer.current() == lex_less_or_equal || 
 +	    		   m_lexer.current() == lex_greater || m_lexer.current() == lex_greater_or_equal)
 +	    	{
 +	    		lexeme_t l = m_lexer.current();
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater :
 +	    						l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal,
 +	    						n, parse_additive_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +	    
 +	    // EqualityExpr ::= RelationalExpr
 +	    //					| EqualityExpr '=' RelationalExpr
 +	    //					| EqualityExpr '!=' RelationalExpr
 +	    xpath_ast_node* parse_equality_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_relational_expression();
 +
 +	    	while (m_lexer.current() == lex_equal || m_lexer.current() == lex_not_equal)
 +	    	{
 +	    		lexeme_t l = m_lexer.current();
 +
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, n, parse_relational_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +	    
 +	    // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
 +	    xpath_ast_node* parse_and_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_equality_expression();
 +
 +	    	while (m_lexer.current() == lex_string && !strcmp(m_lexer.contents(), "and"))
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(ast_op_and, n, parse_equality_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +
 +	    // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
 +	    xpath_ast_node* parse_or_expression()
 +	    {
 +	    	xpath_ast_node* n = parse_and_expression();
 +
 +	    	while (m_lexer.current() == lex_string && !strcmp(m_lexer.contents(), "or"))
 +	    	{
 +	    		m_lexer.next();
 +
 +	    		n = new (m_alloc) xpath_ast_node(ast_op_or, n, parse_and_expression());
 +	    	}
 +
 +	    	return n;
 +	    }
 +		
 +		// Expr ::= OrExpr
 +		xpath_ast_node* parse_expression()
 +		{
 +			return parse_or_expression();
 +		}
 +
 +	public:
 +		explicit xpath_parser(const char* query, xpath_allocator& alloc): m_alloc(alloc), m_lexer(query)
 +		{
 +		}
 +
 +		xpath_ast_node* parse()
 +		{
 +			return parse_expression();
 +		}
 +	};
 +
 +	xpath_query::xpath_query(const char* query): m_alloc(0), m_root(0)
 +	{
 +		compile(query);
 +	}
 +
 +	xpath_query::~xpath_query()
 +	{
 +		delete m_alloc;
 +	}
 +
 +	void xpath_query::compile(const char* query)
 +	{
 +		delete m_alloc;
 +		m_alloc = new xpath_allocator;
 +
 +		xpath_parser p(query, *m_alloc);
 +
 +		m_root = p.parse();
 +		m_root->check_semantics();
 +	}
 +
 +	bool xpath_query::evaluate_boolean(const xml_node& n)
 +	{
 +		if (!m_root) return false;
 +		
 +		xpath_context c;
 +		
 +		c.root = n.root();
 +		c.n = n;
 +		c.position = 1;
 +		c.size = 1;
 +		
 +		return m_root->eval_boolean(c);
 +	}
 +	
 +	double xpath_query::evaluate_number(const xml_node& n)
 +	{
 +		if (!m_root) return gen_nan();
 +		
 +		xpath_context c;
 +		
 +		c.root = n.root();
 +		c.n = n;
 +		c.position = 1;
 +		c.size = 1;
 +		
 +		return m_root->eval_number(c);
 +	}
 +	
 +	std::string xpath_query::evaluate_string(const xml_node& n)
 +	{
 +		if (!m_root) return std::string();
 +		
 +		xpath_context c;
 +		
 +		c.root = n.root();
 +		c.n = n;
 +		c.position = 1;
 +		c.size = 1;
 +		
 +		return m_root->eval_string(c);
 +	}
 +	
 +	xpath_node_set xpath_query::evaluate_node_set(const xml_node& n)
 +	{
 +		if (!m_root) return xpath_node_set();
 +		
 +		xpath_context c;
 +		
 +		c.root = n.root();
 +		c.n = n;
 +		c.position = 1;
 +		c.size = 1;
 +		
 +		return m_root->eval_node_set(c);
 +	}
 +
 +	xpath_node xml_node::select_single_node(const char* query) const
 +	{
 +		xpath_query q(query);
 +		return select_single_node(q);
 +	}
 +
 +	xpath_node xml_node::select_single_node(xpath_query& query) const
 +	{
 +		xpath_node_set s = query.evaluate_node_set(*this);
 +		return s.empty() ? xpath_node() : s.first();
 +	}
 +
 +	xpath_node_set xml_node::select_nodes(const char* query) const
 +	{
 +		xpath_query q(query);
 +		return select_nodes(q);
 +	}
 +
 +	xpath_node_set xml_node::select_nodes(xpath_query& query) const
 +	{
 +		return query.evaluate_node_set(*this);
 +	}
 +}
 +
 +#endif
 | 
