
//pugxml.h

//#define _CRT_SECURE_NO_WARNINGS 1


/////////////////////////////////////////////////////////////////////////////
// Define this in any implementation, before "pugxml.h", to be notified of API campatibility.
#define PUGAPI_VARIANT 0x58475550   //The Pug XML library variant we are using in this implementation.
#define PUGAPI_VERSION_MAJOR 1      //The Pug XML library major version we are using in this implementation.
#define PUGAPI_VERSION_MINOR 2      //The Pug XML library minor version we are using in this implementation.


///////////////////////////////////////////////////////////////////////////////
//
// Pug XML Parser - Version 1.0002
// --------------------------------------------------------
// 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).
//
///////////////////////////////////////////////////////////////////////////////
#if !defined(_PUGXML_)
#define _PUGXML_
#pragma once


//#define PUGOPT_MEMFIL //Uncomment to enable memory-mapped file parsing support.
//#define PUGOPT_NONSEG //Uncomment to enable non-destructive (non-segmenting) parsing support.


#ifdef PUGOPT_MEMFIL
#   ifndef PUGOPT_NONSEG
#       define PUGOPT_NONSEG //PUGOPT_MEMFIL implies PUGOPT_NONSEG.
#   endif
#endif

#include <tchar.h>
#include <wtypes.h>
#include <iostream>
#include <ostream>
#include <string>
#if defined(PUGOPT_MEMFIL) | defined(PUGOPT_NONSEG)
#   include <assert.h>
#endif
#if _MSC_VER >= 1600
#include <iterator>
#endif


#ifndef HIWORD
#   define HIWORD(X) ((unsigned short)((unsigned long)(X)>>16))
#   define LOWORD(X) ((unsigned short)((unsigned long)(X)&0xFFFF))
#endif


//<summary>
//  Library variant ID. The ID 0x58475550 is owned by Kristen Wegner. You *MUST* 
//  provide your own unique ID if you modify or fork the code in this library to 
//  your own purposes. If you change this then *you* are now the maintainer, not me. 
//  Change also in the package section of pugxml.xml, and append yourself to the 
//  authors section.
//</summary>
#define PUGAPI_INTERNAL_VARIANT 0x58475550
//<summary>Major version. Increment for each major release. Only change if you own the variant.</summary>
#define PUGAPI_INTERNAL_VERSION_MAJOR 1
//<summary>Minor version. Increment for each minor release. Only change if you own the variant ID.</summary>
#define PUGAPI_INTERNAL_VERSION_MINOR 2

#define PUGAPI_INTERNAL_VERSION ((PUGAPI_INTERNAL_VERSION_MINOR&0xFFFF)|PUGAPI_INTERNAL_VERSION_MAJOR<<16)


#define PUGDEF_ATTR_NAME_SIZE 128
#define PUGDEF_ATTR_VALU_SIZE 256
#define PUGDEF_ELEM_NAME_SIZE 256


//<summary>The PugXML Parser namespace.</summary>
namespace pug
{

//<summary>The Library Variant ID. See PUGAPI_INTERNAL_VARIANT for an explanation.</summary>
//<returns>The current Library Variant ID.</returns>
inline static unsigned long lib_variant(){ return PUGAPI_INTERNAL_VARIANT; }
//<summary>The library version. High word is major version. Low word is minor version.</summary>
//<returns>The current Library Version.</returns>
inline static unsigned long lib_version(){ return PUGAPI_INTERNAL_VERSION; }


//<summary>A 'name=value' XML attribute structure.</summary>
typedef struct t_xml_attribute_struct
{
    TCHAR*          name;           //Pointer to attribute name.
    bool            name_insitu;    //True if 'name' is a segment of the original parse string.
#ifdef PUGOPT_NONSEG
    unsigned int    name_size;      //Length of element name.
#endif
    TCHAR*          value;          //Pointer to attribute value.
    bool            value_insitu;   //True if 'value' is a segment of the original parse string.
#ifdef PUGOPT_NONSEG
    unsigned int    value_size;     //Length of element name.
#endif
}
    xml_attribute_struct;


//<summary>Tree node classification.</summary>
//<remarks>See 'xml_node_struct::type'.</remarks>
typedef enum t_xml_node_type
{
    node_null,          //An undifferentiated entity.
    node_document,      //A document tree's absolute root.
    node_element,       //E.g. '&lt;...&gt;'
    node_pcdata,        //E.g. '&gt;...&lt;'
    node_cdata,         //E.g. '&lt;![CDATA[...]]&gt;'
    node_comment,       //E.g. '&lt;!--...--&gt;'
    node_pi,            //E.g. '&lt;?...?&gt;'
    node_include,       //E.g. '&lt;![INCLUDE[...]]&gt;'
    node_doctype,       //E.g. '&lt;!DOCTYPE ...&gt;'.
    node_dtd_entity,    //E.g. '&lt;!ENTITY ...&gt;'.
    node_dtd_attlist,   //E.g. '&lt;!ATTLIST ...&gt;'.
    node_dtd_element,   //E.g. '&lt;!ELEMENT ...&gt;'.
    node_dtd_notation,  //E.g. '&lt;!NOTATION ...&gt;'.
    node_attribute
}
    xml_node_type;


static const unsigned long parse_grow = 4; //Default child element & attribute space growth increment.


//Parser Options
static const unsigned long parse_minimal        = 0x00000000; //Unset the following flags.
static const unsigned long parse_pi             = 0x00000002; //Parse '&lt;?...?&gt;'
static const unsigned long parse_doctype        = 0x00000004; //Parse '&lt;!DOCTYPE ...&gt;' section, setting '[...]' as data member.
static const unsigned long parse_comments       = 0x00000008; //Parse &lt;!--...--&gt;'
static const unsigned long parse_cdata          = 0x00000010; //Parse '&lt;![CDATA[...]]&gt;', and/or '&lt;![INCLUDE[...]]&gt;'
static const unsigned long parse_escapes        = 0x00000020; //Not implemented.
static const unsigned long parse_trim_pcdata    = 0x00000040; //Trim '&gt;...&lt;'
static const unsigned long parse_trim_attribute = 0x00000080; //Trim 'foo="..."'.
static const unsigned long parse_trim_cdata     = 0x00000100; //Trim '&lt;![CDATA[...]]&gt;', and/or '&lt;![INCLUDE[...]]&gt;'
static const unsigned long parse_trim_entity    = 0x00000200; //Trim '&lt;!ENTITY name ...&gt;', etc.
static const unsigned long parse_trim_doctype   = 0x00000400; //Trim '&lt;!DOCTYPE [...]&gt;'
static const unsigned long parse_trim_comment   = 0x00000800; //Trim &lt;!--...--&gt;'
static const unsigned long parse_wnorm          = 0x00001000; //Normalize all entities that are flagged to be trimmed.
static const unsigned long parse_dtd            = 0x00002000; //If parse_doctype set, then parse whatever is in data member ('[...]').
static const unsigned long parse_dtd_only       = 0x00004000; //If parse_doctype|parse_dtd set, then parse only '&lt;!DOCTYPE [*]&gt;'
static const unsigned long parse_default        = 0x0000FFFF;
static const unsigned long parse_noset          = 0x80000000;


//<summary>An XML document tree node.</summary>
typedef struct t_xml_node_struct
{
    t_xml_node_struct*      parent;             //Pointer to parent
    TCHAR*                  name;               //Pointer to element name.
#ifdef PUGOPT_NONSEG
    unsigned int            name_size;          //Length of element name. Since 19 Jan 2003 NF.
#endif
    bool                    name_insitu;        //True if 'name' is a segment of the original parse string.
    xml_node_type           type;               //Node type; see xml_node_type.
    unsigned int            attributes;         //Count attributes.
    unsigned int            attribute_space;    //Available pointer space in 'attribute'.
    xml_attribute_struct**  attribute;          //Array of pointers to attributes; see xml_attribute_struct.
    unsigned int            children;           //Count children in member 'child'.
    unsigned int            child_space;        //Available pointer space in 'child'.
    t_xml_node_struct**     child;              //Array of pointers to children.
    TCHAR*                  value;              //Pointer to any associated string data.
#ifdef PUGOPT_NONSEG
    unsigned int            value_size;         //Length of element data. Since 19 Jan 2003 NF.
#endif
    bool                    value_insitu;       //True if 'data' is a segment of the original parse string.
}
xml_node_struct;


//<summary>Concatenate 'rhs' to 'lhs', growing 'rhs' if neccessary.</summary>
//<param name="lhs">Pointer to pointer to receiving string. Note: If '*lhs' is not null, it must have been dynamically allocated using 'malloc'.</param>
//<param name="rhs">Source.</param>
//<returns>Success if 'realloc' was successful.</returns>
//<remarks>'rhs' is resized and 'rhs' is concatenated to it.</remarks>
inline static bool strcatgrow(TCHAR** lhs,const TCHAR* rhs)
{
    if(!*lhs) //Null, so first allocate.
    {
        *lhs = (TCHAR*) malloc(1UL*sizeof(TCHAR));
        **lhs = 0; //Zero-terminate.
    }
    size_t ulhs = _tcslen(*lhs);
    size_t urhs = _tcslen(rhs);
    TCHAR* temp = (TCHAR*) realloc(*lhs,(ulhs+urhs+1UL)*sizeof(TCHAR));
    if(!temp) return false; //Realloc failed.
    memcpy(temp+ulhs,rhs,urhs*sizeof(TCHAR)); //Concatenate.
    temp[ulhs+urhs] = 0; //Terminate it.
    *lhs = temp;
    return true;
}


inline static bool chartype_symbol(TCHAR c) //Character is alphanumeric, -or- '_', -or- ':', -or- '-', -or- '.'.
{ return (_istalnum(c)||c==_T('_')||c==_T(':')||c==_T('-')||c==_T('.')); }
inline static bool chartype_space(TCHAR c) //Character is greater than 0 or character is less than exclamation.
{ return (c>0 && c<_T('!')); }
inline static bool chartype_enter(TCHAR c) //Character is '&lt;'.
{ return (c==_T('<')); }
inline static bool chartype_leave(TCHAR c) //Character is '&gt;'.
{ return (c==_T('>')); }
inline static bool chartype_close(TCHAR c) //Character is '/'.
{ return (c==_T('/')); }
inline static bool chartype_equals(TCHAR c) //Character is '='.
{ return (c==_T('=')); }
inline static bool chartype_special(TCHAR c) //Character is '!'.
{ return (c==_T('!')); }
inline static bool chartype_pi(TCHAR c) //Character is '?'.
{ return (c==_T('?')); }
inline static bool chartype_dash(TCHAR c) //Character is '-'.
{ return (c==_T('-')); }
inline static bool chartype_quote(TCHAR c) //Character is &quot;&lsquo;&quot; -or- &lsquo;&quot;&lsquo;.
{ return (c==_T('"')||c==_T('\'')); }
inline static bool chartype_lbracket(TCHAR c) //Character is '['.
{ return (c==_T('[')); }
inline static bool chartype_rbracket(TCHAR c) //Character is ']'.
{ return (c==_T(']')); }


#ifdef PUGOPT_NONSEG


//<summary>Concatenate 'rhs' to 'lhs', growing 'lhs' if neccessary.</summary>
//<param name="lhs">Pointer to pointer to receiving string. Note: If '*lhs' is not null, it must have been dynamically allocated using 'malloc'.</param>
//<param name="rhs">Source.</param>
//<param name="lsize">Specifies the length of *lhs in bytes and returns its new length.</param>
//<param name="rsize">Specifies the length of *rhs in bytes.</param>
//<returns>Success if 'realloc' was successful.</returns>
//<remarks>'lhs' is resized and 'rhs' is concatenated to it.</remarks>
inline static bool strcatgrown_impl(TCHAR** lhs,const TCHAR* rhs,unsigned int& lsize,unsigned int rsize)
{
    if(!*lhs) //Null, allocate and copy.
    {
        *lhs = (TCHAR*) malloc(rsize+sizeof(TCHAR));
        if(!*lhs)
        {
            lsize = 0;
            return false; //Allocate failed.
        }
        memcpy(*lhs,rhs,rsize); //Concatenate.
        *(*lhs + rsize) = 0; //Terminate it.
        lsize = rsize;
    }
    else //Reallocate. NF I don't think this is right for MBCS, nor is code in 'StrCatGrow()'.
    {
        TCHAR* temp = (TCHAR*) realloc(*lhs,lsize + rsize + sizeof(TCHAR));
        if(!temp) return false; //Realloc failed.
        memcpy(temp+lsize,rhs,rsize); //Concatenate.
        lsize += rsize; //Set new length.
        temp[lsize] = 0; //Terminate it.
        *lhs = temp;
    }
    return true;
}

//<summary>Concatenate 'rhs' to 'lhs', growing 'lhs' if neccessary.</summary>
//<param name="lhs">Pointer to pointer to receiving string. Note: If '*lhs' is not null, it must have been dynamically allocated using 'malloc'.</param>
//<param name="rhs">Source.</param>
//<param name="lsize">Specifies the length of *lhs in bytes and returns its new length.</param>
//<returns>Success if 'realloc' was successful.</returns>
//<remarks>'lhs' is resized and 'rhs' is concatenated to it.</remarks>
inline static bool strcatgrown(TCHAR** lhs,const TCHAR* rhs,unsigned int& lsize)
{
    const unsigned int rsize = _tcslen(rhs) * sizeof(TCHAR);
    return pug::strcatgrown_impl(lhs,rhs,lsize,rsize);
}

//<summary>Trim leading and trailing whitespace.</summary>
//<param name="s">Pointer to pointer to string.</param>
//<param name="len">Specifies the length of *s in bytes and returns its new length.</param>
//<returns>Success.</returns>
//<remarks>*s is modified to point to the first non-white character in the string.</remarks>
inline static bool strwtrim(TCHAR** s,unsigned int& len)
{
    if(!s || !*s) return false;
    TCHAR* pse = *s + len;
    while(*s < pse && pug::chartype_space(**s)) //Find first non-white character.
        ++*s; //As long as we hit whitespace, increment the string pointer.
    for(; *s < --pse;) //As long as we hit whitespace, decrement.
    {
        if(!pug::chartype_space(*pse))
        {
            len = pse + 1 - *s;
            break;
        }
    }
    return true;
}


#else


//<summary>Trim leading and trailing whitespace.</summary>
//<param name="s">Pointer to pointer to string.</param>
//<returns>Success.</returns>
inline static bool strwtrim(TCHAR** s)
{
    if(!s || !*s) return false;
    while(**s > 0 && **s < _T('!')) ++*s; //As long as we hit whitespace, increment the string pointer.
    const TCHAR* temp = *s;
    while(0 != *temp++); //Find the terminating null.
    long i, n = (long)(temp-*s-1);
    --n; //Start from the last string TCHAR.
    for(i=n; (i > -1) && (*s)[i] > 0 && (*s)[i] < _T('!'); --i); //As long as we hit whitespace, decrement.
    if(i<n) (*s)[i+1] = 0; //Zero-terminate.
    return true;
}


//<summary>
//  In situ trim leading and trailing whitespace, then convert all consecutive 
//  whitespace to a single space TCHAR.
//</summary>
//<param name="s">Pointer to pointer to string.</param>
//<returns>Success.</returns>
inline static bool strwnorm(TCHAR** s)
{
    if (!s || !*s)
        return false; // No string to normalize.
    while (**s > 0 && **s < _T('!')) ++ (*s); // As long as we hit whitespace, increment the string pointer.
    const TCHAR* temp = *s;
    while (0 != *temp++); // Find the terminating null.
    long n = (long)(temp - *s - 1);
    TCHAR* norm = (TCHAR*)malloc(sizeof(TCHAR)* (n + 1)); // Allocate a temporary normalization buffer.
    if (!norm)
        return false; // Allocation failed.
    memset(norm, 0, sizeof(TCHAR)* (n + 1)); // Zero it.
    long j = 1;
    norm[0] = (*s)[0];
    long i;
    for (i = 1; i < n; ++i) // For each character, starting at offset 1.
    {
        if ((*s)[i] < _T('!')) // Whitespace-like.
        {
            if ((*s)[i - 1] >= _T('!')) // Previous was not whitespace-like.
            {
                norm[j] = _T(' '); // Convert to a space TCHAR.
                ++j; // Normalization buffer grew by one TCHAR.
            }
        }
        else 
        {
            norm[j] = (*s)[i];
            ++j; 
        } // Not whitespace, so just copy over.
    }
    if (j < n) // Normalization buffer is actually different that input.
    {
        _tcsncpy(*s, norm, j); // So, copy it back to input.
        (*s)[j] = 0; // Zero-terminate.
    }
    free(norm); // Don't need this anymore.
    --n; // Start from the last string TCHAR.
    for (i = n; (i > -1) && (*s)[i] > 0 && (*s)[i] < _T('!'); --i)
        ; // Find the first non-whitespace from the end.
    if (i < n)
        (*s)[i + 1] = 0; // Truncate it.
    return true;
}

#endif


//<summary>Set structure string member to given value.</summary>
//<param name="dest">Pointer to pointer to destination.</param>
//<param name="src">Source.</param>
//<param name="insitu">Pointer to boolean in-situ string flag.</param>
//<returns>True if member was set to the new value.</returns>
//<remarks>
//  If 'src' is larger than 'dest' then 'dest' is resized, in which case 
//  it is probably no longer in-situ,and 'in_situ' is set to false. If 
//  'dest' is already no longer in-situ, and 'src' is too small then the 
//  existing memory pointed to is freed. If 'dest' is larger than or equal 
//  to 'dest' then it is merely copied with no resize.
//</remarks>
inline static bool strcpyinsitu
(
        TCHAR**         dest,
        const TCHAR*    src,
        bool*           insitu
#ifdef PUGOPT_NONSEG
        ,
        unsigned int&   destlen
#endif
)
{
    if (!dest || !src || !insitu)
        return false; // Bad argument(s), so fail.
#ifndef PUGOPT_NONSEG // Always use heap for our r/o string.
    size_t l = (*dest) ? _tcslen(*dest) : 0; // How long is destination?
    if (l >= _tcslen(src)) // Destination is large enough, so just copy.
    {
        if (_tcslen(src) > 0)       // JCrane: no copying null strings
        _tcscpy(*dest, src); // Copy.
        return TRUE; // Success.
    }
    else // Destination is too small.
#endif
    {
        if (*dest && !*insitu)
            free(*dest); // If destination is not in-situ, then free it.
        *dest = NULL; // Mark destination as NULL, forcing 'StrCatGrow' to 'malloc.
#ifdef PUGOPT_NONSEG
        if (strcatgrown(dest, src, destlen)) // Allocate & copy source to destination
#else
            if (strcatgrow(dest, src)) // Allocate & copy source to destination
#endif
            {
                *insitu = false; // Mark as no longer being in-situ, so we can free it later.
                return TRUE; // Success.
            }
    }
    return false; // Failure.
}


//<summary>Character set pattern match.</summary>
//<param name="lhs">String or expression for left-hand side of comparison.</param>
//<param name="rhs">String for right-hand side of comparison.</param>
//<remarks>Used by 'strcmpwild'.</remarks>
inline int strcmpwild_cset(const TCHAR** src,const TCHAR** dst)
{
    int find = 0;
    int excl = 0;
    int star = 1;
    if(**src == _T('!'))
    {
        excl = 1;
        ++(*src);
    }
    while(**src != _T(']') || star == 1)
    {
        if(find == 0)
        {
            if(**src == _T('-') && *(*src-1) < *(*src+1) && *(*src+1) != _T(']') && 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;
}


int strcmpwild_impl(const TCHAR* src,const TCHAR* dst); //Forward declaration.


//<summary>Wildcard pattern match.</summary>
//<param name="lhs">String or expression for left-hand side of comparison.</param>
//<param name="rhs">String for right-hand side of comparison.</param>
//<remarks>Used by 'strcmpwild'.</remarks>
static int strcmpwild_astr(const TCHAR** src, const TCHAR** dst)
{
    int find = 1;
    ++ (*src);
    while ((**dst != 0 && **src == _T('?')) || **src == _T('*'))
    {
        if (**src == _T('?'))
            ++ (*dst);
        ++ (*src);
    }
    while (**src == _T('*')) ++ (*src);
    if (**dst == 0 && **src != 0)
        return 0;
    if (**dst == 0 && **src == 0)
        return 1;
    else
    {
        if (strcmpwild_impl(*src, *dst) == 0)
        {
            do
            {
                ++ (*dst);
                while (**src != **dst && **src != _T('[') && **dst != 0) 
                    ++ (*dst);
            }
            while ((**dst != 0) ? strcmpwild_impl(*src, *dst) == 0 : 0 !=(find = 0));
        }
        if (**dst == 0 && **src == 0)
            find = 1;
        return find;
    }
}



//<summary>Compare two strings, with globbing, and character sets.</summary>
//<param name="lhs">String or expression for left-hand side of comparison.</param>
//<param name="rhs">String for right-hand side of comparison.</param>
//<remarks>Used by 'strcmpwild'.</remarks>
int strcmpwild_impl(const TCHAR* src, const TCHAR* dst)
{
    int find = 1;
    for (; *src != 0 && find == 1 && *dst != 0; ++src)
    {
        switch (*src)
        {
            case _T('?'): 
                ++dst;
                break;
            case _T('['): 
                ++src;
                find = strcmpwild_cset(&src, &dst);
                break;
            case _T('*'): 
                find = strcmpwild_astr(&src, &dst);
                --src;
                break;
            default : find = (int)(*src == *dst);
                ++dst;
        }
    }
    while (*src == _T('*') && find == 1) ++src;
    return (int)(find == 1 && *dst == 0 && *src == 0);
}

//<summary>Compare two strings, with globbing, and character sets.</summary>
//<param name="lhs">String or expression for left-hand side of comparison.</param>
//<param name="rhs">String for right-hand side of comparison.</param>
//<returns>
//  Returns 1 if src does not match dst, or -1 if either src or dst are null, 
//  or 0 if src matches dst.
//</returns>
//<remarks>
//  Simple regular expressions are permitted in 'src': The character '*' matches
//  zero or more characters up to the next pattern, or the end of the string. The 
//  '?' character matches any single character. Character sets and negation are 
//  also permitted, for example, '[abcd]', '[a-zA-Z]', etc.
//</remarks>
inline int strcmpwild(const TCHAR* src,const TCHAR* dst) 
{
    if(!src || !dst) return -1;
    return (strcmpwild_impl(src,dst)==1)?0:1;
}


//<summary>Allocate & init an xml_attribute_struct structure.</summary>
//<returns>Pointer to new xml_attribute_struct structure.</returns>
inline static xml_attribute_struct* new_attribute(void)
{
    xml_attribute_struct* p = (xml_attribute_struct*)malloc(sizeof(xml_attribute_struct)); //Allocate one attribute.
    if(p) //If allocation succeeded.
    {
        p->name = p->value = 0; //No name or value.
#ifdef PUGOPT_NONSEG
        p->name_size = p->value_size = 0; //Lengths of zero.
#endif
        p->name_insitu = p->value_insitu = true; //Default to being in-situ of the parse string.
    }
    return p;
}


//<summary>Allocate & init an xml_node_struct structure.</summary>
//<param name="type">Desired node type.</param>
//<returns>Pointer to new xml_node_struct structure.</returns>
inline static xml_node_struct* new_node(xml_node_type type = node_element)
{
    xml_node_struct* p = (xml_node_struct*)malloc(sizeof(xml_node_struct)); //Allocate one node.
    if(p) //If allocation succeeded.
    {
        p->name = p->value = 0; //No name or data.
#ifdef PUGOPT_NONSEG
        p->name_size = p->value_size = 0;
#endif
        p->type = type; //Set the desired type.
        p->attributes = p->children = 0; //No attributes or children.
        p->name_insitu = p->value_insitu = true; //Default to being in-situ of the parse string.
        if
        (
            type != node_document   && //None of these will have attributes.
            type != node_pcdata     &&
            type != node_cdata      &&
            type != node_attribute  && // Added so xpath create treat an attribute like a node
            type != node_include    &&
            type != node_comment
        )
            p->attribute = (xml_attribute_struct**)malloc(sizeof(xml_attribute_struct*)); //Allocate one attribute.
        else p->attribute = NULL;
        p->attribute_space = (p->attribute) ? 1 : 0;
        if
        (
            type == node_element || //Only these will have children.
            type == node_doctype ||
            type == node_document
        )
            p->child = (xml_node_struct**)malloc(sizeof(xml_node_struct*)); //Allocate one child.
        else p->child = NULL;
        p->child_space = (p->child) ? 1 : 0;
    }
    return p;
}


//<summary>Allocate & append a new xml_node_struct onto the given parent.</summary>
//<param name="parent">Pointer to parent node.</param>
//<param name="grow">Pointer space growth increment.</param>
//<param name="type">Desired node type.</param>
//<returns>Pointer to new node.</returns>
//<remarks>Child pointer space of 'node' may be reallocated.</remarks>
inline static xml_node_struct* append_node(xml_node_struct* parent,long grow,xml_node_type type = node_element)
{
    if(!parent) return NULL; //Must have a parent.
    if(parent->children == parent->child_space) //Out of pointer space.
    {
        xml_node_struct** t = (xml_node_struct**)realloc(parent->child,sizeof(xml_node_struct*)*(parent->child_space+grow)); //Grow pointer space.
        if(t) //Reallocation succeeded.
        {
            parent->child = t;
            parent->child_space += grow; //Update the available space.
        }
    }
    xml_node_struct* child = new_node(type); //Allocate a new child.
    child->parent = parent; //Set it's parent pointer.
    parent->child[parent->children] = child; //Set the parent's child pointer.
    parent->children++; //One more child.
    return child;
}

/////////////////////////////////////////////////////////////////////////
    // Added by JCrane - not working yet - blows up when freed
inline static xml_node_struct* append_node_noalloc(xml_node_struct* parent,long grow, xml_node_struct* child)
{
    if(!parent) return NULL; //Must have a parent.
    if(parent->children == parent->child_space) //Out of pointer space.
    {
        xml_node_struct** t = (xml_node_struct**)realloc(parent->child,sizeof(xml_node_struct*)*(parent->child_space+grow)); //Grow pointer space.
        if(t) //Reallocation succeeded.
        {
            parent->child = t;
            parent->child_space += grow; //Update the available space.
        }
    }
    child->parent = parent; //Set it's parent pointer.
    parent->child[parent->children] = child; //Set the parent's child pointer.
    parent->children++; //One more child.
    return child;
}
/////////////////////////////////////////////////////////////////////////


//<summary>Allocate & append a new attribute to the given xml_node_struct.</summary>
//<param name="node">Pointer to parent node.</param>
//<param name="grow">Pointer space growth increment.</param>
//<returns>Pointer to appended xml_attribute_struct.</returns>
//<remarks>Attribute pointer space of 'node' may be reallocated.</remarks>
inline static xml_attribute_struct* append_attribute(xml_node_struct* node,long grow)
{
    if(!node) return NULL;
    xml_attribute_struct* a = new_attribute();
    if(!a) return NULL;
    if(node->attributes == node->attribute_space) //Out of space, so grow.
    {
        xml_attribute_struct** t = (xml_attribute_struct**)realloc(node->attribute,sizeof(xml_node_struct*)*(node->attribute_space+grow));
        if(t)
        {
            node->attribute = t;
            node->attribute_space += grow;
        }
    }
    node->attribute[node->attributes] = a;
    node->attributes++;
    return a;
}


//<summary>Non-recursively free a tree.</summary>
//<param name="root">
//  Pointer to the root of the tree. Note: 'root' must have been dynamically 
//  allocated using 'malloc' or 'realloc', as 'free_node' tries to also free 
//  the structure pointed to by 'root'.
//</param>
//<remarks>'root' no longer points to a valid structure.</remarks>
inline static void free_node(xml_node_struct* node)
{
    if(!node) return;

    register xml_node_struct* cursor = node;

    //Free all children of children.
    do
    {
LOC_STEP_INTO:
        for(; cursor->children>0; --cursor->children) //Free each child in turn; 'children' keeps count while we jump around.
        {
            register xml_node_struct* t = cursor->child[cursor->children-1]; //Take a pointer to the child.
            if(t && t->children) //If the child has children.
            {
                cursor = t; //Step in.
                goto LOC_STEP_INTO; //Step into this node.
            }
            else if(t)
            {
                if(t->attributes) //Child has attributes.
                {
                    register unsigned int n = t->attributes; //Free each attribute.
                    for(register unsigned int i=0; i<n; ++i)
                    {
                        if(t->attribute[i]->name && !t->attribute[i]->name_insitu)
                            free(t->attribute[i]->name);
                        if(t->attribute[i]->value && !t->attribute[i]->value_insitu)
                            free(t->attribute[i]->value);
                        free(t->attribute[i]);
                    }
                }
                if(t->attribute) free(t->attribute); //Free attribute pointer space.
                if(t->child) free(t->child); //Free child pointer space.
                if(t->name && !t->name_insitu) free(t->name);
                if(t->value && !t->value_insitu) free(t->value);
                free(t); //Free the child node.
            }
        }
        cursor = cursor->parent; //Step out.
    }
    while(cursor->children); //While there are children.
    //Finally, free the root's children & the root itself.
    if(cursor->attributes)
    {
        register unsigned int n = cursor->attributes;
        for(register unsigned int i=0; i<n; ++i)
        {
            if(cursor->attribute[i]->name && !cursor->attribute[i]->name_insitu)
                free(cursor->attribute[i]->name);
            if(cursor->attribute[i]->value && !cursor->attribute[i]->value_insitu)
                free(cursor->attribute[i]->value);
            free(cursor->attribute[i]);
        }
    }
    if(cursor->attribute) free(cursor->attribute); //Free attribute pointer space.
    if(cursor->child) free(cursor->child); //Free child pointer space.
    if(cursor->name && !cursor->name_insitu) free(cursor->name); //Free name & data.
    if(cursor->value && !cursor->value_insitu) free(cursor->value);
    free(cursor); //Free the root itself.
}

//<summary>Recursively free a tree.</summary>
//<param name="root">Pointer to the root of the tree.</param>
//<remarks>Not used.</remarks>
inline static void free_node_recursive(xml_node_struct* root)
{
    if(root)
    {
        unsigned int n = root->attributes;
        register unsigned int i;
        for(i=0; i<n; i++)
        {
            if(root->attribute[i]->name && !root->attribute[i]->name_insitu)
                free(root->attribute[i]->name);
            if(root->attribute[i]->value && !root->attribute[i]->value_insitu)
                free(root->attribute[i]->value);
            free(root->attribute[i]);
        }
        free(root->attribute);
        n = root->children;
        for(i=0; i<n; i++)
            free_node_recursive(root->child[i]);
        free(root->child);
        if(root->name && !root->name_insitu) free(root->name);
        if(root->value && !root->value_insitu) free(root->value);
        free(root);
    }
}


//<summary>Parser utilities.</summary>
#define SKIPWS()            { while(chartype_space(*s)) ++s; if(*s==0) return s; }
#define OPTSET(OPT)         ( optmsk & OPT )
#define PUSHNODE(TYPE)      { cursor = append_node(cursor,growby,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; }
#ifndef PUGOPT_NONSEG
#   define ENDSEG()         { ch = *s; *s = 0; ++s; if(*s==0) return s; }
#else
#   define ENDSEG()         { ch = *s; ++s; if(*s==0) return s; }
#   define SETLEN()         ( cursor->value_size = s - cursor->value )
#   define ENDSEGDAT()      { ch = *s; SETLEN(); ++s; if(*s==0) return s; } 
#   define ENDSEGNAM(S)     { ch = *s; S->name_size = s - S->name; ++s; if(*s==0) return s; } 
#   define ENDSEGATT(S)     { ch = *s; S->value_size = s - S->value; ++s; if(*s==0) return s; }
#endif


//<summary>Static single-pass in-situ parse the given xml string.</summary>
//<param name="s">Pointer to XML-formatted string.</param>
//<param name="root">Pointer to root.</param>
//<param name="grow">Pointer space growth increment.</param>
//<param name="optmsk">Parse options mask.</param>
//<returns>Last string position or null.</returns>
//<remarks>
//  Input string is zero-segmented if 'PUGOPT_NONSEG' is not defined. Memory 
//  may have been allocated to 'root' (free with 'free_node').
//</remarks>
static TCHAR* parse(register TCHAR* s,xml_node_struct* xmldoc,long growby,unsigned long optmsk = parse_default)
{
    if(!s || !xmldoc) return s;
    TCHAR ch = 0; //Current char, in cases where we must null-terminate before we test.
    xml_node_struct* cursor = xmldoc; //Tree node cursor.
    TCHAR* mark = s; //Marked string position for temporary look-ahead.
    while(*s!=0)
    {
LOC_SEARCH: //Obliviously search for next element.
        SCANFOR(chartype_enter(*s)); //Find the next '<'.
        if(chartype_enter(*s))
        {
            ++s;
LOC_CLASSIFY: //What kind of element?
            if(chartype_pi(*s)) //'<?...'
            {
                ++s;
                if(chartype_symbol(*s) && OPTSET(parse_pi))
                {
                    mark = s;
                    SCANFOR(chartype_pi(*s)); //Look for terminating '?'.
#ifndef PUGOPT_NONSEG
                    if(chartype_pi(*s)) *s = _T('/'); //Same semantics as for '<.../>', so fudge it.
#endif
                    s = mark;
                    PUSHNODE(node_pi); //Append a new node on the tree.
                    goto LOC_ELEMENT; //Go read the element name.
                }
                else //Bad PI or parse_pi not set.
                {
                    SCANFOR(chartype_leave(*s)); //Look for '>'.
                    ++s;
                    mark = 0;
                    continue;
                }
            }
            else if(chartype_special(*s)) //'<!...'
            {
                ++s;
                if(chartype_dash(*s)) //'<!-...'
                {
                    ++s;
                    if(OPTSET(parse_comments) && chartype_dash(*s)) //'<!--...'
                    {
                        ++s;
                        PUSHNODE(node_comment); //Append a new node on the tree.
                        cursor->value = s; //Save the offset.
                        while(*s!=0 && *(s+1) && *(s+2) && !((chartype_dash(*s) && chartype_dash(*(s+1))) && chartype_leave(*(s+2)))) ++s; //Scan for terminating '-->'.
                        if(*s==0) return s;
#ifdef PUGOPT_NONSEG
                        SETLEN(); //NF 19 Jan 2003.
#else
                        *s = 0; //Zero-terminate this segment at the first terminating '-'.
#endif
                        if(OPTSET(parse_trim_comment)) //Trim whitespace.
                        {
#ifdef PUGOPT_NONSEG
                            strwtrim(&cursor->value,cursor->value_size);
#else
                            if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                            else strwtrim(&cursor->value);
#endif
                        }
                        s += 2; //Step over the '\0-'.
                        POPNODE(); //Pop since this is a standalone.
                        goto LOC_LEAVE; //Look for any following PCDATA.
                    }
                    else
                    {
                        while(*s!=0 && *(s+1)!=0 && *(s+2)!=0 && !((chartype_dash(*s) && chartype_dash(*(s+1))) && chartype_leave(*(s+2)))) ++s; //Scan for terminating '-->'.
                        if(*s==0) return s;
                        s += 2;
                        goto LOC_LEAVE; //Look for any following PCDATA.
                    }
                }
                else if(chartype_lbracket(*s)) //'<![...'
                {
                    ++s;
                    if(*s==_T('I')) //'<![I...'
                    {
                        ++s;
                        if(*s==_T('N')) //'<![IN...'
                        {
                            ++s;
                            if(*s==_T('C')) //'<![INC...'
                            {
                                ++s;
                                if(*s==_T('L')) //'<![INCL...'
                                {
                                    ++s;
                                    if(*s==_T('U')) //'<![INCLU...'
                                    {
                                        ++s;
                                        if(*s==_T('D')) //'<![INCLUD...'
                                        {
                                            ++s;
                                            if(*s==_T('E')) //'<![INCLUDE...'
                                            {
                                                ++s;
                                                if(chartype_lbracket(*s)) //'<![INCLUDE[...'
                                                {
                                                    ++s;
                                                    if(OPTSET(node_cdata))
                                                    {
                                                        PUSHNODE(node_include); //Append a new node on the tree.
                                                        cursor->value = s; //Save the offset.
                                                        while(!(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)))) ++s; //Scan for terminating ']]>'.
                                                        if(chartype_rbracket(*s))
                                                        {
#ifdef PUGOPT_NONSEG
                                                            SETLEN(); //NF 19 Jan 2003.
#else
                                                            *s = 0; //Zero-terminate this segment.
#endif
                                                            ++s;
                                                            if(OPTSET(parse_trim_cdata)) //Trim whitespace.
                                                            {
#ifdef PUGOPT_NONSEG
                                                                strwtrim(&cursor->value, cursor->value_size);
#else
                                                                if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                                                else strwtrim(&cursor->value);
#endif
                                                            }
                                                        }
                                                        POPNODE(); //Pop since this is a standalone.
                                                    }
                                                    else //Flagged for discard, but we still have to scan for the terminator.
                                                    {
                                                        while(*s!=0 && *(s+1)!=0 && *(s+2)!=0 && !(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)))) ++s; //Scan for terminating ']]>'.
                                                        ++s;
                                                    }
                                                    ++s; //Step over the last ']'.
                                                    goto LOC_LEAVE; //Look for any following PCDATA.
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if(*s==_T('C')) //'<![C...'
                    {
                        ++s;
                        if(*s==_T('D')) //'<![CD...'
                        {
                            ++s;
                            if(*s==_T('A')) //'<![CDA...'
                            {
                                ++s;
                                if(*s==_T('T')) //'<![CDAT...'
                                {
                                    ++s;
                                    if(*s==_T('A')) //'<![CDATA...'
                                    {
                                        ++s;
                                        if(chartype_lbracket(*s)) //'<![CDATA[...'
                                        {
                                            ++s;
                                            if(OPTSET(parse_cdata))
                                            {
                                                PUSHNODE(node_cdata); //Append a new node on the tree.
                                                cursor->value = s; //Save the offset.
                                                while(*s!=0 && *(s+1)!=0 && *(s+2)!=0 && !(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)))) ++s; //Scan for terminating ']]>'.
                                                if(*(s+2)==0) return s; //Very badly formed.
                                                if(chartype_rbracket(*s))
                                                {
#ifdef PUGOPT_NONSEG
                                                    SETLEN(); //NF 19 Jan 2003.
#else
                                                    *s = 0; //Zero-terminate this segment.
#endif
                                                    ++s;
                                                    if(OPTSET(parse_trim_cdata)) //Trim whitespace.
                                                    {
#ifdef PUGOPT_NONSEG
                                                        strwtrim(&cursor->value,cursor->value_size);
#else
                                                        if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                                        else strwtrim(&cursor->value);
#endif
                                                    }
                                                }
                                                POPNODE(); //Pop since this is a standalone.
                                            }
                                            else //Flagged for discard, but we still have to scan for the terminator.
                                            {
                                                while(*s!=0 && *(s+1)!=0 && *(s+2)!=0 && !(chartype_rbracket(*s) && chartype_rbracket(*(s+1)) && chartype_leave(*(s+2)))) ++s; //Scan for terminating ']]>'.
                                                ++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==_T('D')) //'<!D...'
                {
                    ++s;
                    if(*s==_T('O')) //'<!DO...'
                    {
                        ++s;
                        if(*s==_T('C')) //'<!DOC...'
                        {
                            ++s;
                            if(*s==_T('T')) //'<!DOCT...'
                            {
                                ++s;
                                if(*s==_T('Y')) //'<!DOCTY...'
                                {
                                    ++s;
                                    if(*s==_T('P')) //'<!DOCTYP...'
                                    {
                                        ++s;
                                        if(*s==_T('E')) //'<!DOCTYPE...'
                                        {
                                            ++s;
                                            SKIPWS(); //Eat any whitespace.
                                            xml_attribute_struct* a = 0;
                                            if(OPTSET(parse_doctype))
                                            {
                                                PUSHNODE(node_doctype); //Append a new node on the tree.
                                                a = append_attribute(cursor,3); //Store the DOCTYPE name.
                                                a->value = a->name = s; //Save the offset.
                                            }
                                            SCANWHILE(chartype_symbol(*s)); //'<!DOCTYPE symbol...'
#ifdef PUGOPT_NONSEG
                                            if(OPTSET(parse_doctype))
                                                a->name_size = a->value_size = s - a->value; //Save the length. rem: Before ENDSEG()
#endif
                                            ENDSEG(); //Save char in 'ch', terminate & step over.
                                            if(chartype_space(ch)) SKIPWS(); //Eat any whitespace.
LOC_DOCTYPE_SYMBOL:
                                            if(chartype_symbol(*s))
                                            {
                                                mark = s;
                                                SCANWHILE(chartype_symbol(*s)); //'...symbol SYSTEM...'
                                                if(OPTSET(parse_doctype))
                                                {
                                                    a = append_attribute(cursor,1);
                                                    a->value = a->name = mark;
#ifdef PUGOPT_NONSEG
                                                    a->value_size = a->name_size = s - mark; //NF 19 Jan 2003.
#else
                                                    *s = 0;
#endif
                                                }
                                                ++s;
                                                SKIPWS();
                                            }
                                            if(chartype_quote(*s)) //'...SYSTEM "..."'
                                            {
LOC_DOCTYPE_QUOTE:
                                                ch = *s;
                                                ++s;
                                                mark = s;
                                                while(*s!=0 && *s != ch) ++s;
                                                if(*s!=0)
                                                {
                                                    if(OPTSET(parse_doctype))
                                                    {
                                                        a = append_attribute(cursor,1);
                                                        a->value = mark;
#ifdef PUGOPT_NONSEG
                                                        a->value_size = s - mark; //NF 19 Jan 2003.
#else
                                                        *s = 0;
#endif
                                                    }
                                                    ++s;
                                                    SKIPWS(); //Eat whitespace.
                                                    if(chartype_quote(*s)) goto LOC_DOCTYPE_QUOTE; //Another quoted section to store.
                                                    else if(chartype_symbol(*s)) goto LOC_DOCTYPE_SYMBOL; //Not wellformed, but just parse it.
                                                }
                                            }
                                            if(chartype_lbracket(*s)) //'...[...'
                                            {
                                                ++s; //Step over the bracket.
                                                if(OPTSET(parse_doctype)) cursor->value = s; //Store the offset.
                                                unsigned int bd = 1; //Bracket depth counter.
                                                while(*s!=0) //Loop till we're out of all brackets.
                                                {
                                                    if(chartype_rbracket(*s)) --bd;
                                                    else if(chartype_lbracket(*s)) ++bd;
                                                    if(bd == 0) break;
                                                    ++s;
                                                }
                                                //Note: 's' now points to end of DTD, i.e.: ']'.
                                                if(OPTSET(parse_doctype))
                                                {
                                                    //Note: If we aren't parsing the DTD ('!parse_dtd', etc.) then it is stored in the DOM as one whole chunk.
#ifdef PUGOPT_NONSEG
                                                    SETLEN(); //NF 19 Jan 2003
#else
                                                    *s = 0; //Zero-terminate.
#endif
                                                    if(OPTSET(parse_dtd)||OPTSET(parse_dtd_only))
                                                    {
                                                        if(OPTSET(parse_dtd))
                                                        {
#ifdef PUGOPT_NONSEG
                                                            TCHAR svch = *s;
                                                            try
                                                            {
                                                                *s = 0; //Zero-terminate.
                                                                parse(cursor->value,cursor,growby,optmsk); //Parse it.
                                                            }
                                                            catch(...){ assert(false); }
                                                            *s = svch;
#else
                                                            parse(cursor->value,cursor,growby,optmsk); //Parse it.
#endif
                                                        }
                                                        if(OPTSET(parse_dtd_only)) return (s+1); //Flagged to parse DTD only, so leave here.
                                                    }
                                                    else if(OPTSET(parse_trim_doctype)) //Trim whitespace.
                                                    {
#ifdef PUGOPT_NONSEG
                                                        strwtrim(&cursor->value, cursor->value_size);
#else
                                                        if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                                        else strwtrim(&cursor->value);
#endif
                                                    }
                                                    ++s; //Step over the zero.
                                                    POPNODE(); //Pop since this is a standalone.
                                                }
                                                SCANFOR(chartype_leave(*s));
                                                continue;
                                            }
                                            //Fall-through; make sure we pop.
                                            POPNODE(); //Pop since this is a standalone.
                                            continue;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else if(chartype_symbol(*s)) //An inline DTD tag.
                {
                    mark = s;
                    SCANWHILE(chartype_symbol(*s));
                    ENDSEG(); //Save char in 'ch', terminate & step over.
                    xml_node_type e = node_dtd_entity;
#ifdef PUGOPT_NONSEG
                    const unsigned int dtdilen = (s - 1) - mark;
                    if(_tcsncmp(mark,_T("ATTLIST"),max((7*sizeof(TCHAR)),dtdilen))==0) e = node_dtd_attlist;
                    else if(_tcsncmp(mark,_T("ELEMENT"),max((7*sizeof(TCHAR)),dtdilen))==0) e = node_dtd_element;
                    else if(_tcsncmp(mark,_T("NOTATION"),max((8*sizeof(TCHAR)),dtdilen))==0) e = node_dtd_notation;
#else
                    if(_tcscmp(mark,_T("ATTLIST"))==0) e = node_dtd_attlist;
                    else if(_tcscmp(mark,_T("ELEMENT"))==0) e = node_dtd_element;
                    else if(_tcscmp(mark,_T("NOTATION"))==0) e = node_dtd_notation;
#endif
                    PUSHNODE(e); //Append a new node on the tree.
                    if(*s!=0 && chartype_space(ch))
                    {
                        SKIPWS(); //Eat whitespace.
                        if(chartype_symbol(*s) || *s==_T('%'))
                        {
                            mark = s;
                            if(*s==_T('%')) //Could be '<!ENTITY % name' -or- '<!ENTITY %name'
                            {
#ifdef PUGOPT_NONSEG
                                //Note: For memory-mapped file support we need to treat 's' as read-only so we can't do '*(s-1) = _T('%');' below.
                                cursor->name = mark; //Sort out extraneous whitespace when we retrieve it. TODO: Whitespace cleanup.
#endif
                                ++s;
                                if(chartype_space(*s))
                                {
                                    SKIPWS(); //Eat whitespace.
#ifndef PUGOPT_NONSEG
                                    *(s-1) = _T('%');
                                    cursor->name = (s-1);
#endif
                                }
#ifndef PUGOPT_NONSEG
                                else cursor->name = mark;
#endif
                            }
                            else cursor->name = s;
                            SCANWHILE(chartype_symbol(*s));
#ifdef PUGOPT_NONSEG
                            cursor->name_size = s - cursor->name;
#endif
                            ENDSEG(); //Save char in 'ch', terminate & step over.
                            if(chartype_space(ch))
                            {
                                SKIPWS(); //Eat whitespace.
                                if(e == node_dtd_entity) //Special case; may have multiple quoted sections w/anything inside.
                                {
                                    cursor->value = s; //Just store everything here.
                                    bool qq = false; //Quote in/out flag.
                                    while(*s != 0) //Loop till we find the right sequence.
                                    {
                                        if(!qq && chartype_quote(*s)){ ch = *s; qq = true; }
                                        else if(qq && *s == ch) qq = false;
                                        else if(!qq && chartype_leave(*s)) //Not in quoted reqion and '>' hit.
                                        {
#ifdef PUGOPT_NONSEG
                                            SETLEN(); //NF 19 Jan 2003.
#else
                                            *s = 0;
#endif
                                            ++s;
                                            if(OPTSET(parse_trim_entity))
                                            {
#ifdef PUGOPT_NONSEG
                                                strwtrim(&cursor->value,cursor->value_size);
#else
                                                if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                                else strwtrim(&cursor->value);
#endif
                                            }
                                            POPNODE();
                                            goto LOC_SEARCH;
                                        }
                                        ++s;
                                    }
                                    if(OPTSET(parse_trim_entity))
                                    {
#ifdef PUGOPT_NONSEG
                                        strwtrim(&cursor->value, cursor->value_size);
#else
                                        if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                        else strwtrim(&cursor->value);
#endif
                                    }
                                }
                                else
                                {
                                    cursor->value = s;
                                    SCANFOR(chartype_leave(*s)); //Just look for '>'.
#ifdef PUGOPT_NONSEG
                                    SETLEN(); //NF 19 Jan 2003.
#else
                                    *s = 0;
#endif
                                    ++s;
                                    if(OPTSET(parse_trim_entity))
                                    {
#ifdef PUGOPT_NONSEG
                                        strwtrim(&cursor->value, cursor->value_size);
#else
                                        if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                                        else strwtrim(&cursor->value);
#endif
                                    }
                                    POPNODE();
                                    goto LOC_SEARCH;
                                }
                            }
                        }
                    }
                    POPNODE();
                }
            }
            else if(chartype_symbol(*s)) //'<#...'
            {
                cursor = append_node(cursor,growby); //Append a new node to the tree.
LOC_ELEMENT: //Scan for & store element name.
                cursor->name = s;
                SCANWHILE(chartype_symbol(*s)); //Scan for a terminator.
#ifdef PUGOPT_NONSEG
                cursor->name_size = s - cursor->name; //Note: Before ENDSEG().
#endif
                ENDSEG(); //Save char in 'ch', terminate & step over.
                if
                    (
                    *s!=0 &&
                    (
                    chartype_close(ch) //'</...'
#ifdef PUGOPT_NONSEG
                    //||
                    //chartype_pi(ch) //Treat '?>' as '/>' NF 19 Jan 2003
#endif
                    )
                    )
                {
                    SCANFOR(chartype_leave(*s)); //Scan for '>', stepping over the tag name.
                    POPNODE(); //Pop.
                    continue;
                }
                else if(*s!=0 && !chartype_space(ch)) goto LOC_PCDATA; //No attributes, so scan for PCDATA.
                else if(*s!=0 && chartype_space(ch))
                {
                    SKIPWS(); //Eat any whitespace.
LOC_ATTRIBUTE:
                    if(chartype_symbol(*s)) //<... #...
                    {
                        xml_attribute_struct* a = append_attribute(cursor,growby); //Make space for this attribute.
                        a->name = s; //Save the offset.
                        SCANWHILE(chartype_symbol(*s)); //Scan for a terminator.
#ifdef PUGOPT_NONSEG
                        ENDSEGNAM(a);
#else
                        ENDSEG(); //Save char in 'ch', terminate & step over.
#endif
                        if(*s!=0 && chartype_space(ch)) SKIPWS(); //Eat any whitespace.
                        if(*s!=0 && (chartype_equals(ch) || chartype_equals(*s))) //'<... #=...'
                        {
                            if(chartype_equals(*s)) ++s;
                            SKIPWS(); //Eat any whitespace.
                            if(chartype_quote(*s)) //'<... #="...'
                            {
                                ch = *s; //Save quote char to avoid breaking on "''" -or- '""'.
                                ++s; //Step over the quote.
                                a->value = s; //Save the offset.
                                SCANFOR(*s == ch); //Scan for the terminating quote, or '>'.
#ifdef PUGOPT_NONSEG
                                ENDSEGATT(a);
#else
                                ENDSEG(); //Save char in 'ch', terminate & step over.
#endif
                                if(OPTSET(parse_trim_attribute)) //Trim whitespace.
                                {
#ifdef PUGOPT_NONSEG
                                    strwtrim(&a->value,a->value_size);
#else
                                    if(OPTSET(parse_wnorm)) strwnorm(&a->value);
                                    else strwtrim(&a->value);
#endif
                                }
                                if(chartype_leave(*s)){ ++s; goto LOC_PCDATA; }
                                else if(chartype_close(*s))
                                {
                                    ++s;
                                    POPNODE();
                                    SKIPWS(); //Eat any whitespace.
                                    if(chartype_leave(*s)) ++s;
                                    goto LOC_PCDATA;
                                }
                                if(chartype_space(*s)) //This may indicate a following attribute.
                                {
                                    SKIPWS(); //Eat any whitespace.
                                    goto LOC_ATTRIBUTE; //Go scan for additional attributes.
                                }
                            }
                        }
                        if(chartype_symbol(*s)) goto LOC_ATTRIBUTE;
                        else if(*s!=0 && cursor->type == node_pi)
                        {
#ifdef PUGOPT_NONSEG
                            SCANFOR(chartype_pi(*s)); //compliments change where we don't fudge to '/>' when we find the PI. NF 20 Jan 2003
                            SKIPWS(); //Eat any whitespace.
                            if(chartype_pi(*s)) ++s;
#else
                            SCANFOR(chartype_close(*s));
                            SKIPWS(); //Eat any whitespace.
                            if(chartype_close(*s)) ++s;
#endif
                            SKIPWS(); //Eat any whitespace.
                            if(chartype_leave(*s)) ++s;
                            POPNODE();
                            goto LOC_PCDATA;
                        }
                    }
                }
LOC_LEAVE:
                if(chartype_leave(*s)) //'...>'
                {
                    ++s; //Step over the '>'.
LOC_PCDATA: //'>...<'
                    mark = s; //Save this offset while searching for a terminator.
                    SKIPWS(); //Eat whitespace if no genuine PCDATA here.
                    if(chartype_enter(*s)) //We hit a '<...', with only whitespace, so don't bother storing anything.
                    {
                        if(chartype_close(*(s+1))) //'</...'
                        {
                            SCANFOR(chartype_leave(*s)); //Scan for '>', stepping over any end-tag name.
                            POPNODE(); //Pop.
                            continue; //Continue scanning.
                        }
                        else goto LOC_SEARCH; //Expect a new element enter, so go scan for it.
                    }
                    s = mark; //We hit something other than whitespace; restore the original offset.
                    PUSHNODE(node_pcdata); //Append a new node on the tree.
                    cursor->value = s; //Save the offset.
                    SCANFOR(chartype_enter(*s)); //'...<'
#ifdef PUGOPT_NONSEG
                    ENDSEGDAT();
#else
                    ENDSEG(); //Save char in 'ch', terminate & step over.
#endif
                    if(OPTSET(parse_trim_pcdata)) //Trim whitespace.
                    {
#ifdef PUGOPT_NONSEG
                        strwtrim(&cursor->value,cursor->value_size);
#else
                        if(OPTSET(parse_wnorm)) strwnorm(&cursor->value);
                        else strwtrim(&cursor->value);
#endif
                    }
                    POPNODE(); //Pop since this is a standalone.
                    if(chartype_enter(ch)) //Did we hit a '<...'?
                    {
                        if(chartype_close(*s)) //'</...'
                        {
                            SCANFOR(chartype_leave(*s)); //'...>'
                            POPNODE(); //Pop.
                            goto LOC_LEAVE;
                        }
                        else if(chartype_special(*s)) goto LOC_CLASSIFY; //We hit a '<!...'. We must test this here if we want comments intermixed w/PCDATA.
                        else if(*s) goto LOC_CLASSIFY;
                        else return s;
                    }
                }
                //Fall-through A.
                else if(chartype_close(*s)) //'.../'
                {
                    ++s;
                    if(chartype_leave(*s)) //'.../>'
                    {
                        POPNODE(); //Pop.
                        ++s;
                        continue;
                    }
                }
            }
            //Fall-through B.
            else if(chartype_close(*s)) //'.../'
            {
                SCANFOR(chartype_leave(*s)); //'.../>'
                POPNODE(); //Pop.
                continue;
            }
        }
    }
    return s;
}


//<summary>Read data from the file at 'path' into the buffer. Free with 'free'.</summary>
//<param name="path">File path.</param>
//<param name="buffer">Pointer to pointer to string to recieve buffer.</param>
//<param name="size">Pointer to count bytes read and stored in 'buffer'.</param>
//<param name="tempsize">Temporary read buffer size.</param>
//<returns>Success if file at 'path' was opened and bytes were read into memory.</returns>
//<remarks>Memory is allocated at '*buffer'. Free with 'free'.</remarks>
inline static bool load_file(const TCHAR* path,TCHAR** buffer,unsigned long* size,unsigned long tempsize = 4096)
{
    if(!path || !buffer || !size) return false;
    *size = 0;
    *buffer = 0;
    HANDLE file_handle = CreateFile(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(file_handle == INVALID_HANDLE_VALUE) return false;
    TCHAR* temp = (TCHAR*) malloc(sizeof(TCHAR)*tempsize);
    if(!temp) return false;
    unsigned long read_bytes = 0;
    ZeroMemory(temp,sizeof(TCHAR)*tempsize);
    while(ReadFile(file_handle,(void*)temp,tempsize-1,&read_bytes,0) && read_bytes && strcatgrow(buffer,temp))
    {
        *size += read_bytes;
        ZeroMemory(temp,sizeof(TCHAR)*tempsize);
    }
    CloseHandle(file_handle);
    free(temp);
    return (*size) ? true : false;
}


//<summary>A void pointer array. Used by various xml_node::find* functions.</summary>
class pointer_array
{
//Internal Data Members
protected:
    unsigned int    _size; //Count items.
    unsigned int    _room; //Available space.
    void**          _data; //The list.
    unsigned int    _grow; //Grow by increment.
public:
    //<summary>Default constructor.</summary>
    //<param name="grow">Array growth increment.</param>
    pointer_array(unsigned int grow = 4):
        _size(0),
            _room(0),
            _data(NULL),
            _grow(grow)
        {
            _data = (void**)malloc(sizeof(void*)*_grow);
            _room = (_data) ? _grow : 0;
        }
        ~pointer_array(){ if(_data) free(_data); }
public:
    bool empty(){ return (_size == 0); } //True if there is no data in the array.
    void remove_all(){ _size = 0; } //Remove all data elements from the array.
    void clear() //Free any allocated memory.
    {
        if(_data)
        {
            _data = (void**)realloc(_data,sizeof(void*)*_grow); //Reallocate to first growth increment.
            _room = _grow; //Mark it as such.
            _size = 0; //Mark array as empty.
        }
    }
    virtual void*& operator[](unsigned int i) //Access element at subscript, or dummy value if overflow.
    {
        static void* dummy = 0;
        if(i < _size) return _data[i]; else return dummy;
    }
    unsigned int size(){ return _size; } //Count data elements in the array.
    virtual void* at(unsigned int i){ if(i < _size) return _data[i]; else return NULL; } //Access element at subscript, or NULL if overflow.
    long push_back(void* element) //Append a new element to the array.
    {
        if(_data) //Fail if no array.
        {
            if(_size < _room) //There is enough allocated space.
            {
                _data[_size] = element; //Set it.
                _size++; //Increment our count of elements.
                return _size-1; //Return the element's subscript.
            }
            else //Not enough room.
            {
                void** temp = (void**)realloc(_data,sizeof(void*)*(_size+_grow)); //Grow the array.
                if(temp) //Reallocation succeeded.
                {
                    _room += _grow; //Increment available space.
                    _data = temp; //Assign reallocated value to array pointer.
                    _data[_size] = element; //Set the element to be added.
                    _size++; //Increment our count of elements.
                    return _size-1; //Return the element's subscript.
                }
            }
        }
        return -1; //Something failed, so return a bad subscript.
    }
};


//<summary>A simple indentation stack.</summary>
//<remarks>Used by xml_node::outer_xml function.</remarks>
class indent_stack
{
//Internal Data Members
protected:
    TCHAR   _inch; //The indent character.
    TCHAR*  _stac; //The aggregate indent string (stack).
    int     _size; //Current depth (avoids using '_tcslen' on push/pop).
//Construction/Destruction
public:
    //<summary>Default constructor.</summary>
    //<param name="c">Indent character.</param>
    indent_stack(TCHAR c = _T('\t')):
        _inch(c),
            _stac(0) ,
            _size(0)
        {
            _stac = (TCHAR*)malloc(sizeof(TCHAR)); //Allocate.
            *_stac = 0; //Zero-terminate.
        }
        //Destructor.
        virtual ~indent_stack(){ if(_stac) free(_stac); }
//Stack Operators
public:
    //<summary>Grow indent string by one indent character.</summary>
    //<remarks>Reallocates the indent string.</remarks>
    void push()
    {
        if(_inch && _stac)
        {
            _size++;
            _stac = (TCHAR*)realloc(_stac,sizeof(TCHAR)*(_size+1));
            _stac[_size-1] = _inch;
            _stac[_size] = 0;
        }
    }
    //<summary>Shrink the indent string by one indent character.</summary>
    void pop()
    {
        if(_inch && _stac && _size > 0)
        {
            _size--;
            _stac = (TCHAR*)realloc(_stac,sizeof(TCHAR)*(_size+1));
            _stac[_size] = 0;
        }
    }
    //<summary>Accesses the indent depth.</summary>
    //<returns>The current indent string, or "" if empty.</returns>
    const TCHAR* depth(){ return (_inch && _stac) ? _stac : _T(""); }
};


//<summary>
//  Stream output. Recursively writes the given xml_node_struct structure to 
//  the given stream. NOTE: Use this recursive implementation for debug purposes 
//  only, since a large tree may cause a stack overflow.
//</summary>
//<param name="os">Reference to output stream.</param>
//<param name="indent">Reference to indentation stack.</param>
//<param name="node">Pointer to the node.</param>
//<param name="breaks">Use linebreaks?</param>
//<returns>
//  String data is written to stream. Indent stack may be altered.
//  If you want to make this prettier, and to avoid propagating whitespace,
//  you will have to trim excess whitespace from the PCDATA sections.
//</returns>
inline static void outer_xml(std::basic_ostream<TCHAR,std::char_traits<TCHAR> > & os,indent_stack& indent,xml_node_struct* node,bool breaks = true)
{
    if(node && os.good()) //There is a node and ostream is OK.
    {
        register unsigned int n, i;
        os << indent.depth();
        switch(node->type)
        {
        case node_dtd_attlist:
            if(node->name)
            {
#ifdef PUGOPT_NONSEG
                os << _T("<!ATTLIST ");
                os.write( node->name, node->name_size );
#else
                os << _T("<!ATTLIST ") << node->name;
#endif
                if(node->value)
#ifdef PUGOPT_NONSEG
                {
                    os << _T(" ");
                    os.write( node->value, node->value_size );
                }
#else
                    os << _T(" ") << node->value;
#endif

                os << _T(">");
            }
            break;
        case node_dtd_element:
            if(node->name)
            {
#ifdef PUGOPT_NONSEG
                os << _T("<!ELEMENT ");
                os.write( node->name, node->name_size );
                if(node->value)
                {
                    os << _T(" ");
                    os.write( node->value, node->value_size );
                }
#else
                os << _T("<!ELEMENT ") << node->name;
                if(node->value) os << _T(" ") << node->value;
#endif
                os << _T(">");
            }
            break;
        case node_dtd_entity:
            if(node->name)
            {
#ifdef PUGOPT_NONSEG
                os << _T("<!ENTITY ");
                os.write( node->name, node->name_size );
                if(node->value)
                {
                    os << _T(" ");
                    os.write( node->value, node->value_size );
                }
#else
                os << _T("<!ENTITY ") << node->name;
                if(node->value) os << _T(" ") << node->value;
#endif
                os << _T(">");
            }
            break;
        case node_dtd_notation:
            if(node->name)
            {
#ifdef PUGOPT_NONSEG
                os << _T("<!NOTATION ");
                os.write( node->name, node->name_size );
                if(node->value)
                {
                    os << _T(" ");
                    os.write( node->value, node->value_size );
                }
#else
                os << _T("<!NOTATION ") << node->name;
                if(node->value) os << _T(" ") << node->value;
#endif
                os << _T(">");
            }
            break;
        case node_doctype:
            os << _T("<!DOCTYPE");
            n = node->attributes;
            for(i=0; i<n; ++i)
            {
                os << _T(" ");
                if(node->attribute[i]->name)
#ifdef PUGOPT_NONSEG
                    os.write( node->attribute[i]->name, node->attribute[i]->name_size );
#else
                    os << node->attribute[i]->name;
#endif
                else if(node->attribute[i]->value)
#ifdef PUGOPT_NONSEG
                {
                    os << _T("\"");
                    os.write( node->attribute[i]->value, node->attribute[i]->value_size );
                    os << _T("\"");
                }
#else
                    os << _T("\"") << node->attribute[i]->value << _T("\"");
#endif
            }
            if(node->children)
            {
                if(breaks) os << std::endl;
                else os << _T(" ");
                os << _T("[");
                if(breaks) os << std::endl;
                else os << _T(" ");
                n = node->children;
                indent.push(); //Push the indent stack.
                for(i=0; i<n; ++i)
                {
                    if
                    (
                        node->child[i] && //There is a child at i.
                        (
                            node->child[i]->type == node_dtd_attlist    || //Skip all other types.
                            node->child[i]->type == node_dtd_element    ||
                            node->child[i]->type == node_dtd_entity     ||
                            node->child[i]->type == node_dtd_notation
                        )
                    )
                        outer_xml(os,indent,node->child[i],breaks);
                }
                indent.pop(); //Pop the indent stack.
                os << _T("]");
            }
            else if(node->value)
#ifdef PUGOPT_NONSEG
            {
                os << _T(" [");
                os.write(node->value,node->value_size);
                os << _T("]");
            }
#else
                os << _T(" [") << node->value << _T("]");
#endif
            os << _T(">");
            break;
        case node_pcdata:
#ifdef PUGOPT_NONSEG
            if(node->value) os.write(node->value,node->value_size);
#else
            if(node->value) os << node->value;
#endif
            break;
        case node_cdata:
#ifdef PUGOPT_NONSEG
            if(node->value)
            {
                os << _T("<![CDATA[");
                os.write(node->value,node->value_size);
                os << _T("]]>");
            }
#else
            if(node->value) os << _T("<![CDATA[") << node->value << _T("]]>");
#endif
            break;
        case node_include:
#ifdef PUGOPT_NONSEG
            if(node->value)
            {
                os << _T("<![INCLUDE[");
                os.write(node->value, node->value_size);
                os << _T("]]>");
            }
#else
            if(node->value) os << _T("<![INCLUDE[") << node->value << _T("]]>");
#endif
            break;
        case node_comment:
#ifdef PUGOPT_NONSEG
            if(node->value)
            {
                os << _T("<!--");
                os.write(node->value, node->value_size);
                os << _T("-->");
            }
#else
            if(node->value) os << _T("<!--") << node->value << _T("-->");
#endif
            break;
        case node_element:
        case node_pi:
            os << _T("<");
            if(node->type==node_pi) os << _T("?");
            if(node->name)
#ifdef PUGOPT_NONSEG
                os.write(node->name,node->name_size);
#else
                os << node->name;
#endif
            else os << _T("anonymous");
            n = node->attributes;
            for(i=0; i<n; ++i)
            {
                if(node->attribute[i] && node->attribute[i]->name)
                {
#ifdef PUGOPT_NONSEG
                    os << _T(" ");
                    os.write(node->attribute[i]->name,node->attribute[i]->name_size);
                    if(node->attribute[i]->value)
                    {
                        os << _T("=\"");
                        os.write(node->attribute[i]->value,node->attribute[i]->value_size);
                        os << _T("\"");
                    }
#else
                    os << _T(" ") << node->attribute[i]->name;
                    if(node->attribute[i]->value) os << _T("=\"") << node->attribute[i]->value << _T("\"");
#endif
                }
            }
            n = node->children;
            if(n && node->type == node_element)
            {
                os << _T(">");
                if(n == 1 && node->child[0]->type == node_pcdata)
                {
                    if(node->child[0] && node->child[0]->value)
#ifdef PUGOPT_NONSEG
                        os.write(node->child[0]->value,node->child[0]->value_size);
#else
                        os << node->child[0]->value;
#endif
                }
                else
                {
                    if(breaks) os << std::endl;
                    indent.push();
                    for(i=0; i<n; ++i) pug::outer_xml(os,indent,node->child[i],breaks);
                    indent.pop();
                    os << indent.depth();
                }
                os << _T("</");
#ifdef PUGOPT_NONSEG
                if(node->name)
                    os.write(node->name, node->name_size);
#else
                if(node->name) os << node->name;
#endif
                os << _T(">");
            }
            else
            {
                if(node->type==node_pi) os << _T("?>");
                else os << _T("/>");
            }
            break;
        default: break;
        }
        if(breaks) os << std::endl;
        os.flush();
    }
}

#if _MSC_VER >= 1600
template<class _Category,
class _Ty,
class _Diff = ptrdiff_t,
class _Pointer = _Ty *,
class _Reference = _Ty&,
class _Base_class = std::_Iterator_base>
struct _Iterator_with_base
: public _Base_class

{ // base type for all iterator classes
typedef _Category iterator_category;
typedef _Ty value_type;
typedef _Diff difference_type;
typedef _Diff distance_type; // retained
typedef _Pointer pointer;
typedef _Reference reference;
};

template<class _Ty,
class _Diff,
class _Pointer,
class _Reference>
struct _Ranit
: public _Iterator_with_base<std::random_access_iterator_tag,
_Ty, _Diff, _Pointer, _Reference>
{ // base type for container random-access iterator classes
};
#endif

//<summary>Abstract iterator class for interating over a node's members.</summary>
//<remarks>Used as base class for 'xml_node_iterator' and 'xml_attribute_iterator'.</remarks>
#if _MSC_VER < 1300
template <class _Ty,class _D>
class xml_iterator : public std::_Ranit<_Ty,_D>
#else
template <class _Ty,class _Diff,class _Pointer,class _Reference>
#if _MSC_VER >= 1600
class xml_iterator : public _Ranit<_Ty,_Diff,_Pointer,_Reference>
#else
class xml_iterator : public std::_Ranit<_Ty,_Diff,_Pointer,_Reference>
#endif
#endif
{
protected:
    xml_node_struct* _vref; //A pointer to the node over which to iterate.
    long _sscr; //Current subscript of element.
public:
    xml_iterator() : _vref(0), _sscr(-1) {} //Default constructor.
    xml_iterator(xml_node_struct* vref,long sscr = 0) : _vref(vref), _sscr(sscr){ } //Initializing constructor.
    xml_iterator(const xml_iterator& r) : _vref(r._vref), _sscr(r._sscr){ } //Copy constructor.
    virtual ~xml_iterator(){} //Destructor.
public:
    virtual bool good() = 0; //Internal validity of '_vref'.
    virtual bool oob() = 0; //Out of bounds check for '_sscr' with respect to '_vref'. Returns true if '_sscr' is O.O.B.
public:
    virtual long subscript(){ return _sscr; } //Get subscript value;
    virtual void subscript(long new_subscript){ _sscr = new_subscript; } //Set subscript value;
public:
    virtual xml_iterator& operator=(const xml_iterator& rhs){ _vref = rhs._vref; _sscr = rhs._sscr; return *this; } //Assignment.
    virtual bool operator==(const xml_iterator& rhs){ return (_sscr == rhs._sscr); } //True if this is equal to RHS.
    virtual bool operator!=(const xml_iterator& rhs){ return (_sscr != rhs._sscr); } //True if this is not equal to RHS.
    virtual bool operator<(const xml_iterator& rhs){ return (_sscr < rhs._sscr); } //True if this subscript is less than RHS.
    virtual bool operator>(const xml_iterator& rhs){ return (_sscr > rhs._sscr); } //True if this subscript is greater than RHS.
    virtual bool operator<=(const xml_iterator& rhs){ return (_sscr <= rhs._sscr); } //True if this subscript is less than or equal to RHS.
    virtual bool operator>=(const xml_iterator& rhs){ return (_sscr >= rhs._sscr); } //True if this subscript is greater than or equal to RHS.
    virtual xml_iterator& operator++(){ _sscr++; return *this; } //Increment the iterator (subscript).
    virtual xml_iterator& operator--(){ _sscr--; return *this; } //Decrement the iterator (subscript).
    virtual _Ty& operator*() = 0; //Dereference operator.
    virtual _Ty* operator->() = 0;
};

class xml_node; //Forward decl.


//<summary>Abstract tree walker class for xml_node::traverse().</summary>
class xml_tree_walker
{
protected:
    long _deep; //Current node depth.
public:
    xml_tree_walker() : _deep(0) {} //Default constructor.
    virtual ~xml_tree_walker(){} //Destructor.
public:
    virtual void push(){ ++_deep; } //Increment node depth.
    virtual void pop(){ --_deep; } //Decrement node depth.
    virtual long depth(){ return (_deep > 0) ? _deep : 0; } //Access node depth.
public:
    //<summary>Callback when traverse on a given root node begins.</summary>
    //<returns>Returning false will abort the traversal.</returns>
    //<remarks>Override this to implement your own custom behavior.</remarks>
    virtual bool begin(xml_node&){ return true; }
    //<summary>Callback for each node that is hit on traverse.</summary>
    //<returns>Returning false will abort the traversal.</returns>
    virtual bool for_each(xml_node&) = 0;
    //<summary>Callback when traverse on a given root node ends.</summary>
    //<returns>Returning false will abort the traversal.</returns>
    //<remarks>Override this to implement your own custom behavior.</remarks>
    virtual bool end(xml_node&){ return true; }
};


//<summary>Provides a light-weight wrapper for manipulating xml_attribute_struct structures.</summary>
//<remarks>
//  Note: xml_attribute does not create any memory for the attribute it wraps; 
//  it only wraps a pointer to an existing xml_attribute_struct.
//</remarks>
class xml_attribute
{
//Internal Data Members
protected:
    xml_attribute_struct* _attr; //The internal attribute pointer.
//Construction/Destruction
public:
    xml_attribute() : _attr(NULL) {} //Default constructor.
    xml_attribute(xml_attribute_struct* attr) : _attr(attr) {} //Initializing constructor.
    xml_attribute(const xml_attribute& r) : _attr(r._attr) {} //Copy constructor.
    virtual ~xml_attribute(){} //Destructor.
//Operators
public:
    void attach(xml_attribute_struct* v){ _attr = v; }
    xml_attribute& operator=(const xml_attribute& r){ _attr = r._attr; return *this; } //Assign internal pointer.
    bool operator==(const xml_attribute& r){ return (_attr == r._attr); } //Compare internal pointer.
    bool operator!=(const xml_attribute& r){ return (_attr != r._attr); }
    operator xml_attribute_struct*(){ return _attr; }
    //<summary>Cast attribute value as std::string. If not found, return empty.</summary>
    //<returns>The std::string attribute value, or empty.</returns>
    //<remarks>Note: Modifying this will not change the value, e.g. read only.</remarks>
    /*operator std::string()
    {
        std::string temp;
        if(!empty() && has_value())
        {
#ifdef PUGOPT_NONSEG
            temp.append(_attr->value,_attr->value_size);
#else
            temp = _attr->value;
#endif
        }
        return temp;
    }*/
    //<summary>Cast attribute value as integral character string. If not found, return NULL.</summary>
    //<returns>Integral character string attribute value, or NULL.</returns>
    //<remarks>Warning: Modifying this may corrupt portions of the document tree.</remarks>
    operator const TCHAR*()
    {
        if(empty() || !has_value()) return NULL;
        return _attr->value;
    }
    //<summary>Cast attribute value as long. If not found, return 0.</summary>
    //<returns>Attribute value as long, or 0.</returns>
    //<remarks>Note: Modifying this will not change the value, e.g. read only.</remarks>
    operator long()
    {
        if(empty() || !has_value()) return 0;
#ifdef PUGOPT_NONSEG
        TCHAR temp[PUGDEF_ATTR_VALU_SIZE];
        unsigned int valulen = sizeof(temp)-1;
        const unsigned int maxlen = valulen ? min(valulen,_attr->value_size) : _attr->value_size;
        _tcsncpy(temp,_attr->value,maxlen);
        temp[maxlen] = 0;
        return _tcstol(temp,NULL,10);
#else
        return _tcstol(_attr->value,NULL,10);
#endif
    }
    //<summary>Cast attribute value as double. If not found, return 0.0.</summary>
    //<returns>Attribute value as double, or 0.0.</returns>
    //<remarks>Note: Modifying this will not change the value, e.g. read only.</remarks>
    operator double()
    {
        if(empty() || !has_value()) return 0.0;
#ifdef PUGOPT_NONSEG
        TCHAR temp[PUGDEF_ATTR_VALU_SIZE];
        unsigned int valulen = sizeof(temp)-1;
        const unsigned int maxlen = valulen ? min(valulen,_attr->value_size) : _attr->value_size;
        _tcsncpy(temp,_attr->value,maxlen);
        temp[maxlen] = 0;
        return _tcstod(temp,0);
#else
        return _tcstod(_attr->value,0);
#endif
    }
    //<summary>Cast attribute value as bool. If not found, return false.</summary>
    //<returns>Attribute value as bool, or false.</returns>
    //<remarks>Note: Modifying this will not change the value, e.g. read only.</remarks>
    operator bool()
    {
        if(empty() || !has_value()) return false;
        if(*(_attr->value))
        {
            return //Only look at first char:
            (
                *(_attr->value) == _T('1') || //1*
                *(_attr->value) == _T('t') || //t* (true)
                *(_attr->value) == _T('T') || //T* (True|TRUE)
                *(_attr->value) == _T('y') || //y* (yes)
                *(_attr->value) == _T('Y') //Y* (Yes|YES)
            )
                ? true : false; //Return true if matches above, else false.
        }
    }
    //<summary>Set attribute to std::string.</summary>
    //<param name="rhs">Value std::string to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    //xml_attribute& operator=(const std::string& rhs){ value(rhs.c_str()); return *this; }
    //<summary>Set attribute to string.</summary>
    //<param name="rhs">Value string to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator=(const TCHAR* rhs){ if(rhs) value(rhs); return *this; }
    //<summary>Set attribute to long.</summary>
    //<param name="rhs">Value long to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator=(long rhs)
    {
        TCHAR temp[32] = {0};
        _stprintf(temp,_T("%ld"),rhs);
        value(temp);
        return *this;
    }
    //<summary>Set attribute to double.</summary>
    //<param name="rhs">Value double to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator=(double rhs)
    {
        TCHAR temp[32] = {0};
        _stprintf(temp,_T("%lf"),rhs);
        value(temp);
        return *this;
    }
    //<summary>Set attribute to bool.</summary>
    //<param name="rhs">Value bool to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator=(bool rhs)
    {
        value(rhs?_T("true"):_T("false"));
        return *this;
    }
    //<summary>Right-shift attribute value to std::string.</summary>
    //<param name="rhs">Reference to std::string to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    /*xml_attribute& operator>>(std::string& rhs)
    {
#ifdef PUGOPT_NONSEG
        rhs.clear();
        rhs.append(_attr->value,_attr->value_size);
#else
        rhs = value();
#endif
        return *this;
    }*/
    //<summary>Right-shift attribute value to long.</summary>
    //<param name="rhs">Reference to long to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator>>(long& rhs){ rhs = (long)*this; return *this; }
    //<summary>Right-shift attribute value to double.</summary>
    //<param name="rhs">Reference to double to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator>>(double& rhs){ rhs = (double)*this; return *this; }
    //<summary>Right-shift attribute value to bool.</summary>
    //<param name="rhs">Reference to bool to set.</param>
    //<returns>Reference to xml_attribute.</returns>
    xml_attribute& operator>>(bool& rhs){ rhs = (bool)*this; return *this; }
    //<summary>Left-shift attribute value to long.</summary>
    //<param name="lhs">Reference to long to set.</param>
    //<param name="rhs">Reference to xml_attribute to read.</param>
    //<returns>Reference to long.</returns>
    friend long& operator<<(long& lhs,xml_attribute& rhs){ lhs = (long)rhs; return lhs; }
    //<summary>Left-shift attribute value to double.</summary>
    //<param name="lhs">Reference to double to set.</param>
    //<param name="rhs">Reference to xml_attribute to read.</param>
    //<returns>Reference to double.</returns>
    friend double& operator<<(double& lhs,xml_attribute& rhs){ lhs = (double)rhs; return lhs; }
    //<summary>Left-shift attribute value to bool.</summary>
    //<param name="lhs">Reference to bool to set.</param>
    //<param name="rhs">Reference to xml_attribute to read.</param>
    //<returns>Reference to bool.</returns>
    friend bool& operator<<(bool& lhs,xml_attribute& rhs){ lhs = (bool)rhs; return lhs; }
    //<summary>Left-shift long to attribute value.</summary>
    //<param name="lhs">Reference to xml_attribute to set.</param>
    //<param name="rhs">Reference to long to read.</param>
    //<returns>Reference to xml_attribute.</returns>
    friend xml_attribute& operator<<(xml_attribute& lhs,const long rhs){ lhs = rhs; return lhs; }
    //<summary>Left-shift double to attribute value.</summary>
    //<param name="lhs">Reference to xml_attribute to set.</param>
    //<param name="rhs">Reference to double to read.</param>
    //<returns>Reference to xml_attribute.</returns>
    friend xml_attribute& operator<<(xml_attribute& lhs,const double& rhs){ lhs = rhs; return lhs; }
    //<summary>Left-shift bool to attribute value.</summary>
    //<param name="lhs">Reference to xml_attribute to set.</param>
    //<param name="rhs">Reference to bool to read.</param>
    //<returns>Reference to xml_attribute.</returns>
    friend xml_attribute& operator<<(xml_attribute& lhs,const bool& rhs){ lhs = rhs; return lhs; }
public:
    bool empty(){ return (_attr == NULL); } //True if the internal xml_attribute_struct pointer is NULL.
    bool has_name(){ return (!empty() && _attr->name); } //True if the attribute has a name.
    bool has_value(){ return (!empty() && _attr->value); } //True if the attribute has a value.
#ifdef PUGOPT_NONSEG
    bool has_name(const TCHAR* name) { return (name && !empty() && has_name() && _tcsncmp(_attr->name,name,_attr->name_size)==0); } //Is named 'name'.
    bool has_value(const TCHAR* value) { return (value && !empty() && has_value() && _tcsncmp(_attr->value,value,_attr->value_size)==0); } //Has value 'value'.
#else
    bool has_name(const TCHAR* name) { return (name && !empty() && has_name() && _tcscmp(_attr->name,name)==0); } //Is named 'name'.
    bool has_value(const TCHAR* value) { return (value && !empty() && has_value() && _tcscmp(_attr->value,value)==0); } //Has value 'value'.
#endif
public:
    const TCHAR* name(){ return (!empty() && _attr->name) ? _attr->name : _T(""); } //Access the attribute name.
#ifdef PUGOPT_NONSEG
    const unsigned int name_size(){ return (!empty()) ? _attr->name_size : 0; } //Access the attribute name length (for PUGOPT_NONSEG).
#endif
    bool name(TCHAR* new_name) //Set the attribute name.
    {
        if(!empty() && new_name)
#ifdef PUGOPT_NONSEG
            return strcpyinsitu(&_attr->name,new_name,&_attr->name_insitu,_attr->name_size);
#else
            return strcpyinsitu(&_attr->name,new_name,&_attr->name_insitu);
#endif
        return false;
    }
    const TCHAR* value(){ return (!empty()) ? _attr->value : _T(""); } //Access the attribute value.
#ifdef PUGOPT_NONSEG
    const unsigned int value_size(){ return (!empty()) ? _attr->value_size : 0; } //Access the attribute name length (for PUGOPT_NONSEG).
#endif
    bool value(const TCHAR* new_value) //Set the attribute value.
    {
        if(!empty() && new_value)
#ifdef PUGOPT_NONSEG
            return strcpyinsitu(&_attr->value,new_value,&_attr->value_insitu,_attr->value_size);
#else
            return strcpyinsitu(&_attr->value,new_value,&_attr->value_insitu);
#endif
        return false;
    }
};


class xml_node; //Forward declaration.


//<summary>Forward wrapper for any as-yet undefined class.</summary>
//<remarks>
//  Used by xml_node_iterator, and xml_attribute_iterator to assist with
//  operator->(), and operator*() mapping to xml_node and xml_attribute
//  types.
//</remarks>
template <typename TYPE> class forward_class 
{
protected:
    TYPE* _obj; //The class, internal.
public:
    forward_class() : _obj(NULL) { _obj = new TYPE(); } //Default constructor.
    forward_class(const TYPE& r) : _obj(NULL) { _obj = new TYPE(r); } //Copy constructor.
    virtual ~forward_class(){ if(_obj) delete _obj; } //Destructor.
public:
    TYPE& operator* (){ return *_obj; } //Dereference to the class.
    TYPE* operator->(){ return _obj; }  //Class member selection.
    operator TYPE (){ return *_obj; }   //Cast as class type.
    operator TYPE&(){ return *_obj; }   //Cast as class type reference.
    operator TYPE*(){ return _obj; }    //Cast as class type pointer.
};


//<summary>Provides a light-weight wrapper for manipulating xml_node_struct structures.</summary>
class xml_node
{
//Internal Data Members
protected:

    xml_node_struct* _root; //Pointer to node root.
    xml_node_struct _dummy; //Utility.

//Construction/Destruction
public:

    //<summary>Default constructor.</summary>
    //<remarks>
    //  Node root points to a dummy 'xml_node_struct' structure. Test for this 
    //  with 'empty'.
    //</remarks>
    xml_node(): _root(0)
    {
        memset(&_dummy,0,sizeof(xml_node_struct));
        _dummy.type = node_null;
        _dummy.parent = &_dummy;
        _root = &_dummy;
    }

    //<summary>Construct, wrapping the given 'xml_node_struct' pointer.</summary>
    //<param name="p">Pointer to node to wrap.</param>
    //<remarks>It is possible that 'p' is NULL, so test for this with 'empty'.</remarks>
    xml_node(xml_node_struct* p): _root(p) { memset(&_dummy,0,sizeof(xml_node_struct)); }

    //<summary>Copy constructor.</summary>
    //<param name="r">Reference to node.</param>
    //<remarks>
    //  Only the root pointer is assigned, so both classes now in fact point 
    //  to the same structure.
    //</remarks>
    xml_node(const xml_node& r): _root(r._root) {}

    //<summary>Destructor.</summary>
    virtual ~xml_node(){}
    
    //<summary>Attach to the given structure.</summary>
    //<param name="p">Pointer to node structure to wrap.</param>
    //<returns>Pointer to previous node structure.</returns>
    xml_node_struct* attach(xml_node_struct* p)
    {
        xml_node_struct* prev = _root;
        _root = p;
        return prev;
    }

//Iteration
public:

    //<summary>Child node iterator.</summary>
#if _MSC_VER < 1300
    class xml_node_iterator : public xml_iterator<xml_node,long>
#else
    class xml_node_iterator : public xml_iterator<xml_node,long,xml_node*,xml_node&>
#endif
    {
    protected:
        forward_class<xml_node> _wrap; //Wrapper for xml_node.
    public:
#if _MSC_VER < 1300
        xml_node_iterator() : _wrap(), xml_iterator<xml_node,long>() {} //Default constructor.
        xml_node_iterator(xml_node_struct* vref,long sscr = 0) : _wrap(), xml_iterator<xml_node,long>(vref,sscr) { } //Initializing constructor.
        xml_node_iterator(const xml_node_iterator& r) : _wrap(), xml_iterator<xml_node,long>(r) { } //Copy constructor.
#else
        xml_node_iterator() : _wrap(), xml_iterator<xml_node,long,xml_node*,xml_node&>() {} //Default constructor.
        xml_node_iterator(xml_node_struct* vref,long sscr = 0) : _wrap(), xml_iterator<xml_node,long,xml_node*,xml_node&>(vref,sscr) { } //Initializing constructor.
        xml_node_iterator(const xml_node_iterator& r) : _wrap(), xml_iterator<xml_node,long,xml_node*,xml_node&>(r) { } //Copy constructor.
#endif
        virtual bool good() //Internal validity.
        {
            if
            (
                _vref != 0          && //Pointing to some node.
                _vref->child != 0   && //The node has an array of children.
                _vref->children > 0    //There are 1 or more children in the array.
            )
                return true;
            return false;
        }
        virtual bool oob() //Out of bounds check.
        {
            if
            (
                !good()     || //There is no data over which to iterate.
                _sscr < 0   || //Subscript is out of range.
                _sscr >= (long)_vref->children
            )
                return true;
            return false;
        }
        //<summary>Pointer dereference for current xml_node.<summary>
        //<returns>
        //  Reference to the internal xml_node object, which wraps the 
        //  xml_node_struct corresponding to the node at the
        //  current subscript.
        //</returns>
        virtual xml_node& operator*()
        {
            if(!oob()) _wrap->attach(_vref->child[_sscr]);
            else _wrap->attach(NULL);
            return (xml_node&)_wrap;
        }
        virtual xml_node* operator->() //Member selection for current xml_node.
        {
            if(!oob()) _wrap->attach(_vref->child[_sscr]);
            else _wrap->attach(NULL);
            return (xml_node*)_wrap;
        }
    };

    //<summary>Attribute iterator.</summary>
#if _MSC_VER < 1300
    class xml_attribute_iterator : public xml_iterator<xml_attribute,long>
#else
    class xml_attribute_iterator : public xml_iterator<xml_attribute,long,xml_attribute*,xml_attribute&>
#endif
    {
    protected:
        forward_class<xml_attribute> _wrap;
    public:
#if _MSC_VER < 1300
        xml_attribute_iterator() : _wrap(), xml_iterator<xml_attribute,long>() {} //Default constructor.
        xml_attribute_iterator(xml_node_struct* vref,long sscr = 0) : _wrap(), xml_iterator<xml_attribute,long>(vref,sscr) { } //Initializing constructor.
        xml_attribute_iterator(const xml_attribute_iterator& r) : _wrap(), xml_iterator<xml_attribute,long>(r) { } //Copy constructor.
#else
        xml_attribute_iterator() : _wrap(), xml_iterator<xml_attribute,long,xml_attribute*,xml_attribute&>() {} //Default constructor.
        xml_attribute_iterator(xml_node_struct* vref,long sscr = 0) : _wrap(), xml_iterator<xml_attribute,long,xml_attribute*,xml_attribute&>(vref,sscr) { } //Initializing constructor.
        xml_attribute_iterator(const xml_attribute_iterator& r) : _wrap(), xml_iterator<xml_attribute,long,xml_attribute*,xml_attribute&>(r) { } //Copy constructor.
#endif
        virtual bool good() //Internal validity check.
        {
            if
            (
                _vref != 0              && //Pointing to some node.
                _vref->attribute != 0   && //The node has an array of children.
                _vref->attributes > 0      //There are 1 or more children in the array.
            )
                return true;
            return false;
        }
        virtual bool oob() //Out of bounds check.
        {
            if
            (
                !good()     || //There is no data over which to iterate.
                _sscr < 0   || //Subscript is out of range.
                _sscr >= (long)_vref->attributes //For 'end'
            )
                return true;
            return false;
        }
        //<summary>Pointer dereference for current xml_attribute.</summary>
        //<returns>
        //  Reference to the internal xml_attribute object, which wraps the 
        //  xml_attribute_struct corresponding to the attribute at the
        //  current subscript.
        //</returns>
        virtual xml_attribute& operator*() 
        {
            if(!oob()) _wrap->attach(_vref->attribute[_sscr]);
            else _wrap->attach(NULL);
            return (xml_attribute&)_wrap;
        }
        //<summary>Member selection for current xml_attribute.</summary>
        //<returns></returns>
        virtual xml_attribute* operator->()
        {
            if(!oob()) _wrap->attach(_vref->attribute[_sscr]);
            else _wrap->attach(NULL);
            return (xml_attribute*)_wrap;
        }
    };

    //<summary>Base iterator type (for child nodes). Same as 'child_iterator'.</summary>
    typedef xml_node_iterator iterator;
    //<summary>Base iterator type (for child nodes). Same as 'iterator'.</summary>
    typedef xml_node_iterator child_iterator;
    //<summary>Base iterator type (for sibling nodes). Same as 'iterator'.</summary>
    typedef xml_node_iterator sibling_iterator;
    //<summary>Attribute iterator type.</summary>
    typedef xml_attribute_iterator attribute_iterator;

    //<summary>Access the begin iterator for this node's collection of child nodes.</summary>
    //<returns>The begin iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'children_begin'.</remarks>
    iterator begin(){ return iterator(_root,0); }
    //<summary>Access the end iterator for this node's collection of child nodes.</summary>
    //<returns>The end iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'children_end'.</remarks>
    iterator end(){ return iterator(_root,_root->children); }
    //<summary>Erase the given node from node's collection of child nodes.</summary>
    //<returns>The begin iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'children_erase'.</remarks>
    iterator erase(iterator where){ remove_child((unsigned int)where.subscript()); return iterator(_root,0); }
    
    //<summary>Access the begin iterator for this node's collection of child nodes.</summary>
    //<returns>The begin iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'begin'.</remarks>
    child_iterator children_begin(){ return child_iterator(_root,0); }
    //<summary>Access the end iterator for this node's collection of child nodes.</summary>
    //<returns>The end iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'end'.</remarks>
    child_iterator children_end(){ return child_iterator(_root,_root->children); }
    //<summary>Erase the given node from node's collection of child nodes.</summary>
    //<returns>The begin iterator for this node's collection of child nodes.</returns>
    //<remarks>Same as 'erase'.</remarks>
    child_iterator children_erase(child_iterator where){ remove_child((unsigned int)where.subscript()); return child_iterator(_root,0); }
    
    //<summary>Access the begin iterator for this node's collection of attributes.</summary>
    //<returns>The begin iterator for this node's collection of attributes.</returns>
    attribute_iterator attributes_begin(){ return attribute_iterator(_root,0); }
    //<summary>Access the end iterator for this node's collection of attributes.</summary>
    //<returns>The end iterator for this node's collection of attributes.</returns>
    attribute_iterator attributes_end(){ return attribute_iterator(_root,_root->attributes); }
    //<summary>Erase the given attribute from node's collection of attributes.</summary>
    //<returns>The begin iterator for this node's collection of attributes.</returns>
    attribute_iterator attributes_erase(attribute_iterator where){ remove_attribute((unsigned int)where.subscript()); return attribute_iterator(_root,0); }

    //<summary>Access the begin iterator for this node's collection of siblings.</summary>
    //<returns>The begin iterator for this node's collection of siblings.</returns>
    sibling_iterator siblings_begin(){ if(!empty()) return sibling_iterator(_root->parent,0); return sibling_iterator(); }
    //<summary>Access the end iterator for this node's collection of siblings.</summary>
    //<returns>The end iterator for this node's collection of siblings.</returns>
    sibling_iterator siblings_end(){ if(!empty()) return sibling_iterator(_root->parent,_root->parent->children); return sibling_iterator(); }
    //<summary>Erase the given sibling from node's collection of siblings.</summary>
    //<returns>The begin iterator for this node's collection of siblings.</returns>
    sibling_iterator siblings_erase(sibling_iterator where){ parent().remove_child((unsigned int)where.subscript()); return iterator(_root->parent,0); }

//Overloaded Operators
public:

    operator xml_node_struct*(){ return _root; } //Cast as xml_node_struct pointer.
    operator void*(){ return (void*)_root; } //Cast root as void*.
    xml_node& operator=(const xml_node& r){ _root = r._root; return *this; } //Assign to xml_node_struct pointer.
    bool operator==(const xml_node& r){ return (_root == r._root); } //True if this has the same internal xml_node_struct pointer value.
    xml_node operator[](unsigned int i){ return child(i); } //Access the child at subscript.

//Node Classification
public:

    bool empty() { return (_root == 0 || _root->type == node_null); } //Node pointer is null, or type is node_null. Same as type_null.
    bool type_null() { return empty(); } //Node pointer is null, or type is node_null. Same as empty.
    bool type_document() { return (_root && _root == _root->parent && _root->type == node_document); } //Node is tree root.
    bool type_element() { return (_root && _root->type == node_element); } //Node is an element.
    bool type_comment() { return (_root && _root->type == node_comment); } //Node is a comment.
    bool type_pcdata() { return (_root && _root->type == node_pcdata); } //Node is PCDATA.
    bool type_cdata() { return (_root && _root->type == node_cdata); } //Node is CDATA.
    bool type_include() { return (_root && _root->type == node_include); } //Node is INCLUDE.
    bool type_pi() { return (_root && _root->type == node_pi); } //Node is a processing instruction.
    bool type_doctype() { return (_root && _root->type == node_doctype); } //Node is DOCTYPE.
    bool type_dtd_item() { return (_root && _root->type > node_doctype); } //Node is NODE_DTD_*.
    bool type_dtd_attlist() { return (_root && _root->type == node_dtd_attlist); } //Node is node_dtd_attlist.
    bool type_dtd_element() { return (_root && _root->type == node_dtd_element); } //Node is node_dtd_element.
    bool type_dtd_entity() { return (_root && _root->type == node_dtd_entity); } //Node is node_dtd_entity.
    bool type_dtd_notation() { return (_root && _root->type == node_dtd_notation); } //Node is node_dtd_notation.

//Member Inventory
public:

    bool has_value() { return (!empty() && _root->value != 0); } //Node has data (comment, CDATA or PCDATA).
    bool has_child_nodes() { return (!empty() && children() > 0); } //Node has 1 or more children.
    bool has_attributes() { return (!empty() && attributes() > 0); } //Node has 1 or more attributes.
    bool has_siblings() { return (!empty() && siblings() > 0); } //Node has one or more siblings.
    bool has_name() { return (!empty() && _root->name != 0); } //Node has a name.
    //bool has_name(const std::string& name) { return has_name(name.c_str()); } //Node is named 'name'.
    bool has_attribute(const std::string& name) { return has_attribute(name.c_str()); } //Node has an attribute named 'name'.
#ifdef PUGOPT_NONSEG
    bool has_name(const TCHAR* name) const { return (name && _root && _root->name && _tcsncmp(_root->name,name,_root->name_size)==0); } //Node is named 'name'.
#else
    bool has_name(const TCHAR* name) const { return (name && _root && _root->name && strcmpwild(name,_root->name)==0); } //Node is named 'name'.
#endif
    bool has_attribute(const TCHAR* name){ return (mapto_attribute_idx(name) > -1); } //Node has an attribute named name.

//Member Accessors
public:

#ifdef PUGOPT_NONSEG

    //<summary>Access node name if any.</summary>
    //<returns>Name, or dummy value if the no name.</returns>
    //<remarks>Only returns up to 'PUGDEF_ELEM_NAME_SIZE' chars of name.</remarks>
    const TCHAR* name()
    {
        static TCHAR temp[PUGDEF_ELEM_NAME_SIZE] = {0};
        if(has_name())
        {
            _tcsncpy(temp,_root->name,_root->name_size);
            temp[_root->name_size<PUGDEF_ELEM_NAME_SIZE?_root->name_size:(PUGDEF_ELEM_NAME_SIZE-1)] = 0;
            return temp;
        }
        return _T("");
    }
    unsigned int name_size(){ return (has_name()) ? _root->name_size : 0; } //Get node name length if any, else 0.
    unsigned int value_size(){ return (has_value()) ? _root->value_size : 0; } //Get node value length if any, else 0.
    inline bool matches_attribute_name(const TCHAR* name,const unsigned int namelen,const int i) const { return (_tcsncmp(name,_root->attribute[i]->name,max(namelen,_root->attribute[i]->name_size))==0); } //There is an attribute at 'i' named 'name'.
    inline bool matches_child_name(const TCHAR* name,const unsigned int namelen,const int i) const { return (_tcsncmp(name,_root->child[i]->name,max(namelen,_root->child[i]->name_size))==0); } //There is a child at 'i' named 'name'.
    inline bool matches_name(const TCHAR* name,const unsigned int namelen,xml_node_struct* node) const { return (_tcsncmp(name,node->name,max(namelen,node->name_size))==0); } //This is named 'name'.
    inline bool matches_value(const TCHAR* data,const unsigned int datalen,xml_node_struct* node) const { return (_tcsncmp(data,node->value,max(datalen,node->value_size))==0); } //This is valued 'value'.
    inline bool matches_attribute_name(const TCHAR* name,const unsigned int namelen,xml_attribute_struct* attr) const { return (_tcsncmp(name,attr->name,max(namelen,attr->name_size))==0); } //The given attribute is named 'name'.
    inline bool matches_attribute_name_value(const TCHAR* value,const unsigned int valulen,xml_attribute_struct* attr) const { return (_tcsncmp(value,attr->value,max(valulen,attr->value_size))==0); } //The given attribute is valued 'value'.
#else
    const TCHAR* name(){ return (has_name()) ? _root->name : _T(""); } //Access pointer to node name if any, else empty string.
    inline bool matches_attribute_name(const TCHAR* name,const unsigned int i) const { return (strcmpwild(name,_root->attribute[i]->name)==0); } //There is an attribute at 'i' named 'name'.
    inline bool matches_child_name(const TCHAR* name,const unsigned int i) const { return (strcmpwild(name,_root->child[i]->name)==0); } //There is a child at 'i' named 'name'.
    inline bool matches_name(const TCHAR* name,xml_node_struct* node) const { return (strcmpwild(name,node->name)==0); } //This is named 'name'.
    inline bool matches_value(const TCHAR* data,xml_node_struct* node) const { return (strcmpwild(data,node->value)==0); } //This is valued 'value'.
    inline bool matches_attribute_name(const TCHAR* attribute,xml_attribute_struct* attr) const { return (strcmpwild(attribute,attr->name)==0); } //The given attribute is named 'name'.
    inline bool matches_attribute_name_value(const TCHAR* value,xml_attribute_struct* attr) const { return (strcmpwild(value,attr->value)==0); } //The given attribute is valued 'value'.
#endif
    xml_node_type type() const { return (_root) ? (xml_node_type)_root->type : node_null; } //Access node entity type.
    const TCHAR* value() { return (has_value()) ? _root->value : _T(""); } //Access pointer to data if any, else empty string.
    unsigned int children() const { return _root->children; } //Access node's child count.
    xml_node child(unsigned int i){ return (i < children()) ? xml_node(_root->child[i]) : xml_node(); } //Access child node at subscript as xml_node or xml_node(NULL) if bad subscript.
    unsigned int attributes() const { return _root->attributes; } //Access node's attribute count.
    xml_attribute attribute(unsigned int i){ return (i < attributes()) ? xml_attribute(_root->attribute[i]) : xml_attribute(); } //Access attribute at subscript if any, else empty attribute.
    //<summary>Access or create the attribute having 'name'.</summary>
    //<param name="name">Name of attribute to access/create.</param>
    //<returns>Reference to xml_attribute wrapper.</returns>
    xml_attribute attribute(const std::string& name){ return attribute(name.c_str()); }
    //<summary>Access or create the attribute having 'name'.</summary>
    //<param name="name">Name of attribute to access/create.</param>
    //<returns>Reference to xml_attribute wrapper.</returns>
    xml_attribute attribute(const TCHAR* name)
    {
        xml_attribute_struct* attr = mapto_attribute_ptr(name);
        if(!attr) attr = append_attribute(name,_T(""));
        return xml_attribute(attr);
    }
    const unsigned int siblings(){ return (!type_document()) ? _root->parent->children : 0; } //Access node's sibling count (parent's child count).
    xml_node sibling(unsigned int i){ return (!type_document() && i < siblings()) ? xml_node(_root->parent->child[i]) : xml_node(); } //Access sibling node at subscript as xml_node or xml_node(NULL) if bad subscript.
    xml_node parent(){ return (!type_document()) ? xml_node(_root->parent) : xml_node(); } //Access node's parent if any, else xml_node(NULL)

    //<summary>Return the first child that has data's data. If none, return NULL.</summary>
    //<param name="value">Returns a copy of the data.</param>
    //<param name="valuelen">Specifies the maximum number of characters to copy into value.</param>
    //<returns>Pointer to value if exists, else NULL.</returns>
    //<remarks>
    //  Used to get the PCDATA for the current element. This handles elements 
    //  like: &lt;LINE&gt;&lt;STAGEDIR&gt;Aside&lt;/STAGEDIR&gt;Thy father, 
    //  Pompey, would ne'er have&lt;/LINE&gt;, where 'this' points to &lt;LINE&gt;.
    //</remarks>
    TCHAR* child_value(TCHAR* value,const unsigned int valuelen)const
    {
        if(_root->children)
        {
            for(register unsigned int i=0; i < _root->children; ++i)
            {
                xml_node_struct* node = _root->child[i];
                if(node->value)
                {
                    const unsigned int n =
#ifdef PUGOPT_NONSEG
                        min(valuelen,node->value_size);
#else
                        min(valuelen,_tcslen(node->value));
#endif
                    _tcsncpy(value,node->value,n);
                    value[n] = 0;
                    break;
                }
            }
            return value;
        }
        return NULL;
    }

//Name-To-Object Mapping
public:

    //<summary>Map an attribute name to a pointer to that attribute, if found.</summary>
    //<param name="name">Reference to name of attribute to find.</param>
    //<returns>Pointer to attribute, or NULL if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many attributes.</remarks>
    xml_attribute_struct* mapto_attribute_ptr(const std::string& name){ return mapto_attribute_ptr(name.c_str()); }

    //<summary>Map an attribute name to a pointer to that attribute, if found.</summary>
    //<param name="name">Pointer to name of attribute to find.</param>
    //<returns>Pointer to attribute, or NULL if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many attributes.</remarks>
    xml_attribute_struct* mapto_attribute_ptr(const TCHAR* name)
    {
        if(!_root || !name) return NULL;
        register unsigned int n = _root->attributes;
#ifdef PUGOPT_NONSEG
        const int namelen = _tcslen(name);
#endif
        for(register unsigned int i=0; i<n; ++i)
#ifdef PUGOPT_NONSEG
            if(matches_attribute_name(name,namelen,i))
#else
            if(matches_attribute_name(name,i))
#endif
                return _root->attribute[i];
        return NULL;
    }

    //<summary>Map an attribute name to the index of that attribute, if found.</summary>
    //<param name="name">Pointer to name of attribute to find.</param>
    //<returns>Index of attribute, or -1 if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many attributes.</remarks>
    int mapto_attribute_idx(const TCHAR* name)
    {
        if(!_root || !name) return -1;
        register unsigned int n = _root->attributes;
#ifdef PUGOPT_NONSEG
        const int namelen = _tcslen(name);
#endif
        for(register unsigned int i=0; i<n; ++i)
#ifdef PUGOPT_NONSEG
            if(matches_attribute_name(name,namelen,i))
#else
            if(matches_attribute_name(name,i))
#endif
                return i;
        return -1;
    }

    //<summary>Map a child name to a pointer to the first instance, if found.</summary>
    //<param name="name">Reference to name of child to find.</param>
    //<returns>Index of child, or -1 if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many children.</remarks>
    xml_node_struct* mapto_child_ptr(const std::string& name){ return mapto_child_ptr(name.c_str()); }

    //<summary>Map a child name to a pointer to the first instance, if found.</summary>
    //<param name="name">Pointer to name of child to find.</param>
    //<returns>Index of child, or -1 if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many children.</remarks>
    xml_node_struct* mapto_child_ptr(const TCHAR* name)
    {
        if(!_root || !name) return NULL;
        register unsigned int n = _root->children;
#ifdef PUGOPT_NONSEG
        const int namelen = _tcslen(name);
#endif
        for(register unsigned int i=0; i<n; ++i)
        {
            if
                (
                _root->child[i]->name &&
#ifdef PUGOPT_NONSEG
                matches_child_name(name,namelen,i)
#else
                matches_child_name(name,i)
#endif
                )
                return _root->child[i];
        }
        return NULL;
    }

    //<summary>Map a child name to the index of the first instance, if found.</summary>
    //<param name="name">Reference to name of child to find.</param>
    //<returns>Index of child, or -1 if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many children.</remarks>
    int mapto_child_idx(const std::string& name){ return mapto_child_idx(name.c_str()); }

    //<summary>Map a child name to the index of the first instance, if found.</summary>
    //<param name="name">Pointer to name of child to find.</param>
    //<returns>Index of child, or -1 if not found.</returns>
    //<remarks>Implement your own hash table if you have a great many children.</remarks>
    int mapto_child_idx(const TCHAR* name)
    {
        if(!_root || !name) return -1;
        register unsigned int n = _root->children;
#ifdef PUGOPT_NONSEG
        const int namelen = _tcslen(name);
#endif
        for(register unsigned int i=0; i<n; ++i)
        {
            if
                (
                _root->child[i]->name &&
#ifdef PUGOPT_NONSEG
                matches_child_name(name,namelen,i)
#else
                matches_child_name(name,i)
#endif
                )
                return i;
        }
        return -1;
    }

//Traversal Helpers
public:

    //<summary>Find all elements having the given name.</summary>
    //<param name="name">Reference to name of child to find.</param>
    //<param name="found">Reference to xml_node_list or pointer_array to receive the matching elements.
    void all_elements_by_name(const std::string& name,pointer_array& found){ all_elements_by_name(name.c_str(),found); }

    //<summary>Find all elements having the given name.</summary>
    //<param name="name">Pointer to name of child to find.</param>
    //<param name="found">Reference to xml_node_list or pointer_array to receive the matching elements.</param>
    void all_elements_by_name(const TCHAR* name,pointer_array& found)
    {
        if(empty() || !name) return; //Invalid node, so fail.
        if(_root->children > 0) //Has children.
        {
#ifdef PUGOPT_NONSEG
            const unsigned int namelen = _tcslen(name);
#endif
            register unsigned int n = _root->children; //For each child.
            for(register unsigned int i=0; i<n; ++i)
            {
                if
                    (
                    _root->child[i] && //There is a child at i.
                    _root->child[i]->name && //The child has a name.
#ifdef PUGOPT_NONSEG
                    matches_child_name(name,namelen,i)
#else
                    matches_child_name(name,i)
#endif
                    )
                    found.push_back(_root->child[i]); //push_back it to the array.
                if(_root->child[i]->children) //If there are children.
                {
                    xml_node subsearch(_root->child[i]); //Wrap it up for ease.
                    subsearch.all_elements_by_name(name,found); //Find any matching children.
                }
            }
        }
    }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element. 
    //  Use for shallow drill-downs.
    //</summary>
    //<param name="name">Const reference to name of element to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_name(const std::string& name){ return first_element_by_name(name.c_str()); }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element. 
    //  Use for shallow drill-downs.
    //</summary>
    //<param name="name">Pointer to name of element to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_name(const TCHAR* name)
    {
        if(empty() || !name) return xml_node(); //Invalid node, so fail.
        if(_root->children > 0) //Has children.
        {
            register unsigned int n = _root->children; //For each child.
#ifdef PUGOPT_NONSEG
            const int namelen = _tcslen(name);
#endif
            for(register unsigned int i=0; i<n; ++i)
            {
                if
                (
                    _root->child[i]->name &&
#ifdef PUGOPT_NONSEG
                    matches_child_name(name,namelen,i)
#else
                    matches_child_name(name,i)
#endif
                )
                    return xml_node(_root->child[i]);
                else if(_root->child[i]->children)
                {
                    xml_node subsearch(_root->child[i]); //Wrap it up for ease.
                    xml_node found = subsearch.first_element_by_name(name);
                    if(!found.empty()) return found; //Found.
                }
            }
        }
        return xml_node(); //Not found.
    }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element 
    //  also having matching PCDATA.
    //</summary>
    //<param name="name">Reference to name of element to find.</param>
    //<param name="value">Reference to PCDATA to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found with PCDATA 'value'.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_value(const std::string& name,const std::string& value){ return first_element_by_value(name.c_str(),value.c_str()); }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element 
    //  also having matching PCDATA.
    //</summary>
    //<param name="name">Pointer to name of element to find.</param>
    //<param name="value">Pointer to PCDATA to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found with PCDATA 'value'.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_value(const TCHAR* name,const TCHAR* value)
    {
        if(empty() || !name || !value) return xml_node(); //Invalid node, so fail.
        if(_root->children > 0) //Has children.
        {
            register unsigned int n = _root->children; //For each child.
#ifdef PUGOPT_NONSEG
            const unsigned int namelen = _tcslen(name);
            const unsigned int valulen = _tcslen(value);
#endif
            for(register unsigned int i=0; i<n; ++i)
            {
                if
                (
                    _root->child[i] && //There is a child at i.
                    _root->child[i]->name && //The child has a name.
#ifdef PUGOPT_NONSEG
                    matches_child_name(name,namelen,i)
#else
                    matches_child_name(name,i)
#endif
                )
                {
                    register unsigned int m = _root->child[i]->children; //For each child of child.
                    for(register unsigned int j=0; j<m; ++j)
                    {
                        if
                        (
                            _root->child[i]->child[j] && //There is a child at j.
                            _root->child[i]->child[j]->type == node_pcdata && //It is of the PCDATA type.
                            _root->child[i]->child[j]->value && //It has data.
#ifdef PUGOPT_NONSEG
                            matches_value(value,valulen,_root->child[i]->child[j])
#else
                            matches_value(value,_root->child[i]->child[j])
#endif
                        )
                            return xml_node(_root->child[i]); //Wrap it up and return.
                    }
                }
                else if(_root->child[i] && _root->child[i]->children) //The child has children.
                {
                    xml_node subsearch(_root->child[i]); //Wrap it up for ease.
                    xml_node found = subsearch.first_element_by_value(name,value); //Search any children.
                    if(!found.empty()) return found; //Found.
                }
            }
        }
        return xml_node(); //Not found.
    }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element 
    //  also having matching attribute.
    //</summary>
    //<param name="name">Reference to name of element to find.</param>
    //<param name="attr_name">Reference to name of attribute to find.</param>
    //<param name="attr_value">Reference to attribute value to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_attribute(const std::string& name,const std::string& attr_name,const std::string& attr_value){ return first_element_by_attribute(name.c_str(),attr_name.c_str(),attr_value.c_str()); }

    //<summary>
    //  Recursively-implemented depth-first find the first matching element 
    //  also having matching attribute.
    //</summary>
    //<param name="name">Pointer to name of element to find.</param>
    //<param name="attr_name">Pointer to name of attribute to find.</param>
    //<param name="attr_value">Pointer to attribute value to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_element_by_attribute(const TCHAR* name,const TCHAR* attr_name,const TCHAR* attr_value)
    {
        if(empty() || !name || !attr_name || !attr_value) return xml_node(); //Invalid data, so fail.
        if(_root->children > 0) //Has children.
        {
#ifdef PUGOPT_NONSEG
            const unsigned int namelen = _tcslen(name);
            const unsigned int attrlen = _tcslen(attr_name);
            const unsigned int valulen = _tcslen(attr_value);
#endif
            register unsigned int n = _root->children; //For each child.
            for(register unsigned int i=0; i<n; ++i)
            {
                if
                (
                    _root->child[i] && //There is a child at i.
                    _root->child[i]->name && //The child has a name.
#ifdef PUGOPT_NONSEG
                    matches_name(name,namelen,_root->child[i])
#else
                    matches_name(name,_root->child[i])
#endif
                )
                {
                    register unsigned int m = _root->child[i]->attributes; //For each attribute of child.
                    for(register unsigned int j=0; j<m; ++j)
                    {
                        if
                        (
                            _root->child[i]->attribute[j] && //There is an attribute at j.
                            _root->child[i]->attribute[j]->name && //The attribute has a name.
#ifdef PUGOPT_NONSEG
                            matches_attribute_name(attr_name,attrlen,_root->child[i]->attribute[j]) &&
#else
                            matches_attribute_name(attr_name,_root->child[i]->attribute[j]) &&
#endif
                            _root->child[i]->attribute[j]->value && //The attribute has a value.
#ifdef PUGOPT_NONSEG
                            matches_attribute_name_value(attr_value,valulen,_root->child[i]->attribute[j])
#else
                            matches_attribute_name_value(attr_value,_root->child[i]->attribute[j])
#endif
                        )
                            return xml_node(_root->child[i]); //Wrap it up and return.
                    }
                    // JC test begin
                    if(_root->child[i] && _root->child[i]->children)
                    {
                        xml_node subsearch(_root->child[i]); //Wrap it up for ease.
                        xml_node found = subsearch.first_element_by_attribute(name,attr_name,attr_value); //Search any children.
                        if(!found.empty()) return found; //Found.
                    }
                    // JC test end
                }
                else if(_root->child[i] && _root->child[i]->children)
                {
                    xml_node subsearch(_root->child[i]); //Wrap it up for ease.
                    xml_node found = subsearch.first_element_by_attribute(name,attr_name,attr_value); //Search any children.
                    if(!found.empty()) return found; //Found.
                }
            }
        }
        return xml_node(); //Not found.
    }

    //<summary>
    //  Recursively-implemented depth-first find the first matching entity. 
    //  Use for shallow drill-downs.
    //</summary>
    //<param name="name">Pointer to name of element to find.</param>
    //<returns>Valid xml_node if such element named 'name' is found.</returns>
    //<remarks>xml_node may be invalid if not found; test with 'empty'.</remarks>
    xml_node first_node(xml_node_type type)
    {
        if(!_root) return xml_node();
        if(_root->children > 0) //Has children.
        {
            register unsigned int n = _root->children; //For each child.
            for(register unsigned int i=0; i<n; ++i)
            {
                if(_root->child[i]->type==type)
                    return xml_node(_root->child[i]);
                else if(_root->child[i]->children)
                {
                    xml_node subsearch(_root->child[i]);
                    xml_node found = subsearch.first_node(type);
                    if(!found.empty()) return found; //Found.
                }
            }
        }
        return xml_node(); //Not found.
    }

    //<summary>Move to the absolute root of the document tree.</summary>
    //<returns>True if the current node is valid.</returns>
    //<remarks>Member '_root' may now point to absolute root of the document.</remarks>
    bool moveto_root()
    {
        if(empty()) return false; //Nowhere to go.
        while(!type_document()) _root = _root->parent; //Keep stepping out until we hit the root.
        return true; //Success.
    }

    //<summary>Move to the current node's parent.</summary>
    //<returns>true if there is a parent and cursor is not parent, and cursor points thereto.</returns>
    //<remarks>'_root' may now point to parent.</remarks>
    bool moveto_parent()
    {
        if(empty() || type_document()) return false; //Invalid, or at the root (has no parent).
        _root = _root->parent; //Move to parent.
        return true; //Success.
    }

    //<summary>
    //  Move to the current node's sibling at subscript. Equivalent to 
    //  'moveto_child' following 'moveto_parent'.
    //</summary>
    //<param name="i">Subscript of sibling to move cursor to.</param>
    //<returns>True if valid subscript, and cursor points thereto.</returns>
    //<remarks>If matching co-node was found, '_root' points thereto.</remarks>
    bool moveto_sibling(unsigned int i)
    {
        if(empty()) return false; //Nowhere to go.
        xml_node_struct* restore = _root; //Save position in case invalid subscript & we want to restore.
        if(moveto_parent()) //Try to move to parent.
        {
            if(i < children()) //Subscript is in range. (Assume parent *does* have children.)
            {
                _root = _root->child[i]; //Move to child at subscript ('sibling').
                return true; //Success.
            }
        }
        _root = restore; //Bad subscript, or parent move; restore.
        return false;
    }

    //<summary>Move to the current node's first sibling matching given name.</summary>
    //<param name="name">Element name of sibling to move to.</param>
    //<returns>True if sibling was found, and cursor points thereto.</returns>
    //<remarks>If matching co-node was found, '_root' points thereto.</remarks>
    bool moveto_first_sibling(const std::string& name){ return moveto_first_sibling(name.c_str()); }

    //<summary>Move to the current node's first sibling matching given name.</summary>
    //<param name="name">Element name of sibling to move to.</param>
    //<returns>True if sibling was found, and cursor points thereto.</returns>
    //<remarks>If matching co-node was found, '_root' points thereto.</remarks>
    bool moveto_first_sibling(const TCHAR* name)
    {
        if(empty() || !name) return false; //Nowhere to go, or nothing to find.
        xml_node_struct* restore = _root; //Save position in case invalid subscript & we want to restore.
        if(moveto_parent()) //Try to move to parent.
        {
#ifdef PUGOPT_NONSEG
            const unsigned int namelen = _tcslen(name);
#endif
            register unsigned int n = children(); //Search for matching name
            for(register unsigned int i=0; i<n; ++i)
            {
                //NF 24 Jan 2003 Changed to get child(i) just once per iteration.
                xml_node node = child(i); //Access child node at subscript as xml_node or xml_node(NULL) if bad subscript.
                if(node.type_element()||node.type_pi()) //Other types won't have names.
                {
#ifdef PUGOPT_NONSEG
                    if(_tcsncmp(name,node.name(),max(namelen,node.name_size()))==0) //Do names match?
#else
                    if(strcmpwild(name,node.name())==0) //Do names match?
#endif
                    {
                        _root = node; //Move there.
                        return true; //Success.
                    }
                }
            }
        }
        _root = restore; //Failed to locate any such sibling; restore position.
        return false;
    }

    //<summary>Move to the current node's child at subscript.</summary>
    //<param name="i">Subscript of child to move cursor to.</param>
    //<returns>true if valid subscript, and cursor points thereto.</returns>
    //<remarks>If matching sub-node was found, '_root' points thereto.</remarks>
    bool moveto_child(unsigned int i)
    {
        if(empty()) return false; //Null, so no children.
        if(has_child_nodes() && i < children()) //Has children and subscript is in bounds.
        {
            _root = child(i); //Move to the child at i.
            return true; //Success.
        }
        return false; //Failure.
    }

    //<summary>Move to the current node's child matching given name.</summary>
    //<param name="name">Element name of child to move to if found.</param>
    //<returns>True if child was found, and cursor points thereto.</returns>
    //<remarks>If matching sub-node was found, '_root' points thereto.</remarks>
    bool moveto_child(const std::string& name){ return moveto_child(name.c_str()); }

    //<summary>Move to the current node's child matching given name.</summary>
    //<param name="name">Element name of child to move to if found.</param>
    //<returns>True if child was found, and cursor points thereto.</returns>
    //<remarks>If matching sub-node was found, '_root' points thereto.</remarks>
    bool moveto_child(const TCHAR* name)
    {
        if(empty() || !name || !has_child_nodes()) return false; //The node is null, a name was not specified, or node has no children.
#ifdef PUGOPT_NONSEG
        const unsigned int namelen = _tcslen(name);
#endif
        register unsigned int n = children(); //For each child.
        for(register unsigned int i=0; i<n; ++i)
        {
            //NF 24 Jan 2003: Changed to get child(i) just once per iteration.
            xml_node node = child(i); //Access child node at subscript as xml_node or xml_node(NULL) if bad subscript.
#ifdef PUGOPT_NONSEG
            if(_tcsncmp(name,node.name(),max(namelen,node.name_size()))==0) //Do names match?
#else
            if(strcmpwild(name,node.name())==0) //If the name is identical with 'name'.
#endif
            {
                _root = node; //Move to it.
                return true; //Success.
            }
        }
        return false; //Failure.
    }

    //<summary>Move to the current node's next sibling by position and name.</summary>
    //<param name="name">Name of sibling to move to if found.</param>
    //<returns>True if there is a next sibling, and cursor points thereto.</returns>
    bool moveto_next_sibling(const std::string& name){ return moveto_next_sibling(name.c_str()); }

    //<summary>Move to the current node's next sibling by position and name.</summary>
    //<param name="name">Name of sibling to move to if found.</param>
    //<returns>True if there is a next sibling, and cursor points thereto.</returns>
    bool moveto_next_sibling(const TCHAR* name)
    {
        if(empty() || type_document() || !_root->parent || !name) return false; //Null, or at root, or no name, so there are no valid matches.
#ifdef PUGOPT_NONSEG
        const unsigned int namelen = _tcslen(name);
#endif
        register unsigned int n = _root->parent->children; //For each child of parent.
        for(register unsigned int i=0; i<(n-1); ++i)
        {
            if
            (
                _root->parent->child[i] && //There is a child at i.
                _root->parent->child[i] == _root && //The child is identical with this node.
                i < (n-1) //This is not the last child.
            )
            {
                for(++i; i<n; ++i) //For each following child.
                {
                    if
                    (
                        _root->parent->child[i] && //There is a child at i.
                        _root->parent->child[i]->name && //The child's name is not null.
#ifdef PUGOPT_NONSEG
                        matches_name(name,namelen,_root->parent->child[i])
#else
                        matches_name(name,_root->parent->child[i])
#endif
                    )
                    {
                        moveto_sibling(i); //Move to it.
                        return true; //Success.
                    }
                }
            }
        }
        return false; //Failure.
    }

    //<summary>Move to the current node's next sibling by position.</summary>
    //<returns>True if there is a next sibling, and cursor points thereto.</returns>
    bool moveto_next_sibling()
    {
        if(empty() || type_document() || !_root->parent) return false; //Null or at root, so there are no valid siblings.
        register unsigned int n = _root->parent->children; //For each child of parent (each sibling).
        for(register unsigned int i=0; i<(n-1); ++i)
        {
            if
            (
                _root->parent->child[i] && //There is a child at i.
                _root->parent->child[i] == _root && //The child is identical with this node.
                i < (n-1) //This is not the last child.
            )
            {
                for(++i; i<n; ++i) //For each following child.
                {
                    if(_root->parent->child[i]) //There is a child at i.
                    {
                        moveto_sibling(i); //Move to it.
                        return true; //Success.
                    }
                }
            }
        }
        return false; //Failure.
    }

    //<summary>Compile the absolute node path from root as a text string.</summary>
    //<param name="delimiter">Delimiter string to insert between element names.</param>
    //<returns>Path string (e.g. with '/' as delimiter, '/document/.../this'.</returns>
    /*std::string path(const TCHAR* delimiter = _T("/"))
    {
        TCHAR* path = NULL; //Current path.
        TCHAR* temp; //Temporary pointer.
        xml_node cursor = *this; //Make a copy.
#ifdef PUGOPT_NONSEG
        unsigned int destlen = 0;
        strcatgrown_impl(&path,cursor.name(),destlen,cursor.name_size()); //Get this name.
#else
        strcatgrow(&path,cursor.name()); //Get this name.
#endif
        while(cursor.moveto_parent() && !cursor.type_document()) //Loop to parent (stopping on actual root because it has no name).
        {
            temp = NULL; //Mark as null so 'strcatgrow' will allocate memory.
#ifdef PUGOPT_NONSEG
            destlen = 0;
            strcatgrown_impl(&temp,cursor.name(),destlen,cursor.name_size()); //Append next element name.
#else
            strcatgrow(&temp,cursor.name()); //Append next element name.
#endif
            strcatgrow(&temp,delimiter); //Append delimiter.
            strcatgrow(&temp,path); //Append current path.
            free(path); //Free the old path.
            path = temp; //Set path as new string.
        }
        temp = NULL;
        strcatgrow(&temp,delimiter); //Prepend final delimiter.
        strcatgrow(&temp,path); //Append current path.
        free(path); //Free the old path.
        std::string returns = temp; //Set path as new string.
        free(temp);
        return returns; //Return the path;
    }*/

    //<summary>Search for a node by path.</summary>
    //<param name="path">
    //  Path string; e.g. './foo/bar' (relative to node), '/foo/bar' (relative 
    //  to root), '../foo/bar' (pop relative position).
    //</param>
    //<param name="delimiter">Delimiter string to use in tokenizing path.</param>
    //<returns>Matching node, or xml_node(NULL) if not found.</returns>
    //xml_node first_element_by_path(const std::string& path,const std::string& delimiter = _T("/")){ return first_element_by_path(path.c_str(),delimiter.c_str()); }

    //<summary>Search for a node by path.</summary>
    //<param name="path">
    //  Path string; e.g. './foo/bar' (relative to node), '/foo/bar' (relative 
    //  to root), '../foo/bar' (pop relative to position).
    //</param>
    //<param name="delimiter">Delimiter string to use in tokenizing path.</param>
    //<returns>Matching node, or xml_node(NULL) if not found.</returns>
    //<remarks>To-do: Support XPath-style queries.</remarks>
    xml_node first_element_by_path(const TCHAR* path,const TCHAR* delimiter = _T("/"))
    {
        if(!path) return xml_node();
        TCHAR* temp = NULL;
        pointer_array path_segments; //Array of path segments.
        xml_node found = *this; //Current search context.
        strcatgrow(&temp,path);
        TCHAR* name = _tcstok(temp,delimiter);
        while(name) //Tokenize the whole path.
        {
            path_segments.push_back((void*)name); //push_back it to array.
            name = _tcstok(NULL,delimiter); //Get the next token,
        }
        register unsigned int n = path_segments.size();
        if(n == 0) return xml_node(); //Return null node if no path segments.
        if(path[0]==delimiter[0]) found.moveto_root(); //Absolute path; e.g. '/foo/bar'
        for(register unsigned int i = 0; i<n; ++i) //For each path segment.
        {
            name = (TCHAR*)path_segments.at(i);
            if(name)
            {
                if(*name==_T('.')) //Is '.' or '..'
                {
                    if(_tcscmp(name,_T(".."))==0) found.moveto_parent(); //Pop.
                    else continue; //Ignore '.' since it is redundant if path is './path'.
                }
                else
                {
                    register unsigned int j, m = found.children(); //For each child.
                    for(j=0; j<m; ++j)
                    {
                        if(found.child(j).has_name(name)) //Name matches?
                        {
                            found = found.child(j); //Move to this child.
                            goto NEXT_ELEM; //Search next path segment.
                        }
                    }
                    if(found.moveto_next_sibling(found.name())) //Find next sibling having same name.
                    {
                        if(i > 0) --i; //Try the previous path segment.
                        goto NEXT_ELEM;
                    }
                    else //Move to parent to search further.
                    {
                        if(!found.type_document() && found.moveto_parent() && !found.type_document()) //Not root and stepped to parent and parent is not root.
                        {
                            if(i > 0) --i; //Try the previous path segment.
                            if(found.moveto_next_sibling(found.name())) //Try to find next sibling having same name.
                            {
                                if(i > 0) --i; //Try the previous path segment.
                                goto NEXT_ELEM;
                            }
                        }
                    }
                }
            }
NEXT_ELEM:;
            if(found.type_document()) //Can't move up any higher, so fail.
            {
                free(temp); //Got to free this.
                return xml_node(); //Return null node.
            }
        }
        free(temp); //Got to free this.
        return found; //Return the matching node.
    }

    //<summary>Recursively traverse the tree.</summary>
    //<param name="walker">Reference to tree walker derived from xml_tree_walker.</param>
    //<returns>True if traversal was not halted by xml_tree_walker::for_each() callback.</returns>
    bool traverse(xml_tree_walker& walker)
    {
        if(walker.depth() == 0 && !walker.begin(*this)) return false; //Send the callback for begin traverse if depth is zero.
        if(!empty()) //Don't traveres if this is a null node.
        {
            walker.push(); //Increment the walker depth counter.
            register unsigned int n = _root->children; //For each child.
            for(register unsigned int i=0; i<n; ++i)
            {
                if(_root->child[i]) //There is a child at i.
                {
                    xml_node subsearch(_root->child[i]); //Wrap it.
                    if(!(walker.for_each(subsearch) && subsearch.traverse(walker)))
                        return false; //Traversal was aborted.
                }
            }
            walker.pop(); //Decrement the walker depth counter.
        }
        if(walker.depth() == 0 && !walker.end(*this)) return false; //Send the callback for end traverse if depth is zero.
        return true;
    }

//Editorial Helpers
public:

    //<summary>Set element name.</summary>
    //<param name="new_name">New element name.</param>
    //<returns>Success.</returns>
    bool name(const std::string& new_name){ return name(new_name.c_str()); }

    //<summary>Set element name.</summary>
    //<param name="new_name">New element name.</param>
    //<returns>Success.</returns>
    bool name(const TCHAR* new_name)
    {
        if((type_element() || type_pi()) && new_name)
#ifdef PUGOPT_NONSEG
            return strcpyinsitu(&_root->name,new_name,&_root->name_insitu,_root->name_size );
#else
            return strcpyinsitu(&_root->name,new_name,&_root->name_insitu);
#endif
        return false;
    }

    //<summary>Set node data.</summary>
    //<param name="value">New data (PCDATA, CDATA, or comment) value.</param>
    //<returns>Success.</returns>
    bool value(const std::string& new_value){ return value(new_value.c_str()); }

    //<summary>Set node data.</summary>
    //<param name="value">New data (PCDATA, CDATA, or comment) value.</param>
    //<returns>Success.</returns>
    bool value(const TCHAR* new_value)
    {
        if((type_pcdata() || type_cdata() || type_comment()) && new_value)
#ifdef PUGOPT_NONSEG
            return strcpyinsitu(&_root->value,new_value,&_root->value_insitu,_root->value_size);
#else
            return strcpyinsitu(&_root->value,new_value,&_root->value_insitu);
#endif
        return false;
    }

    //<summary>Remove attribute at the given subscript.</summary>
    //<param name="i">Subscript.</param>
    //<returns>Success.</returns>
    bool remove_attribute(unsigned int i)
    {
        unsigned int n = _root->attributes;
        if(i < n)
        {
            xml_attribute_struct* temp = _root->attribute[i];
            --n;
            for(unsigned int j=i; j<n; ++j)
                _root->attribute[j] = _root->attribute[j+1];
            _root->attribute[n] = NULL;
            if(!temp->name_insitu) free(temp->name);
            if(!temp->value_insitu) free(temp->value);
            free(temp);
            --_root->attributes;
            return true;
        }
        return false;
    }

    //<summary>Remove attribute having the given name.</summary>
    //<param name="name">Name of attribute to delete.</param>
    //<returns>Success.</returns>
    bool remove_attribute(const std::string& name){ return remove_attribute(name.c_str()); }

    //<summary>Remove attribute having the given name.</summary>
    //<param name="name">Name of attribute to delete.</param>
    //<returns>Success.</returns>
    bool remove_attribute(const TCHAR* name)
    {
        int i = mapto_attribute_idx(name);
        if(i > -1) return remove_attribute((unsigned int)i);
        return false;
    }

    //<summary>Append a new attribute to the node list of attributes.</summary>
    //<param name="name">Name.</param>
    //<param name="value">Value thereof.</param>
    //<returns>Attribute structure wrapper.</returns>
    //<remarks>Pointer space may be grown, memory for name/value members allocated.</remarks>
    xml_attribute append_attribute(const std::string& name,const std::string& value){ return append_attribute(name.c_str(),value.c_str()); }

    //<summary>Append a new attribute to the node list of attributes.</summary>
    //<param name="name">Name.</param>
    //<param name="value">Value thereof.</param>
    //<returns>Attribute structure wrapper.</returns>
    //<remarks>Pointer space may be grown, memory for name/value members allocated.</remarks>
    xml_attribute append_attribute(const TCHAR* name,const TCHAR* value)
    {
        if(!name || !value) return xml_attribute(); //We must have both to proceed.
        xml_attribute_struct* p = pug::append_attribute(_root,1); //Append/allocate a new attribute structure.
        if(p) //If append/allocate succeeded.
        {
#ifdef PUGOPT_NONSEG
            strcatgrown(&p->name,name,p->name_size); //Append the name.
            strcatgrown(&p->value,value,p->value_size); //Append the name.
#else
            strcatgrow(&p->name,name); //Append the name.
            strcatgrow(&p->value,value); //Append the name.
#endif
            p->name_insitu = p->value_insitu = false; //Mark as not part of original parse string.
            return xml_attribute(p); //Success.
        }
        return xml_attribute(); //Failure; return an empty.
    }

    //<summary>Append a new attribute of type long to the node list of attributes.</summary>
    //<param name="name">Name.</param>
    //<param name="value">Value thereof.</param>
    //<returns>Attribute structure wrapper.</returns>
    //<remarks>Pointer space may be grown, memory for name/value members allocated.</remarks>
    xml_attribute append_attribute(const TCHAR* name,long value)
    {
        if(!name) return false;
        TCHAR temp[32] = {0};
        _stprintf(temp,_T("%ld"),value);
        return append_attribute(name,temp);
    }

    //<summary>Append a new attribute of type double to the node list of attributes.</summary>
    //<param name="name">Name.</param>
    //<param name="value">Value thereof.</param>
    //<returns>Attribute structure wrapper.</returns>
    //<remarks>Pointer space may be grown, memory for name/value members allocated.</remarks>
    xml_attribute append_attribute(const TCHAR* name,double value)
    {
        if(!name) return false;
        TCHAR temp[32] = {0};
        _stprintf(temp,_T("%lf"),value);
        return append_attribute(name,temp);
    }

    //<summary>Append a new attribute of type bool to the node list of attributes.</summary>
    //<param name="name">Name.</param>
    //<param name="value">Value thereof.</param>
    //<returns>Attribute structure wrapper.</returns>
    //<remarks>Pointer space may be grown, memory for name/value members allocated.</remarks>
    xml_attribute append_attribute(const TCHAR* name,bool value)
    {
        if(!name) return false;
        return append_attribute(name,((value)?_T("true"):_T("false")));
    }

    //<summary>Set the current node entity type.</summary>
    //<param name="new_type">New type to set.</param>
    //<returns>Previous type.</returns>
    //<remarks>If has children and now is not node_element, children are obscured.</remarks>
    xml_node_type type(xml_node_type new_type)
    {
        xml_node_type prev = _root->type; //Save old type.
        _root->type = new_type; //Set new type.
        return prev; //Return old type.
    }

    //<summary>
    //  Allocate & append a child node of the given type at the end of the 
    //  current node array of children.
    //</summary>
    //<param name="type">New child node type.</param>
    //<returns>xml_node wrapping the new child.</returns>
    //<remarks>Pointer space may be grown. An xml_node_struct structure is allocated.</remarks>
    xml_node append_child(xml_node_type type)
    {
        if(type_document()||type_element()) //Don't do anything if not an node_element or root.
        {
            xml_node_struct* p = pug::append_node(_root,1,type); //Append the node.
            if(p)
            {
                p->name_insitu = p->value_insitu = false;
                return xml_node(p); //If we have it, return wrapped.
            }
        }
        return xml_node(); //Return dummy.
    }

/////////////////////////////////////////////////////////////////////////
    // Added by JCrane
    //<summary>
    //  Append a pre-allocated child node of the given type at the end of the 
    //  current node array of children.
    //</summary>
    //<param name="type">New child node type.</param>
    //<returns>xml_node wrapping the new child.</returns>
    //<remarks>Pointer space may be grown. An xml_node_struct structure is allocated.</remarks>
    xml_node append_child_noalloc(xml_node_struct* Node)
    {
        if(type_document()||type_element()) //Don't do anything if not an node_element or root.
        {
            xml_node_struct* p = pug::append_node_noalloc(_root,1,Node); //Append the node.
            if(p)
            {
                p->name_insitu = p->value_insitu = false;
                return Node;
            }
        }
        return xml_node(); //Return dummy.
    }
/////////////////////////////////////////////////////////////////////////

    //<summary>Allocate & insert a child node of the given type at subscript.</summary>
    //<param name="i">Subscript at which to insert.</param>
    //<param name="type">New child node type.</param>
    //<returns>xml_node wrapping the new child.</returns>
    //<remarks>
    //  Pointer space may be grown. An xml_node_struct structure is allocated, 
    //  and existing children are shifted in their array position.
    //</remarks>
    xml_node insert_child(unsigned int i,xml_node_type type)
    {
        if(!type_element()) return xml_node(); //Don't do anything if not an node_element.
        unsigned int n = _root->children; //Get count of existing children.
        if(type_element() && i >= n) return append_child(type); //If subscript at end of array then just append.
        else if(type_element() && i < n)
        {
            xml_node_struct* p = pug::append_node(_root,1,type); //Append the new node (by default at last array position).
            if(p) //Ensure we have it.
            {
                register int m = (i-1); //Stop at i.
                for(register int j=(n-1); j>m; --j) //Starting at one less than end of array, reverse loop to i.
                    _root->child[j+1] = _root->child[j]; //Shift node to right.
                _root->child[i] = p; //Set node at subscript to new node.
                return xml_node(p); //Return new node.
            }
        }
        return xml_node(); //Return dummy.
    }

    //<summary>Delete the child node at the given subscript.</summary>
    //<param name="i">Subscript.</param>
    //<returns>Success.</returns>
    //<remarks>Shifts child array element positions. Frees entire tree under child to be deleted.</remarks>
    bool remove_child(unsigned int i)
    {
        unsigned int n = _root->children;
        if(i < n) //Ensure subscript is in bounds.
        {
            xml_node_struct* p = _root->child[i]; //Keep a pointer to this node so we can free it.
            --n;
			unsigned int j;
            for(j=i; j<n; ++j) //Shift everything left from this point on.
                _root->child[j] = _root->child[j+1];
            _root->child[j] = NULL; //Mark the last element null.
            --_root->children; //One less children.
            p->parent = p; //This ensures we only free this node when calling 'free_node'.
            pug::free_node(p); //Free the node tree.
            return true; //Success.
        }
        return false; //Failure.
    }

    //Stream/Output Helpers
public:

    //<summary>
    //  Stream output. Recursively writes the internal xml_node_struct structure 
    //  to the given stream.
    //</summary>
    //<param name="os">Reference to output stream.</param>
    //<param name="indent_char">Char to use for indent.</param>
    //<param name="breaks">Use linebreaks?</param>
    //<remarks>String data is written to stream.</remarks>
    void outer_xml(std::basic_ostream<TCHAR,std::char_traits<TCHAR> >& os,TCHAR indent_char = _T('\t'),bool breaks = true)
    {
        if(empty()) return; //Make sure there is something to output.
        indent_stack indent(indent_char); //Prepare the indent.
        if(type_document()) //If this is the root, we don't want to output the root itself.
        {
            register unsigned int n = _root->children; //Output each child of the root.
            for(register unsigned int i=0; i<n; ++i)
                pug::outer_xml(os,indent,_root->child[i],breaks);
        }
        else pug::outer_xml(os,indent,_root,breaks); //Output the node.
    }

    //<summary>
    //  Stream output operator. Wraps 'outer_xml'. Recursively writes 
    //  the given node to the given stream.
    //</summary>
    //<param name="os">Reference to output stream.</param>
    //<param name="xml_node">Reference to tree node.</param>
    //<returns>Reference to output stream.</returns>
    //<remarks>String data is written to stream.</remarks>
    friend std::basic_ostream<TCHAR,std::char_traits<TCHAR> >& operator<<(std::basic_ostream<TCHAR,std::char_traits<TCHAR> >& os,xml_node node)
    {
        if(!os.good()) return os;
        if((os.flags()|std::ostream::skipws) == std::ostream::skipws)
            node.outer_xml(os,0,false); //Skipping whitespace; suppress indents & linebreaks.
        else node.outer_xml(os); //Default options.
        return os;
    }
};


//<summary>Provides a high-level interface to the XML parser.</summary>
class xml_parser
{
//Internal Data Members
protected:

    xml_node_struct*    _xmldoc; //Pointer to current XML document tree root.
    long                _growby; //Attribute & child pointer space growth increment.
    bool                _autdel; //Delete the tree on destruct?
    TCHAR*              _buffer; //Pointer to in-memory buffer (for 'parse_file').
    TCHAR*              _strpos; //Where parsing left off (for 'parse_file').
    unsigned long       _optmsk; //Parser options.
#ifdef PUGOPT_MEMFIL
    HANDLE              _mmfile; //File handle.
    HANDLE              _mmfmap; //Handle which maps the file.
    void*               _mmaddr; //Base address of map.
    size_t              _mfsize; //Size of memory-mapped file.
    bool                _addeos; //True if we had to add a 0 to then end of the file.
#endif

//Construction/Destruction
public:

    //<summary>Constructor.</summary>
    //<param name="optmsk">Options mask.</param>
    //<param name="autdel">Delete tree on destruct?</param>
    //<param name="growby">Parser pointer space growth increment.</param>
    //<remarks>Root node structure is allocated.</remarks>
    xml_parser(unsigned long optmsk = parse_default,bool autdel = true,long growby = parse_grow):
        _xmldoc(0),
            _growby(growby),
            _autdel(autdel),
            _optmsk(optmsk),
            _buffer(0),
            _strpos(0)
#ifdef PUGOPT_MEMFIL
            ,
            _mmfile(0),
            _mmfmap(0),
            _mmaddr(0),
            _mfsize(0),
            _addeos(false)
#endif
        {
        }

        //<summary>Direct parse constructor.</summary>
        //<param name="xmlstr">
        //  XML-formatted string to parse. Note: String must persist for the 
        //  life of the tree. String is zero-segmented, but not freed.
        //</param>
        //<param name="optmsk">Parser options.</param>
        //<param name="autdel">Delete tree on destruct?</param>
        //<param name="growby">Parser pointer space growth increment.</param>
        //<remarks>Root node structure is allocated, string is parsed & tree may be grown.</remarks>
        xml_parser(TCHAR* xmlstr,unsigned long optmsk = parse_default,bool autdel = true,long growby = parse_grow) :
        _xmldoc(0),
            _growby(growby),
            _autdel(autdel),
            _optmsk(optmsk),
            _buffer(0),
            _strpos(0)
#ifdef PUGOPT_MEMFIL
            ,
            _mmfile(0),
            _mmfmap(0),
            _mmaddr(0),
            _mfsize(0),
            _addeos(false)
#endif
        {
            parse(xmlstr,_optmsk); //Parse it.
        }

        //<summary>Destructor.</summary>
        //<remarks>Tree memory and string memory may be freed.</remarks>
        virtual ~xml_parser()
        {
            if(_autdel && _xmldoc) free_node(_xmldoc);
            if(_buffer) free(_buffer);
#ifdef PUGOPT_MEMFIL
            close_memfile();
#endif
        }

//Accessors/Operators
public:

    operator xml_node_struct*() { return _xmldoc; } //Cast as xml_node_struct pointer to root.
    operator xml_node() { return xml_node(_xmldoc); } //Cast as xml_node (same as document).
    xml_node document(){ return xml_node(_xmldoc); } //Returns the root wrapped by an xml_node.

//Miscellaneous
public:

    //<summary>Allocate a new, empty root.</summary>
    //<remarks>Tree memory and string memory may be freed.</remarks>
    void create()
    {
        clear(); //Free any allocated memory.
        _xmldoc = new_node(node_document); //Allocate a new root.
        _xmldoc->parent = _xmldoc; //Point to self.
    }

    //<summary>Clear any existing tree or string.</summary>
    //<remarks>Tree memory and string memory may be freed.</remarks>
    void clear()
    {
        if(_xmldoc){ free_node(_xmldoc); _xmldoc = 0; }
        if(_buffer){ free(_buffer); _buffer = 0; }
#ifdef PUGOPT_MEMFIL
        close_memfile();
#endif
    }

#ifdef PUGOPT_MEMFIL

//Memory-Mapped File Support
protected:

    //<summary>Closes any existing memory-mapped file.</summary>
    void close_memfile()
    {
        if(_mmaddr != 0)
        {
            UnmapViewOfFile(_mmaddr);
            _mmaddr = 0;
        }
        if(_mmfmap != 0)
        {
            CloseHandle(_mmfmap);
            _mmfmap = 0;
        }
        if(_mmfile != 0)
        {
            if(_addeos) //Remove the 0 we added to the end of the file.
            {
                SetFilePointer(_mmfile,_mfsize,NULL,FILE_BEGIN);
                SetEndOfFile(_mmfile);
                _addeos = false;
            }
            CloseHandle(_mmfile);
            _mmfile = 0;
        }
        _mfsize = 0;
    }

public:

#endif

    //<summary>Attach an externally-generated root to the parser.</summary>
    //<param name="root">Pointer to node structure.</param>
    //<returns>Pointer to old root if any.</returns>
    //<remarks>New root may be deleted on dtor if autodelete set.</remarks>
    xml_node_struct* attach(xml_node_struct* root)
    {
        xml_node_struct* t = _xmldoc; //Save this root.
        _xmldoc = root; //Assign.
        _xmldoc->parent = _xmldoc; //Ensure we are the root.
        return t; //Return the old root if any.
    }

    //<summary>Detach the current root from the parser.</summary>
    //<returns>Pointer to old root, if any.</returns>
    xml_node_struct* detach()
    {
        xml_node_struct* t = _xmldoc; //Save this root.
        _xmldoc = 0; //So we don't delete later on if autodelete set.
        return t; //Return the old root if any.
    }

    //<summary>Get parser optsions mask.</summary>
    //<returns>Options mask.</returns>
    unsigned long options(){ return _optmsk; }

    //<summary>Set parser options mask.</summary>
    //<param name="optmsk">Options mask to set.</param>
    //<returns>Old options mask.</returns>
    unsigned long options(unsigned long optmsk)
    {
        unsigned long prev = _optmsk;
        _optmsk = optmsk;
        return prev;
    }

    //<summary>Get pointer space growth size increment.</summary>
    //<returns>Grow size.</returns>
    unsigned long growby(){ return _growby; }

    //<summary>Set pointer space growth size increment.</summary>
    //<param name="grow">Grow size to set.</param>
    //<returns>Old size.</returns>
    unsigned long growby(long grow)
    {
        long prev = _growby;
        _growby = grow;
        return prev;
    }

    //<summary>Get parse file buffer last string position.</summary>
    //<returns>Last string position.</returns>
    //<remarks>
    //  Use after parse_file, with parse_dtd_only set in order to recommence 
    //  parse of document body.
    //</remarks>
    TCHAR* strpos()
    {
        return _strpos;
    }

//Parsing Helpers
public:

    //<summary>Parse the given XML string in-situ.</summary>
    //<param name="s">Pointer to XML-formatted string.</param>
    //<param name="optmsk">Parser options mask.</param>
    //<returns>Last string position or null.</returns>
    //<remarks>Input string is zero-segmented.</remarks>
    TCHAR* parse(TCHAR* s,unsigned long optmsk = parse_noset)
    {
        if(!s) return s;
        clear(); //Free any allocated memory.
        _xmldoc = new_node(node_document); //Allocate a new root.
        _xmldoc->parent = _xmldoc; //Point to self.
        if(optmsk != parse_noset) _optmsk = optmsk;
        return pug::parse(s,_xmldoc,_growby,_optmsk); //Parse the input string.
    }

    //<summary>Load into memory and parse the contents of the file at the given path.</summary>
    //<param name="path">File path.</param>
    //<param name="optmsk">Parser options.</param>
    //<returns>Success if the file was loaded.</returns>
    //<remarks>
    //  The file contents is loaded and stored in the member '_buffer' until 
    //  freed by calling 'Parse', 'parse_file', 'clear' or '~xml_parser'.
    //</remarks>
    bool parse_file(const TCHAR* path,unsigned long optmsk = parse_noset)
    {
        if(!path) return false;
        clear(); //clear any existing data.
        unsigned long bytes;
        if(optmsk != parse_noset) _optmsk = optmsk;
        if(load_file(path,&_buffer,&bytes) && bytes > 0)
        {
            _xmldoc = pug::new_node(node_document);
            _xmldoc->parent = _xmldoc; //Point to self.
            TCHAR* s = pug::parse(_buffer,_xmldoc,_growby,_optmsk);
            _strpos = s;
            return true;
        }
        return false;
    }

#ifdef PUGOPT_MEMFIL

    //<summary>Parse the contents of the file at the given path, using a memory-mapped file.</summary>
    //<param name="path">File path.</param>
    //<param name="optmsk">Parser options.</param>
    //<returns>
    //  True (1) if the file was parsed successfully, false (0) if open failed, 
    //  and -1 if an exception occured.
    //</returns>
    //<remarks>
    //  The file contents are available until closed by calling 'parse', 
    //  'parse_file', 'clear' or '~xml_parser'.
    //</remarks>
    int parse_mmfile(const TCHAR* path,unsigned long optmsk = parse_noset)
    {
        int status = 0;
        if(path)
        {
            clear(); //Clear any existing data.
            if(optmsk != parse_noset) _optmsk = optmsk;
            assert((optmsk & parse_wnorm) == 0); //Normalization isn't implemented for memory-mapped files, as of 23 Jan 2003.
            const bool readonly = (optmsk & (parse_dtd|parse_dtd_only)) == 0;
            if(open_mmfile(path,readonly,false))
            {
                //If the file has a 0 at the end we are ok to proceed, otherwise add one.
                if
                (
                    (
                        *(((TCHAR*)_mmaddr) + _mfsize) == 0
                        ||
                        (
                            _mfsize > 0 &&
                            *(((TCHAR*)_mmaddr) + _mfsize - 1) == 0
                        )
                    )
                    ||
                    open_mmfile(path,false,true) //Re-open and add 0 at EOF.
                )
                {
                    try
                    {
                        _xmldoc = new_node(node_document);
                        _xmldoc->parent = _xmldoc; //Point to self.
                        TCHAR* s = pug::parse((TCHAR*)_mmaddr,_xmldoc,_growby,_optmsk);
                        _strpos = s;
                        status = 1;
                    }
                    catch(...)
                    {
                        status = -1;
                        assert(false);
                    }
                }
            }
        }
        return status;
    }

protected:

    //<summary>Opens the specified memory-mapped file.</summary>
    //<param name="path">File path.</param>
    //<param name="readonly">True to open the file for read-only access.</param>
    //<param name="addeos">True to add a 0 to the end of the file.</param>
    //<returns>Success if the file was opened.</returns>
    bool open_mmfile(const TCHAR* path,const bool readonly,const bool addeos)
    {
        clear(); //Close any existing MMF and clear any existing data.
        assert(_mmfile == NULL && _mmfile == NULL && _mmaddr == NULL);
        _addeos = false;
        _mmfile = CreateFile(path,readonly?GENERIC_READ:GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //Open read-only, no share, no security attrs, ..., no template.
        if(_mmfile != INVALID_HANDLE_VALUE)
        {
            _mfsize = ::GetFileSize(_mmfile,NULL);
            _mmfmap = CreateFileMapping(_mmfile,NULL,readonly?PAGE_READONLY:PAGE_READWRITE,0,_mfsize+(addeos?sizeof(TCHAR):0),NULL); //Create map: handle, no security attr, read|read/write, larger if addeos, anonymous.
            if(_mmfmap != NULL)
            {
                assert(_mmaddr == NULL);
                _mmaddr = MapViewOfFile(_mmfmap,readonly?FILE_MAP_READ:FILE_MAP_WRITE,0,0,0); //Map the view: handle, read|read/write, start at beginning, map entire file.
                if(_mmaddr != NULL)
                {
                    if(addeos) //Add a terminating 0 to the end of the file for 'parse()'.
                    {
                        assert(!readonly);
                        *(((TCHAR*)_mmaddr) + _mfsize) = 0;
                        _addeos = true;
                    }
                }
                else
                {
                    CloseHandle(_mmfmap);
                    CloseHandle(_mmfile);
                    _mmfile = _mmfmap = 0;
                }
            }
            else
            {
                CloseHandle(_mmfile);
                _mmfile = 0;
            }
        }
        return (_mmaddr != NULL);
    }

#endif

};


//<summary>An array of nodes, used by xml_node queries.</summary>
class xml_node_list: public pointer_array
{
public:
    xml_node_list(unsigned int grow = 4) : pointer_array(grow) { }
    virtual ~xml_node_list(){ }
public:
    xml_node at(long i){ return xml_node((xml_node_struct*)pointer_array::at((unsigned int)i)); } //Access xml_node at subscript.
    xml_node operator[](long i){ return xml_node((xml_node_struct*)pointer_array::at((unsigned int)i)); } //Access xml_node at subscript.
    friend std::ostream& operator<<(std::ostream& os,xml_node_list& list) //Output helper.
    {
        if(!os.good()) return os;
        unsigned int n = list.size();
        for(unsigned int i=0; i<n; ++i) os << list[i];
        return os;
    }
};


}; //namespace pug


//<summary>Check for Pug XML Library Variant/API Version campatibility.</summary>
#if !defined(PUGAPI_VARIANT) || (PUGAPI_VARIANT != PUGAPI_INTERNAL_VARIANT)
#   pragma message("Severe: The Pug XML library API variant does not match your implementation!")
#   pragma message("Severe: Your implementation may not run correctly with this variant.")
#   pragma message("Note: Read pugxml.xml for transition instructions.")
#   ifndef PUGAPI_VARIANT
#       pragma message("Note: Add #define PUGAPI_VARIANT X in your implementation, where X is:")
#   else //PUGAPI_VARIANT
#       pragma message("Note: Update #define PUGAPI_VARIANT X in your implementation, where X is:")
#   endif //!PUGAPI_VARIANT
#   pragma message("pugxml.h(39) : Value of PUGAPI_INTERNAL_VARIANT.")
#else //defined(PUGAPI_VARIANT)
#   if !defined(PUGAPI_VERSION_MAJOR) || (PUGAPI_VERSION_MAJOR != PUGAPI_INTERNAL_VERSION_MAJOR)
#       pragma message("Warning: The Pug XML library API major version does not match your implementation.")
#       pragma message("Warning: Your implementation may not run correctly with this major version.")
#       pragma message("Note: Read pugxml.xml for transition instructions.")
#       ifndef PUGAPI_VERSION_MAJOR
#           pragma message("Note: Add #define PUGAPI_VERSION_MAJOR X in your implementation, where X is:")
#       else //PUGAPI_VERSION_MAJOR
#           pragma message("Note: Update #define PUGAPI_VERSION_MAJOR X in your implementation, where X is:")
#       endif //!PUGAPI_VERSION_MAJOR
#       pragma message("pugxml.h(44) : Value of PUGAPI_INTERNAL_VERSION_MAJOR.")
#   else //defined(PUGAPI_VERSION_MAJOR)
#       if !defined(PUGAPI_VERSION_MINOR) || (PUGAPI_VERSION_MINOR != PUGAPI_INTERNAL_VERSION_MINOR)
#           pragma message("Note: The Pug XML library API minor version does not match your implementation.")
#           pragma message("Note: Your implementation may not run correctly with this minor version.")
#           pragma message("Note: Read pugxml.xml for transition instructions.")
#           ifndef PUGAPI_VERSION_MINOR
#               pragma message("Note: Add #define PUGAPI_VERSION_MINOR X in your implementation, where X is:")
#           else //PUGAPI_VERSION_MINOR
#               pragma message("Note: Update #define PUGAPI_VERSION_MINOR X in your implementation, where X is:")
#           endif //!PUGAPI_VERSION_MINOR
#           pragma message("pugxml.h(45) : Value of PUGAPI_INTERNAL_VERSION_MINOR.")
#       endif //!defined(PUGAPI_VERSION_MINOR)
#   endif //!defined(PUGAPI_VERSION_MAJOR)
#endif //!defined(PUGAPI_VARIANT)


#endif //!defined(_PUGXML_)

