261 lines
4.5 KiB
C++
261 lines
4.5 KiB
C++
// MyXml.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "MyXml.h"
|
|
|
|
static bool IsValidChar(char c)
|
|
{
|
|
return
|
|
c >= 'a' && c <= 'z' ||
|
|
c >= 'A' && c <= 'Z' ||
|
|
c >= '0' && c <= '9' ||
|
|
c == '-';
|
|
}
|
|
|
|
static bool IsSpaceChar(char c)
|
|
{
|
|
return (c == ' ' || c == '\t' || c == 0x0D || c == 0x0A);
|
|
}
|
|
|
|
#define SKIP_SPACES(s) while (IsSpaceChar(*s)) s++;
|
|
|
|
int CXmlItem::FindProp(const AString &propName) const throw()
|
|
{
|
|
FOR_VECTOR (i, Props)
|
|
if (Props[i].Name == propName)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
AString CXmlItem::GetPropVal(const AString &propName) const
|
|
{
|
|
int index = FindProp(propName);
|
|
if (index >= 0)
|
|
return Props[index].Value;
|
|
return AString();
|
|
}
|
|
|
|
bool CXmlItem::IsTagged(const AString &tag) const throw()
|
|
{
|
|
return (IsTag && Name == tag);
|
|
}
|
|
|
|
int CXmlItem::FindSubTag(const AString &tag) const throw()
|
|
{
|
|
FOR_VECTOR (i, SubItems)
|
|
if (SubItems[i].IsTagged(tag))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
AString CXmlItem::GetSubString() const
|
|
{
|
|
if (SubItems.Size() == 1)
|
|
{
|
|
const CXmlItem &item = SubItems[0];
|
|
if (!item.IsTag)
|
|
return item.Name;
|
|
}
|
|
return AString();
|
|
}
|
|
|
|
const AString * CXmlItem::GetSubStringPtr() const throw()
|
|
{
|
|
if (SubItems.Size() == 1)
|
|
{
|
|
const CXmlItem &item = SubItems[0];
|
|
if (!item.IsTag)
|
|
return &item.Name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
AString CXmlItem::GetSubStringForTag(const AString &tag) const
|
|
{
|
|
int index = FindSubTag(tag);
|
|
if (index >= 0)
|
|
return SubItems[index].GetSubString();
|
|
return AString();
|
|
}
|
|
|
|
const char * CXmlItem::ParseItem(const char *s, int numAllowedLevels)
|
|
{
|
|
SKIP_SPACES(s);
|
|
|
|
const char *beg = s;
|
|
for (;;)
|
|
{
|
|
char c;
|
|
c = *s; if (c == 0 || c == '<') break; s++;
|
|
c = *s; if (c == 0 || c == '<') break; s++;
|
|
}
|
|
if (*s == 0)
|
|
return NULL;
|
|
if (s != beg)
|
|
{
|
|
IsTag = false;
|
|
Name.SetFrom(beg, (unsigned)(s - beg));
|
|
return s;
|
|
}
|
|
|
|
IsTag = true;
|
|
|
|
s++;
|
|
SKIP_SPACES(s);
|
|
|
|
beg = s;
|
|
for (;; s++)
|
|
if (!IsValidChar(*s))
|
|
break;
|
|
if (s == beg || *s == 0)
|
|
return NULL;
|
|
Name.SetFrom(beg, (unsigned)(s - beg));
|
|
|
|
for (;;)
|
|
{
|
|
beg = s;
|
|
SKIP_SPACES(s);
|
|
if (*s == '/')
|
|
{
|
|
s++;
|
|
// SKIP_SPACES(s);
|
|
if (*s != '>')
|
|
return NULL;
|
|
return s + 1;
|
|
}
|
|
if (*s == '>')
|
|
{
|
|
s++;
|
|
if (numAllowedLevels == 0)
|
|
return NULL;
|
|
SubItems.Clear();
|
|
for (;;)
|
|
{
|
|
SKIP_SPACES(s);
|
|
if (s[0] == '<' && s[1] == '/')
|
|
break;
|
|
CXmlItem &item = SubItems.AddNew();
|
|
s = item.ParseItem(s, numAllowedLevels - 1);
|
|
if (!s)
|
|
return NULL;
|
|
}
|
|
|
|
s += 2;
|
|
unsigned len = Name.Len();
|
|
for (unsigned i = 0; i < len; i++)
|
|
if (s[i] != Name[i])
|
|
return NULL;
|
|
s += len;
|
|
if (s[0] != '>')
|
|
return NULL;
|
|
return s + 1;
|
|
}
|
|
if (beg == s)
|
|
return NULL;
|
|
|
|
// ReadProperty
|
|
CXmlProp &prop = Props.AddNew();
|
|
|
|
beg = s;
|
|
for (;; s++)
|
|
{
|
|
char c = *s;
|
|
if (!IsValidChar(c))
|
|
break;
|
|
}
|
|
if (s == beg)
|
|
return NULL;
|
|
prop.Name.SetFrom(beg, (unsigned)(s - beg));
|
|
|
|
SKIP_SPACES(s);
|
|
if (*s != '=')
|
|
return NULL;
|
|
s++;
|
|
SKIP_SPACES(s);
|
|
if (*s != '\"')
|
|
return NULL;
|
|
s++;
|
|
|
|
beg = s;
|
|
for (;;)
|
|
{
|
|
char c = *s;
|
|
if (c == 0)
|
|
return NULL;
|
|
if (c == '\"')
|
|
break;
|
|
s++;
|
|
}
|
|
prop.Value.SetFrom(beg, (unsigned)(s - beg));
|
|
s++;
|
|
}
|
|
}
|
|
|
|
static const char * SkipHeader(const char *s, const char *startString, const char *endString)
|
|
{
|
|
SKIP_SPACES(s);
|
|
if (IsString1PrefixedByString2(s, startString))
|
|
{
|
|
s = strstr(s, endString);
|
|
if (!s)
|
|
return NULL;
|
|
s += strlen(endString);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void CXmlItem::AppendTo(AString &s) const
|
|
{
|
|
if (IsTag)
|
|
s += '<';
|
|
s += Name;
|
|
if (IsTag)
|
|
{
|
|
FOR_VECTOR (i, Props)
|
|
{
|
|
const CXmlProp &prop = Props[i];
|
|
s += ' ';
|
|
s += prop.Name;
|
|
s += '=';
|
|
s += '\"';
|
|
s += prop.Value;
|
|
s += '\"';
|
|
}
|
|
s += '>';
|
|
}
|
|
FOR_VECTOR (i, SubItems)
|
|
{
|
|
const CXmlItem &item = SubItems[i];
|
|
if (i != 0 && !SubItems[i - 1].IsTag)
|
|
s += ' ';
|
|
item.AppendTo(s);
|
|
}
|
|
if (IsTag)
|
|
{
|
|
s += '<';
|
|
s += '/';
|
|
s += Name;
|
|
s += '>';
|
|
}
|
|
}
|
|
|
|
bool CXml::Parse(const char *s)
|
|
{
|
|
s = SkipHeader(s, "<?xml", "?>"); if (!s) return false;
|
|
s = SkipHeader(s, "<!DOCTYPE", ">"); if (!s) return false;
|
|
|
|
s = Root.ParseItem(s, 1000);
|
|
if (!s || !Root.IsTag)
|
|
return false;
|
|
SKIP_SPACES(s);
|
|
return *s == 0;
|
|
}
|
|
|
|
/*
|
|
void CXml::AppendTo(AString &s) const
|
|
{
|
|
Root.AppendTo(s);
|
|
}
|
|
*/
|