//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
#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. '<...>'
node_pcdata, //E.g. '>...<'
node_cdata, //E.g. '<![CDATA[...]]>'
node_comment, //E.g. '<!--...-->'
node_pi, //E.g. '<?...?>'
node_include, //E.g. '<![INCLUDE[...]]>'
node_doctype, //E.g. '<!DOCTYPE ...>'.
node_dtd_entity, //E.g. '<!ENTITY ...>'.
node_dtd_attlist, //E.g. '<!ATTLIST ...>'.
node_dtd_element, //E.g. '<!ELEMENT ...>'.
node_dtd_notation, //E.g. '<!NOTATION ...>'.
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 '<?...?>'
static const unsigned long parse_doctype = 0x00000004; //Parse '<!DOCTYPE ...>' section, setting '[...]' as data member.
static const unsigned long parse_comments = 0x00000008; //Parse <!--...-->'
static const unsigned long parse_cdata = 0x00000010; //Parse '<![CDATA[...]]>', and/or '<![INCLUDE[...]]>'
static const unsigned long parse_escapes = 0x00000020; //Not implemented.
static const unsigned long parse_trim_pcdata = 0x00000040; //Trim '>...<'
static const unsigned long parse_trim_attribute = 0x00000080; //Trim 'foo="..."'.
static const unsigned long parse_trim_cdata = 0x00000100; //Trim '<![CDATA[...]]>', and/or '<![INCLUDE[...]]>'
static const unsigned long parse_trim_entity = 0x00000200; //Trim '<!ENTITY name ...>', etc.
static const unsigned long parse_trim_doctype = 0x00000400; //Trim '<!DOCTYPE [...]>'
static const unsigned long parse_trim_comment = 0x00000800; //Trim <!--...-->'
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 '<!DOCTYPE [*]>'
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 '<'.
{ return (c==_T('<')); }
inline static bool chartype_leave(TCHAR c) //Character is '>'.
{ 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 "‘" -or- ‘"‘.
{ 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>
static 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();
}
}
//<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>
class xml_iterator : public std::_Ranit<_Ty,_Diff,_Pointer,_Reference>
#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: <LINE><STAGEDIR>Aside</STAGEDIR>Thy father,
// Pompey, would ne'er have</LINE>, where 'this' points to <LINE>.
//</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_)