2439 lines
62 KiB
C++
2439 lines
62 KiB
C++
// ===================================================================
|
||
// Class: COXParser (including COXParserObject, COXAttribute, COXParserElement and COXToken)
|
||
// File: OXParser.cpp
|
||
// Purpose: Parser for XML like structures
|
||
|
||
// Version: 9.3
|
||
|
||
// This software along with its related components, documentation and files ("The Libraries")
|
||
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
||
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
||
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
||
// to obtain this file, or directly from our office. For a copy of the license governing
|
||
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
||
//
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
#include "stdafx.h"
|
||
#include "OXParser.h"
|
||
|
||
#include "UTBStrop.h"
|
||
|
||
#ifndef CSTR_EQUAL
|
||
#define CSTR_EQUAL 2 // string 1 equal to string 2
|
||
#endif
|
||
//////////////////////////////////////////////////////////////////////
|
||
// Macros for debugging
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
#ifndef TRACE0 // If this isn't defined, a whole bunch of stuff isn't defined
|
||
#undef TRACE1
|
||
#undef TRACE2
|
||
#undef TRACE3
|
||
#undef UNUSED_ALWAYS
|
||
#undef UNUSED
|
||
|
||
#define UNUSED_ALWAYS(x) (x);
|
||
#ifdef _DEBUG
|
||
#define UNUSED(x)
|
||
#include <crtdbg.h>
|
||
#define TRACE0(text) _RPT0(_CRT_WARN, text)
|
||
#define TRACE1(format, a1) _RPT1(_CRT_WARN, format, a1)
|
||
#define TRACE2(format, a1, a2) _RPT2(_CRT_WARN, format, a1, a2)
|
||
#define TRACE3(format, a1, a2, a3) _RPT3(_CRT_WARN, format, a1, a2, a3)
|
||
#else
|
||
#define UNUSED(x) (x);
|
||
#define TRACE0(text)
|
||
#define TRACE1(format, a1)
|
||
#define TRACE2(format, a1, a2)
|
||
#define TRACE3(format, a1, a2, a3)
|
||
#endif
|
||
|
||
#endif
|
||
|
||
#ifdef _DEBUG
|
||
#define TRACE4(format, a1, a2, a3, a4 ) _RPT4(_CRT_WARN, format, a1, a2, a3, a4)
|
||
#else
|
||
#define TRACE4(format, a1, a2, a3, a4)
|
||
#endif
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// Static data
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
// This is the list of default entities. Just add entities to this list
|
||
// and they will be automatically encoded/decoded during text processing.
|
||
// Note that when the parser writes out text, it will only convert from
|
||
// text to entity (eg '>' ==> >) when the literal text is a single character
|
||
// (so if you add "date == "10 May" and "10 May" appears in text, it won't
|
||
// be converted to "&date;")
|
||
ParserEntity COXParser::m_Entity[] =
|
||
{
|
||
{TEXT("gt"), TEXT(">")},
|
||
{TEXT("lt"), TEXT("<")},
|
||
{TEXT("amp"), TEXT("&")},
|
||
{TEXT("apos"), TEXT("\'")},
|
||
{TEXT("quot"), TEXT("\"")},
|
||
|
||
{TEXT("quot"), TEXT("\"")},
|
||
{TEXT("amp"), TEXT("&")},
|
||
{TEXT("apos"), TEXT("\'")},
|
||
{TEXT("lt"), TEXT("<")},
|
||
{TEXT("gt"), TEXT(">")},
|
||
{TEXT("nbsp"), TEXT(" ")},
|
||
{TEXT("iexcl"), TEXT("¡")},
|
||
{TEXT("cent"), TEXT("¢")},
|
||
{TEXT("pound"), TEXT("£")},
|
||
{TEXT("curren"), TEXT("¤")},
|
||
{TEXT("yen"), TEXT("¥")},
|
||
{TEXT("brvbar"), TEXT("¦")},
|
||
{TEXT("sect"), TEXT("§")},
|
||
{TEXT("uml"), TEXT("¨")},
|
||
{TEXT("copy"), TEXT("©")},
|
||
{TEXT("ordf"), TEXT("ª")},
|
||
{TEXT("laquo"), TEXT("«")},
|
||
{TEXT("not"), TEXT("¬")},
|
||
{TEXT("shy"), TEXT("")},
|
||
{TEXT("reg"), TEXT("®")},
|
||
{TEXT("macr"), TEXT("¯")},
|
||
{TEXT("deg"), TEXT("°")},
|
||
{TEXT("plusmn"), TEXT("±")},
|
||
{TEXT("sup2"), TEXT("²")},
|
||
{TEXT("sup3"), TEXT("³")},
|
||
{TEXT("acute"), TEXT("´")},
|
||
{TEXT("micro"), TEXT("µ")},
|
||
{TEXT("para"), TEXT("¶")},
|
||
{TEXT("middot"), TEXT("·")},
|
||
{TEXT("cedil"), TEXT("¸")},
|
||
{TEXT("sup1"), TEXT("¹")},
|
||
{TEXT("ordm"), TEXT("º")},
|
||
{TEXT("raquo"), TEXT("»")},
|
||
{TEXT("frac14"), TEXT("¼")},
|
||
{TEXT("frac12"), TEXT("½")},
|
||
{TEXT("frac34"), TEXT("¾")},
|
||
{TEXT("iquest"), TEXT("¿")},
|
||
{TEXT("Agrave"), TEXT("À")},
|
||
{TEXT("Aacute"), TEXT("Á")},
|
||
{TEXT("Acirc"), TEXT("Â")},
|
||
{TEXT("Atilde"), TEXT("Ã")},
|
||
{TEXT("Auml"), TEXT("Ä")},
|
||
{TEXT("Aring"), TEXT("Å")},
|
||
{TEXT("AElig"), TEXT("Æ")},
|
||
{TEXT("Ccedil"), TEXT("Ç")},
|
||
{TEXT("Egrave"), TEXT("È")},
|
||
{TEXT("Eacute"), TEXT("É")},
|
||
{TEXT("Ecirc"), TEXT("Ê")},
|
||
{TEXT("Euml"), TEXT("Ë")},
|
||
{TEXT("Igrave"), TEXT("Ì")},
|
||
{TEXT("Iacute"), TEXT("Í")},
|
||
{TEXT("Icirc"), TEXT("Î")},
|
||
{TEXT("Iuml"), TEXT("Ï")},
|
||
{TEXT("ETH"), TEXT("Ð")},
|
||
{TEXT("Ntilde"), TEXT("Ñ")},
|
||
{TEXT("Ograve"), TEXT("Ò")},
|
||
{TEXT("Oacute"), TEXT("Ó")},
|
||
{TEXT("Ocirc"), TEXT("Ô")},
|
||
{TEXT("Otilde"), TEXT("Õ")},
|
||
{TEXT("Ouml"), TEXT("Ö")},
|
||
{TEXT("times"), TEXT("×")},
|
||
{TEXT("Oslash"), TEXT("Ø")},
|
||
{TEXT("Ugrave"), TEXT("Ù")},
|
||
{TEXT("Uacute"), TEXT("Ú")},
|
||
{TEXT("Ucirc"), TEXT("Û")},
|
||
{TEXT("Uuml"), TEXT("Ü")},
|
||
{TEXT("Yacute"), TEXT("Ý")},
|
||
{TEXT("THORN"), TEXT("Þ")},
|
||
{TEXT("szlig"), TEXT("ß")},
|
||
{TEXT("agrave"), TEXT("à")},
|
||
{TEXT("aacute"), TEXT("á")},
|
||
{TEXT("acirc"), TEXT("â")},
|
||
{TEXT("atilde"), TEXT("ã")},
|
||
{TEXT("auml"), TEXT("ä")},
|
||
{TEXT("aring"), TEXT("å")},
|
||
{TEXT("aelig"), TEXT("æ")},
|
||
{TEXT("ccedil"), TEXT("ç")},
|
||
{TEXT("egrave"), TEXT("è")},
|
||
{TEXT("eacute"), TEXT("é")},
|
||
{TEXT("ecirc"), TEXT("ê")},
|
||
{TEXT("euml"), TEXT("ë")},
|
||
{TEXT("igrave"), TEXT("ì")},
|
||
{TEXT("iacute"), TEXT("í")},
|
||
{TEXT("icirc"), TEXT("î")},
|
||
{TEXT("iuml"), TEXT("ï")},
|
||
{TEXT("eth"), TEXT("ð")},
|
||
{TEXT("ntilde"), TEXT("ñ")},
|
||
{TEXT("ograve"), TEXT("ò")},
|
||
{TEXT("oacute"), TEXT("ó")},
|
||
{TEXT("ocirc"), TEXT("ô")},
|
||
{TEXT("otilde"), TEXT("õ")},
|
||
{TEXT("ouml"), TEXT("ö")},
|
||
{TEXT("divide"), TEXT("÷")},
|
||
{TEXT("oslash"), TEXT("ø")},
|
||
{TEXT("ugrave"), TEXT("ù")},
|
||
{TEXT("uacute"), TEXT("ú")},
|
||
{TEXT("ucirc"), TEXT("û")},
|
||
{TEXT("uuml"), TEXT("ü")},
|
||
{TEXT("yacute"), TEXT("ý")},
|
||
{TEXT("thorn"), TEXT("þ")},
|
||
{TEXT("yuml"), TEXT("ÿ")},
|
||
|
||
{ NULL, NULL } // must be last
|
||
};
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// COXToken
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
COXToken::COXToken()
|
||
{
|
||
Clear();
|
||
}
|
||
|
||
COXToken::~COXToken()
|
||
{
|
||
Clear();
|
||
}
|
||
|
||
// --- In :
|
||
// --- Out :
|
||
// --- Returns :
|
||
// --- Effect : Resets the token to an empty state
|
||
void COXToken::Clear()
|
||
{
|
||
m_nType = UNKNOWN;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// COXParserObject
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
COXParserObject::COXParserObject()
|
||
{
|
||
m_nType = UNKNOWN;
|
||
m_pParent = NULL;
|
||
m_nFlags = 0;
|
||
}
|
||
|
||
COXParserObject::COXParserObject(COXParserElement* pParent, int nType,
|
||
LPCTSTR szText)
|
||
{
|
||
m_pParent = pParent;
|
||
m_nType = nType;
|
||
m_nFlags = 0;
|
||
|
||
SetText(szText);
|
||
}
|
||
|
||
COXParserObject::~COXParserObject()
|
||
{
|
||
}
|
||
|
||
COXParserElement* COXParserObject::GetParent() const
|
||
{
|
||
if (m_pParent && m_pParent->GetType() == ELEMENT)
|
||
return m_pParent;
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
BOOL COXParserObject::SetText(LPCTSTR szText)
|
||
{
|
||
return m_Text.SetString(szText);
|
||
}
|
||
|
||
BOOL COXParserObject::IsText(LPCTSTR szText,
|
||
BOOL bCaseSensitive /*=FALSE*/)
|
||
{
|
||
return m_Text.Compare(szText, bCaseSensitive);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// COXAttribute
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
COXAttribute::COXAttribute()
|
||
{
|
||
m_nValue = 0;
|
||
SetType(ATTRIBUTE);
|
||
SetAttributeType(ATTR_UNKNOWN);
|
||
}
|
||
|
||
COXAttribute::COXAttribute(LPCTSTR szName, LPCTSTR szValue)
|
||
{
|
||
m_nValue = 0;
|
||
SetName(szName);
|
||
SetValue(szValue);
|
||
}
|
||
|
||
COXAttribute::COXAttribute(LPCTSTR szName, int nValue)
|
||
{
|
||
SetName(szName);
|
||
SetValue(nValue);
|
||
}
|
||
|
||
COXAttribute::~COXAttribute()
|
||
{
|
||
m_szValue.Empty();
|
||
}
|
||
|
||
LPCTSTR COXAttribute::GetStringValue()
|
||
{
|
||
static TCHAR strValue[20];
|
||
|
||
if (GetAttributeType() == ATTR_INTEGER)
|
||
{
|
||
UTBStr::stprintf(strValue, 20, TEXT("%d"), m_nValue);
|
||
return strValue;
|
||
}
|
||
else if (m_szValue.IsEmpty())
|
||
{
|
||
strValue[0] = TEXT('\0');
|
||
return strValue;
|
||
}
|
||
else
|
||
return m_szValue.GetString();
|
||
}
|
||
|
||
int COXAttribute::GetIntValue() const
|
||
{
|
||
if (GetAttributeType() == ATTR_STRING)
|
||
return 0;
|
||
else
|
||
return m_nValue;
|
||
}
|
||
|
||
void COXAttribute::SetValue(LPCTSTR szValue)
|
||
{
|
||
m_szValue.SetString(szValue);
|
||
SetAttributeType(ATTR_STRING);
|
||
}
|
||
|
||
void COXAttribute::SetValue(int nValue)
|
||
{
|
||
if (GetAttributeType() == ATTR_STRING)
|
||
m_szValue.Empty();
|
||
|
||
m_nValue = nValue;
|
||
SetAttributeType(ATTR_INTEGER);
|
||
}
|
||
|
||
BOOL COXAttribute::IsValue(LPCTSTR szValue,
|
||
BOOL bCaseSensitive /*=FALSE*/)
|
||
{
|
||
if (!szValue || GetAttributeType() != ATTR_STRING || !m_szValue)
|
||
return FALSE;
|
||
|
||
BOOL bFound = FALSE;
|
||
if (!bCaseSensitive)
|
||
bFound = (_tcsicmp(m_szValue, szValue) == 0);
|
||
else
|
||
bFound = (_tcscmp(m_szValue, szValue) == 0);
|
||
|
||
return bFound;
|
||
}
|
||
|
||
void COXAttribute::operator=(COXAttribute& Attr)
|
||
{
|
||
SetType(Attr.GetType());
|
||
SetName(Attr.GetName());
|
||
|
||
if (Attr.GetAttributeType() == ATTR_STRING)
|
||
SetValue(Attr.GetStringValue());
|
||
else
|
||
SetValue(Attr.GetIntValue());
|
||
}
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// COXParserElement
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
COXParserElement::COXParserElement()
|
||
{
|
||
SetType(ELEMENT);
|
||
}
|
||
|
||
COXParserElement::COXParserElement(COXParserElement* pParent,
|
||
LPCTSTR szName)
|
||
{
|
||
SetParent(pParent);
|
||
SetType(ELEMENT);
|
||
SetName(szName);
|
||
}
|
||
|
||
COXParserElement::~COXParserElement()
|
||
{
|
||
Clear();
|
||
}
|
||
|
||
COXParserObject* COXParserElement::AddObject(COXParserObject* pObject)
|
||
{
|
||
pObject->SetParent(this);
|
||
m_Objects.push_back(pObject);
|
||
|
||
return pObject;
|
||
}
|
||
|
||
COXParserElement* COXParserElement::AddElement(LPCTSTR szName)
|
||
{
|
||
COXParserElement *pElement = new COXParserElement(this, szName);
|
||
if (!pElement)
|
||
return NULL;
|
||
|
||
return (COXParserElement*) AddObject(pElement);
|
||
}
|
||
|
||
COXParserObject* COXParserElement::InsertObject(int nIndex,
|
||
COXParserObject* pObject)
|
||
{
|
||
pObject->SetParent(this);
|
||
m_Objects.insert(m_Objects.begin() + nIndex, pObject);
|
||
|
||
return pObject;
|
||
}
|
||
|
||
COXParserObject* COXParserElement::MoveObject(int nIndex,
|
||
COXParserObject* pObject)
|
||
{
|
||
if (!pObject)
|
||
return FALSE;
|
||
|
||
// Find object and remove it from the list
|
||
for (int i = 0; i < NumObjects(); i++)
|
||
{
|
||
if (Object(i) == pObject)
|
||
{
|
||
m_Objects.erase(m_Objects.begin() + i);
|
||
break;
|
||
}
|
||
}
|
||
return InsertObject(nIndex, pObject);
|
||
}
|
||
|
||
COXParserElement* COXParserElement::InsertElement(int nIndex,
|
||
LPCTSTR szName)
|
||
{
|
||
COXParserElement *pElement = new COXParserElement(this, szName);
|
||
if (!pElement)
|
||
return NULL;
|
||
|
||
return (COXParserElement*) InsertObject(nIndex, pElement);
|
||
}
|
||
|
||
BOOL COXParserElement::DelObject(int nIndex)
|
||
{
|
||
if (nIndex >= NumObjects())
|
||
return FALSE;
|
||
|
||
COXParserObject *pObject = Object(nIndex);
|
||
if (pObject)
|
||
delete pObject;
|
||
|
||
m_Objects.erase(m_Objects.begin() + nIndex);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParserElement::DelObject(COXParserObject* pObject)
|
||
{
|
||
if (!pObject)
|
||
return FALSE;
|
||
|
||
for (int i = 0; i < NumObjects(); i++)
|
||
{
|
||
if (Object(i) == pObject)
|
||
return DelObject(i);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
void COXParserElement::ClearObjects()
|
||
{
|
||
for (int i = 0; i < NumObjects(); i++)
|
||
{
|
||
COXParserObject* pObject = Object(i);
|
||
if (pObject)
|
||
delete pObject;
|
||
}
|
||
|
||
m_Objects.clear();
|
||
}
|
||
|
||
#if _MSC_VER >=1200
|
||
#pragma warning(push)
|
||
#endif
|
||
#pragma warning(disable:4239)
|
||
COXParserElement* COXParserElement::FindElement(LPCTSTR szName,
|
||
BOOL bCaseSensitive /*=FALSE*/)
|
||
{
|
||
COXQuickString str = szName;
|
||
UINT nLevel = 0;
|
||
|
||
COXQuickString strFolderName = str.GetToken(nLevel++, TEXT('\\'));
|
||
if (strFolderName.IsEmpty())
|
||
return NULL;
|
||
|
||
|
||
COXParserElement* pElement = this;
|
||
COXParserElement* pTargetElement = NULL;
|
||
|
||
while (!strFolderName.IsEmpty())
|
||
{
|
||
pTargetElement = NULL;
|
||
|
||
for (int i = 0; i < pElement->NumObjects(); i++)
|
||
{
|
||
COXParserElement* pSearchElement =
|
||
(COXParserElement*) pElement->Object(i);
|
||
if (!pSearchElement || pSearchElement->GetType() != ELEMENT)
|
||
continue;
|
||
|
||
// Found folder
|
||
if (pSearchElement->IsName(strFolderName, bCaseSensitive))
|
||
{
|
||
pTargetElement = pSearchElement;
|
||
pElement = pTargetElement;
|
||
break;
|
||
}
|
||
}
|
||
strFolderName = str.GetToken(nLevel++, TEXT('\\'));
|
||
}
|
||
|
||
return pTargetElement;
|
||
}
|
||
#if _MSC_VER >=1200
|
||
#pragma warning(pop)
|
||
#else
|
||
#pragma warning(default:4239)
|
||
#endif
|
||
COXAttribute* COXParserElement::AddAttribute(COXAttribute* pAttribute)
|
||
{
|
||
pAttribute->SetParent(this);
|
||
m_Attributes.push_back(pAttribute);
|
||
|
||
return pAttribute;
|
||
}
|
||
|
||
COXAttribute* COXParserElement::AddAttribute(LPCTSTR szName,
|
||
LPCTSTR szValue)
|
||
{
|
||
COXAttribute *pAttribute = new COXAttribute;
|
||
if (!pAttribute)
|
||
return NULL;
|
||
|
||
pAttribute->SetName(szName);
|
||
pAttribute->SetValue(szValue);
|
||
pAttribute->SetParent(this);
|
||
|
||
return AddAttribute(pAttribute);
|
||
}
|
||
|
||
COXAttribute* COXParserElement::AddAttribute(LPCTSTR szName,
|
||
int nValue)
|
||
{
|
||
COXAttribute *pAttribute = new COXAttribute;
|
||
if (!pAttribute)
|
||
return NULL;
|
||
|
||
pAttribute->SetName(szName);
|
||
pAttribute->SetValue(nValue);
|
||
pAttribute->SetParent(this);
|
||
|
||
return AddAttribute(pAttribute);
|
||
}
|
||
|
||
BOOL COXParserElement::DelAttribute(int nIndex)
|
||
{
|
||
if (nIndex >= NumAttributes())
|
||
return FALSE;
|
||
|
||
COXAttribute *pAttribute = Attribute(nIndex);
|
||
delete pAttribute;
|
||
|
||
m_Attributes.erase(m_Attributes.begin() + nIndex);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParserElement::DelAttribute(COXAttribute* pAttribute)
|
||
{
|
||
if (!pAttribute)
|
||
return FALSE;
|
||
|
||
for (int i = 0; i < NumAttributes(); i++)
|
||
{
|
||
if (Attribute(i) == pAttribute)
|
||
return DelAttribute(i);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
void COXParserElement::ClearAttributes()
|
||
{
|
||
for (int i = 0; i < NumAttributes(); i++)
|
||
{
|
||
COXAttribute *pAttribute = Attribute(i);
|
||
delete pAttribute;
|
||
}
|
||
|
||
m_Attributes.clear();
|
||
}
|
||
|
||
COXAttribute* COXParserElement::FindAttribute(LPCTSTR szName,
|
||
bool bCaseSensitive /*=false*/)
|
||
{
|
||
if (!szName)
|
||
return NULL;
|
||
|
||
for (int i = 0; i < NumAttributes(); i++)
|
||
{
|
||
COXAttribute *pAttribute = Attribute(i);
|
||
if (pAttribute->IsName(szName, (BOOL) bCaseSensitive))
|
||
return pAttribute;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
COXAttribute* COXParserElement::FindAttribute(LPCTSTR szName,
|
||
LPCTSTR szValue,
|
||
bool bCaseSensitive /*=false*/)
|
||
{
|
||
if (!szName || !szValue)
|
||
return NULL;
|
||
|
||
for (int i = 0; i < NumAttributes(); i++)
|
||
{
|
||
COXAttribute* pAttribute = Attribute(i);
|
||
if (!pAttribute ||
|
||
pAttribute->GetAttributeType() != COXAttribute::ATTR_STRING)
|
||
continue;
|
||
|
||
if (pAttribute->IsName(szName, (BOOL) bCaseSensitive) &&
|
||
pAttribute->IsValue(szValue, (BOOL) bCaseSensitive))
|
||
return pAttribute;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
COXAttribute* COXParserElement::FindAttribute(LPCTSTR szName, int nValue,
|
||
bool bCaseSensitive /*=false*/)
|
||
{
|
||
if (!szName)
|
||
return NULL;
|
||
|
||
for (int i = 0; i < NumAttributes(); i++)
|
||
{
|
||
COXAttribute* pAttribute = Attribute(i);
|
||
if (!pAttribute ||
|
||
pAttribute->GetAttributeType() != COXAttribute::ATTR_INTEGER)
|
||
continue;
|
||
|
||
if (pAttribute->IsName(szName, (BOOL) bCaseSensitive) &&
|
||
pAttribute->GetIntValue() == nValue)
|
||
return pAttribute;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// Construction/Destruction
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
COXParser::COXParser()
|
||
{
|
||
m_chStartDelim = TEXT('<');
|
||
m_chEndDelim = TEXT('>');
|
||
m_chTagEnd = TEXT('/');
|
||
m_chProcInstr = TEXT('?');
|
||
m_chMarkup = TEXT('!');
|
||
m_chDash = TEXT('-');
|
||
m_chCR = TEXT('\r');
|
||
m_chLF = TEXT('\n');
|
||
m_chNULL = TEXT('\0');
|
||
m_chEsc = TEXT('&');
|
||
|
||
m_szTab = TEXT(" "); // indent tab when writing XML files
|
||
m_nLineLength = 80; // Max length of a line of text when writing files
|
||
|
||
m_bErrorOnMissingTag = TRUE;
|
||
m_bCaseSensitive = TRUE;
|
||
m_nLine = 0;
|
||
m_nColumn = 0;
|
||
|
||
m_pfnErrorFn = NULL;
|
||
m_dwErrData = 0;
|
||
|
||
// Fill hash table with special characters
|
||
for (int i = 0; m_Entity[i].szName; i++)
|
||
m_EntityTable.Add(m_Entity[i].szName,
|
||
(DWORD)(INT_PTR) m_Entity[i].szLiteral);
|
||
}
|
||
|
||
COXParser::~COXParser()
|
||
{
|
||
Clear();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// Parsing routines
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
void COXParser::Clear()
|
||
{
|
||
m_Token.Clear();
|
||
m_Root.ClearObjects();
|
||
m_Root.ClearAttributes();
|
||
m_nLine = 0;
|
||
m_nColumn = 0;
|
||
m_pBuf = m_pBufStart = NULL;
|
||
}
|
||
|
||
BOOL COXParser::Initialize()
|
||
{
|
||
m_pBuf = m_pBufStart;
|
||
m_nLine = 1;
|
||
m_nColumn = 1;
|
||
|
||
m_Root.SetName(TEXT("[Root]"));
|
||
m_Root.SetParent(NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::Cleanup()
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::IsEmpty() const
|
||
{
|
||
return (!m_Root.NumAttributes() && !m_Root.NumObjects());
|
||
}
|
||
|
||
void COXParser::SetErrorRptFunction(ParserErrorFn pFn, DWORD dwData)
|
||
{
|
||
m_pfnErrorFn = pFn;
|
||
m_dwErrData = dwData;
|
||
}
|
||
|
||
BOOL COXParser::Parse(LPTSTR pBuf)
|
||
{
|
||
if (!pBuf)
|
||
return FALSE;
|
||
|
||
Clear();
|
||
|
||
m_pBufStart = pBuf;
|
||
|
||
Initialize();
|
||
BOOL bResult = ParseElement(&m_Root, 0);
|
||
Cleanup();
|
||
|
||
return bResult;
|
||
}
|
||
|
||
TCHAR COXParser::GetNextChar(int nSteps /*=1*/,
|
||
BOOL bWarnOnError /*=FALSE*/)
|
||
{
|
||
ASSERT(m_pBuf);
|
||
if (!m_pBuf)
|
||
{
|
||
if (bWarnOnError)
|
||
ReportError(ERROR_NULL_BUFFER, TEXT("NULL buffer supplied."));
|
||
return m_chNULL;
|
||
}
|
||
|
||
TCHAR ch = *m_pBuf;
|
||
for (int i = 0; i < nSteps && ch; i++)
|
||
{
|
||
ch = *m_pBuf;
|
||
if (!ch)
|
||
break;
|
||
|
||
m_pBuf++;
|
||
|
||
// Convert CRLF's to LF's, and CR's to LF's
|
||
if (ch == m_chCR)
|
||
{
|
||
ch = m_chLF;
|
||
if (*m_pBuf == m_chLF)
|
||
m_pBuf++;
|
||
}
|
||
|
||
m_nColumn++;
|
||
if (ch == m_chLF)
|
||
{
|
||
m_nLine++;
|
||
m_nColumn = 0;
|
||
}
|
||
}
|
||
|
||
if (ch == m_chNULL)
|
||
{
|
||
if (bWarnOnError)
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer."));
|
||
return m_chNULL;
|
||
}
|
||
|
||
return ch;
|
||
}
|
||
|
||
void COXParser::UngetChar(int nSteps /*=1*/)
|
||
{
|
||
for (int i = 0; i < nSteps; i++)
|
||
{
|
||
if (m_pBuf <= m_pBufStart)
|
||
break;
|
||
|
||
m_pBuf--;
|
||
if (*m_pBuf == m_chLF ||
|
||
(*m_pBuf == m_chCR && *(m_pBuf+1) != m_chLF) )
|
||
{
|
||
m_nLine--;
|
||
m_nColumn = 0;
|
||
for (LPTSTR ptr = m_pBuf;
|
||
ptr >= m_pBufStart && *ptr != m_chLF && *ptr != m_chCR;
|
||
ptr--)
|
||
m_nColumn++;
|
||
}
|
||
else
|
||
m_nColumn--;
|
||
}
|
||
}
|
||
|
||
BOOL COXParser::RemoveWhiteSpace()
|
||
{
|
||
TCHAR ch = GetNextChar();
|
||
while (ch && _istspace(ch))
|
||
ch = GetNextChar();
|
||
|
||
if (ch)
|
||
{
|
||
UngetChar();
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
void COXParser::SaveBufferPos(SAVEPOS& pos)
|
||
{
|
||
pos.pBuf = m_pBuf;
|
||
pos.nLine = m_nLine;
|
||
pos.nCol = m_nColumn;
|
||
}
|
||
|
||
void COXParser::RestoreBufferPos(SAVEPOS& pos)
|
||
{
|
||
m_pBuf = pos.pBuf;
|
||
m_nLine = pos.nLine;
|
||
m_nColumn = pos.nCol;
|
||
}
|
||
|
||
BOOL COXParser::GetNameToken(COXQuickString& str)
|
||
{
|
||
str.Empty();
|
||
str.SetLength(100);
|
||
|
||
TCHAR ch = GetNextChar();
|
||
while (ch && !_istspace(ch) &&
|
||
(_istalnum(ch) || ch == TEXT(':') ||
|
||
ch == TEXT('_') || ch == TEXT('-')) )
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
|
||
BOOL bResult = TRUE;
|
||
|
||
if (ch == m_chNULL)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while name."));
|
||
bResult = FALSE;
|
||
}
|
||
else if (ch != m_chEndDelim && ch != m_chTagEnd &&
|
||
ch != TEXT('=') && !_istspace(ch))
|
||
{
|
||
ReportError(ERROR_ILLEGAL_CHAR,
|
||
TEXT("Illegal character (%c) encountered while parsing name."), ch);
|
||
bResult = FALSE;
|
||
}
|
||
|
||
if (bResult)
|
||
UngetChar();
|
||
else
|
||
str.Empty();
|
||
|
||
return bResult;
|
||
}
|
||
|
||
BOOL COXParser::GetNumberToken(int& nNumber)
|
||
{
|
||
COXQuickString str;
|
||
str.SetLength(20);
|
||
|
||
int nMultiplier = 1;
|
||
|
||
TCHAR ch = GetNextChar();
|
||
if (ch == TEXT('-'))
|
||
{
|
||
nMultiplier = -1;
|
||
ch = GetNextChar();
|
||
}
|
||
else if (ch == TEXT('+'))
|
||
ch = GetNextChar();
|
||
|
||
while (_istdigit(ch))
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
|
||
if (ch == m_chNULL)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing integer."));
|
||
str.Empty();
|
||
return FALSE;
|
||
}
|
||
UngetChar();
|
||
|
||
nNumber = _ttoi((LPCTSTR) str) * nMultiplier;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::GetStringToken(COXQuickString& str, TCHAR chQuoteChar)
|
||
{
|
||
str.Empty();
|
||
str.SetLength(100);
|
||
|
||
TCHAR ch = GetNextChar();
|
||
while (ch && ch != chQuoteChar)
|
||
{
|
||
|
||
#ifdef OXPARSER_CONVERT_ENTITY
|
||
if (ch!=m_chEsc)
|
||
{
|
||
#endif
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
|
||
#ifdef OXPARSER_CONVERT_ENTITY
|
||
}
|
||
else
|
||
{
|
||
BOOL bContinue=TRUE;
|
||
COXQuickString sEsc;
|
||
do
|
||
{
|
||
ch=GetNextChar();
|
||
if (!ch || ch==chQuoteChar)
|
||
bContinue=FALSE;
|
||
else
|
||
if (ch!=TEXT(';'))
|
||
sEsc.Append(ch);
|
||
}
|
||
while(bContinue && ch!=TEXT(';'));
|
||
if (bContinue)
|
||
{
|
||
LPCTSTR lpSymb=GetLiteralString((LPCTSTR) sEsc);
|
||
if (lpSymb)
|
||
{
|
||
str.Append(*lpSymb);
|
||
}
|
||
else
|
||
{
|
||
str.Append(TEXT('&'));
|
||
str.AddString((LPCTSTR) sEsc);
|
||
str.Append(TEXT(';'));
|
||
}
|
||
ch=GetNextChar();
|
||
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (ch != chQuoteChar)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing string."));
|
||
str.Empty();
|
||
return FALSE;
|
||
}
|
||
TRACE1("%s\n",(LPCTSTR) str);
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::GetToken(COXToken& token, BOOL bWarnOnEOF /*=FALSE*/)
|
||
{
|
||
if (!RemoveWhiteSpace())
|
||
return FALSE;
|
||
|
||
token.SetType(COXToken::UNKNOWN);
|
||
|
||
TCHAR ch = GetNextChar(1, bWarnOnEOF);
|
||
if (!ch)
|
||
return FALSE;
|
||
|
||
// "<"
|
||
if (ch == m_chStartDelim)
|
||
{
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (!ch)
|
||
return FALSE;
|
||
|
||
// "<?"
|
||
if (ch == m_chProcInstr)
|
||
{
|
||
token.SetType(COXToken::OPEN_PROCINSTR_BRACKET);
|
||
}
|
||
// "</"
|
||
else if (ch == m_chTagEnd)
|
||
{
|
||
token.SetType(COXToken::OPEN_ENDTAG_BRACKET);
|
||
}
|
||
// "<!"
|
||
else if (ch == m_chMarkup)
|
||
{
|
||
token.SetType(COXToken::OPEN_MARKUP_BRACKET);
|
||
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (ch != m_chDash)
|
||
UngetChar();
|
||
else
|
||
{
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (ch != m_chDash)
|
||
UngetChar(2);
|
||
else
|
||
token.SetType(COXToken::OPEN_COMMENT_BRACKET);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UngetChar();
|
||
token.SetType(COXToken::OPEN_TAG_BRACKET);
|
||
}
|
||
}
|
||
|
||
// ">"
|
||
else if (ch == m_chEndDelim)
|
||
{
|
||
token.SetType(COXToken::CLOSE_TAG_BRACKET);
|
||
}
|
||
|
||
// "/>"
|
||
else if (ch == m_chTagEnd)
|
||
{
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (!ch)
|
||
return FALSE;
|
||
|
||
if (ch == m_chEndDelim)
|
||
token.SetType(COXToken::CLOSE_EMPTYTAG_BRACKET);
|
||
else
|
||
{
|
||
UngetChar(2);
|
||
token.SetType(COXToken::STRING);
|
||
}
|
||
}
|
||
|
||
// "="
|
||
else if (ch == TEXT('='))
|
||
{
|
||
token.SetType(COXToken::EQUAL_SIGN);
|
||
}
|
||
|
||
// """
|
||
else if (ch == TEXT('"'))
|
||
{
|
||
token.SetType(COXToken::QUOTE);
|
||
}
|
||
|
||
// "'"
|
||
else if (ch == TEXT('\''))
|
||
{
|
||
token.SetType(COXToken::APOSTROPHE);
|
||
}
|
||
|
||
// "-->"
|
||
else if (ch == m_chDash)
|
||
{
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (ch != m_chDash)
|
||
{
|
||
UngetChar(2);
|
||
token.SetType(COXToken::STRING);
|
||
}
|
||
else
|
||
{
|
||
ch = GetNextChar(1, bWarnOnEOF);
|
||
if (ch != m_chEndDelim)
|
||
{
|
||
UngetChar(3);
|
||
token.SetType(COXToken::STRING);
|
||
}
|
||
else
|
||
token.SetType(COXToken::CLOSE_COMMENT_BRACKET);
|
||
}
|
||
}
|
||
|
||
// Plain ol' text
|
||
else
|
||
{
|
||
UngetChar();
|
||
token.SetType(COXToken::STRING);
|
||
}
|
||
|
||
#if defined(_DEBUG) && 0
|
||
USES_CONVERSION;
|
||
LPCSTR strToken = NULL;
|
||
switch (token.GetType())
|
||
{
|
||
case COXToken::OPEN_PROCINSTR_BRACKET:
|
||
strToken = "OPEN_PROCINSTR_BRACKET";
|
||
break;
|
||
case COXToken::OPEN_ENDTAG_BRACKET:
|
||
strToken = "OPEN_ENDTAG_BRACKET";
|
||
break;
|
||
case COXToken::OPEN_COMMENT_BRACKET:
|
||
strToken = "OPEN_COMMENT_BRACKET";
|
||
break;
|
||
case COXToken::OPEN_MARKUP_BRACKET:
|
||
strToken = "OPEN_MARKUP_BRACKET";
|
||
break;
|
||
case COXToken::OPEN_TAG_BRACKET:
|
||
strToken = "OPEN_TAG_BRACKET";
|
||
break;
|
||
case COXToken::CLOSE_TAG_BRACKET:
|
||
strToken = "CLOSE_TAG_BRACKET";
|
||
break;
|
||
case COXToken::CLOSE_EMPTYTAG_BRACKET:
|
||
strToken = "CLOSE_EMPTYTAG_BRACKET";
|
||
break;
|
||
case COXToken::EQUAL_SIGN:
|
||
strToken = "EQUAL_SIGN";
|
||
break;
|
||
case COXToken::QUOTE:
|
||
strToken = "QUOTE";
|
||
break;
|
||
case COXToken::APOSTROPHE:
|
||
strToken = "APOSTROPHE";
|
||
break;
|
||
case COXToken::CLOSE_COMMENT_BRACKET:
|
||
strToken = "CLOSE_COMMENT_BRACKET";
|
||
break;
|
||
case COXToken::STRING:
|
||
strToken = "STRING";
|
||
break;
|
||
case COXToken::UNKNOWN:
|
||
default:
|
||
ASSERT(FALSE);
|
||
}
|
||
TRACE3("COXParser: Found %s token at Line %d, Col %d.\n",
|
||
(LPCSTR) strToken, m_nLine, m_nColumn);
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void COXParser::AddObjectToElement(COXParserElement* pElement,
|
||
COXParserObject* pObject)
|
||
{
|
||
if (pObject)
|
||
pElement->AddObject(pObject);
|
||
}
|
||
|
||
BOOL COXParser::ParseAttributes(COXParserElement* pElement)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
COXAttribute* pAttribute = NULL;
|
||
|
||
do
|
||
{
|
||
// What is next in line? If it's not a string value,
|
||
//it's not a name/value pair
|
||
pAttribute = NULL;
|
||
bResult = GetToken(m_Token, TRUE);
|
||
if (!bResult || m_Token.GetType() != COXToken::STRING)
|
||
break;
|
||
|
||
// Create a new name Attribute
|
||
pAttribute = new COXAttribute;
|
||
if (!pAttribute)
|
||
{
|
||
ReportError(ERROR_OUT_OF_MEMORY,
|
||
TEXT("Unable to create new attribute (element %s)"),
|
||
pElement->GetName());
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
// Get the name of the name/value pair
|
||
COXQuickString str;
|
||
if (!GetNameToken(str))
|
||
{
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
pAttribute->SetName(str);
|
||
|
||
// Search for a "=" sign next
|
||
if (!GetToken(m_Token))
|
||
{
|
||
ReportError(ERROR_BAD_TOKEN,
|
||
TEXT("Error while parsing attribute (element %s, name %s)."),
|
||
pElement->GetName(), pAttribute->GetName());
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
if (m_Token.GetType() != COXToken::EQUAL_SIGN)
|
||
{
|
||
ReportError(ERROR_UNEXPECTED_TOKEN,
|
||
TEXT("Unexpected token while parsing attribute (element %s, name %s)."),
|
||
pElement->GetName(), pAttribute->GetName());
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
// Should have a number, "string" or 'string' value next.
|
||
if (!GetToken(m_Token))
|
||
{
|
||
ReportError(ERROR_BAD_TOKEN,
|
||
TEXT("Error while parsing attribute (element %s, name %s)."),
|
||
pElement->GetName(), pAttribute->GetName());
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (m_Token.GetType() == COXToken::STRING)
|
||
{
|
||
int nValue;
|
||
if (!GetNumberToken(nValue))
|
||
{
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
pAttribute->SetValue(nValue);
|
||
}
|
||
else if (m_Token.GetType() == COXToken::QUOTE)
|
||
{
|
||
if ( !GetStringToken(str, TEXT('"')) )
|
||
{
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
pAttribute->SetValue(str);
|
||
}
|
||
else if (m_Token.GetType() == COXToken::APOSTROPHE)
|
||
{
|
||
if ( !GetStringToken(str, TEXT('\'')) )
|
||
{
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
pAttribute->SetValue(str);
|
||
}
|
||
else
|
||
{
|
||
ReportError(ERROR_UNEXPECTED_TOKEN,
|
||
TEXT("Unexpected token while parsing attribute (element %s, name %s)."),
|
||
pElement->GetName(), pAttribute->GetName());
|
||
bResult = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (bResult)
|
||
pElement->AddAttribute(pAttribute);
|
||
|
||
} while (bResult);
|
||
|
||
if (bResult)
|
||
return TRUE;
|
||
else
|
||
{
|
||
delete pAttribute;
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
COXParserElement* COXParser::ParseStartTag(COXParserElement* pParent,
|
||
BOOL& bEmptyTag)
|
||
{
|
||
// Get tag name
|
||
if (!GetToken(m_Token))
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while searching for tag name."));
|
||
return NULL;
|
||
}
|
||
|
||
if (m_Token.GetType() != COXToken::STRING)
|
||
{
|
||
ReportError(ERROR_UNEXPECTED_TOKEN,
|
||
TEXT("Expecting tag name - none found."));
|
||
return NULL;
|
||
}
|
||
|
||
// Get the name of the name/value pair
|
||
COXQuickString str;
|
||
if (!GetNameToken(str))
|
||
return NULL;
|
||
|
||
COXParserElement* pElement = new COXParserElement(pParent, str);
|
||
if (!pElement)
|
||
{
|
||
ReportError(ERROR_OUT_OF_MEMORY,
|
||
TEXT("Unable to create new parser Element"));
|
||
return NULL;
|
||
}
|
||
|
||
// Search through for attributes
|
||
BOOL bResult = ParseAttributes(pElement);
|
||
|
||
if (bResult)
|
||
{
|
||
bEmptyTag = FALSE;
|
||
|
||
if (m_Token.GetType() == COXToken::CLOSE_TAG_BRACKET)
|
||
/* do nothing */;
|
||
else if (m_Token.GetType() == COXToken::CLOSE_EMPTYTAG_BRACKET)
|
||
bEmptyTag = TRUE;
|
||
else
|
||
{
|
||
bResult = FALSE;
|
||
ReportError(ERROR_MISSING_END_BRACKET,
|
||
TEXT("Closing bracket for start tag '%s' not found."),
|
||
pElement->GetName());
|
||
}
|
||
}
|
||
|
||
if (!bResult)
|
||
{
|
||
delete pElement;
|
||
return NULL;
|
||
}
|
||
else
|
||
return pElement;
|
||
}
|
||
|
||
BOOL COXParser::IsEndTagMissing(LPCTSTR szCurrentTag, LPCTSTR szNewTag,
|
||
BOOL NewTagIsEndTag)
|
||
{
|
||
UNUSED_ALWAYS(szCurrentTag);
|
||
UNUSED_ALWAYS(szNewTag);
|
||
UNUSED_ALWAYS(NewTagIsEndTag);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXParser::IgnoreStartTag(COXParserElement* pElement,
|
||
BOOL bEmptyTag)
|
||
{
|
||
UNUSED_ALWAYS(pElement);
|
||
UNUSED_ALWAYS(bEmptyTag);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXParser::IgnoreEndTag(LPCTSTR szEndTag)
|
||
{
|
||
UNUSED_ALWAYS(szEndTag);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
BOOL COXParser::ParseEndTag(COXParserElement* pElement,
|
||
COXQuickString& strEndTag)
|
||
{
|
||
UNUSED_ALWAYS(pElement);
|
||
|
||
if (!GetToken(m_Token, TRUE))
|
||
{
|
||
ReportError(ERROR_BAD_TOKEN,
|
||
TEXT("Unexpected end of buffer while parsing endtag."));
|
||
return FALSE;
|
||
}
|
||
|
||
if (m_Token.GetType() != COXToken::STRING)
|
||
{
|
||
ReportError(ERROR_UNEXPECTED_TOKEN, TEXT("Missing end tag name."));
|
||
return FALSE;
|
||
}
|
||
|
||
// Get the end tag name
|
||
if (!GetNameToken(strEndTag))
|
||
return FALSE;
|
||
|
||
// peel off the final ">"
|
||
if (!GetToken(m_Token) || m_Token.GetType() != COXToken::CLOSE_TAG_BRACKET)
|
||
{
|
||
ReportError(ERROR_BAD_TOKEN,
|
||
TEXT("Missing end bracket while parsing end tag '%s'."),
|
||
(LPCTSTR) strEndTag);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
COXParserObject* COXParser::ParseProcessingInstruction(
|
||
COXParserElement* pParent)
|
||
{
|
||
COXQuickString str;
|
||
str.SetLength(100);
|
||
|
||
TCHAR ch = GetNextChar();
|
||
while (ch)
|
||
{
|
||
TCHAR chNextChar = GetNextChar();
|
||
if (!chNextChar)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing processing instruction."));
|
||
return NULL;
|
||
}
|
||
if (ch == m_chProcInstr && chNextChar == m_chEndDelim)
|
||
ch = m_chNULL;
|
||
else
|
||
{
|
||
str.Append(ch);
|
||
ch = chNextChar;
|
||
}
|
||
}
|
||
|
||
return new COXParserObject(pParent, COXParserObject::PROCINSTR, str);
|
||
}
|
||
|
||
COXParserObject* COXParser::ParseMarkup(COXParserElement* pParent)
|
||
{
|
||
int nMarkupType = COXParserObject::MARKUP;
|
||
|
||
COXQuickString str;
|
||
str.SetLength(100);
|
||
|
||
if (!_tcsncmp(m_pBuf, TEXT("DOCTYPE"), 7))
|
||
{
|
||
TCHAR ch = GetNextChar();
|
||
// Parse initial bit before DTD info
|
||
while (ch && ch != m_chEndDelim && ch != TEXT('['))
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
// Parse DTD info
|
||
if (ch == TEXT('['))
|
||
{
|
||
BOOL bOpened=FALSE;
|
||
CString sEntity;
|
||
LPTSTR pData=m_pBuf;
|
||
TCHAR chSpace=TEXT(' ');
|
||
TCHAR chTab=TEXT('\t');
|
||
while (ch && ch != TEXT(']'))
|
||
{
|
||
if (!bOpened)
|
||
{
|
||
if (ch==TCHAR(0x3c))
|
||
{
|
||
bOpened=TRUE;
|
||
pData=m_pBuf;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (ch==TCHAR(0x3e))
|
||
{
|
||
if (sEntity.GetLength()>8)
|
||
{
|
||
int nRet=CompareString(LOCALE_SYSTEM_DEFAULT,
|
||
NORM_IGNORECASE,(LPCTSTR) sEntity,8,
|
||
_T("!ENTITY "),8);
|
||
|
||
if (CSTR_EQUAL==nRet)
|
||
{
|
||
//found entity
|
||
sEntity=sEntity.Right(sEntity.GetLength()-8);
|
||
CString sLiteral=sEntity;
|
||
int nSpace=sEntity.Find(_T(" "));
|
||
if (nSpace!=-1)
|
||
{
|
||
pData=pData+nSpace+8;
|
||
while (*pData==chSpace ||
|
||
*pData==chTab)
|
||
pData++;
|
||
sEntity=sEntity.Left(nSpace);
|
||
sLiteral=sLiteral.Right(sLiteral.GetLength()-nSpace);
|
||
sLiteral.TrimLeft();
|
||
m_EntityTable.Add((LPCTSTR) sEntity,(DWORD)(INT_PTR) pData);
|
||
}
|
||
}
|
||
}
|
||
sEntity="";
|
||
bOpened=FALSE;
|
||
}
|
||
else
|
||
{
|
||
sEntity+=ch;
|
||
}
|
||
}
|
||
ch = GetNextChar();
|
||
}
|
||
}
|
||
// Parse any remaining left over stuff
|
||
while (ch && ch != m_chEndDelim)
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
if (!ch)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing DOCTYPE entry."));
|
||
return NULL;
|
||
}
|
||
}
|
||
else if (!_tcsncmp(m_pBuf, TEXT("[CDATA["), 7))
|
||
{
|
||
TCHAR ch = GetNextChar(7+1);
|
||
while (ch)
|
||
{
|
||
TCHAR chNextChar1 = GetNextChar();
|
||
TCHAR chNextChar2 = GetNextChar();
|
||
|
||
if (!chNextChar1 || !chNextChar2)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while CDATA."));
|
||
return NULL;
|
||
}
|
||
if (ch == TEXT(']') && chNextChar1 == TEXT(']') && chNextChar2 == m_chEndDelim)
|
||
ch = m_chNULL;
|
||
else
|
||
{
|
||
str.Append(ch);
|
||
ch = chNextChar1;
|
||
UngetChar();
|
||
}
|
||
}
|
||
nMarkupType = COXParserObject::CDATA;
|
||
}
|
||
else
|
||
{
|
||
TCHAR ch = GetNextChar();
|
||
while (ch && ch != m_chEndDelim)
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
if (!ch)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing markup."));
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
return new COXParserObject(pParent, nMarkupType, str);
|
||
}
|
||
|
||
COXParserObject* COXParser::ParseComment(COXParserElement* pParent)
|
||
{
|
||
COXQuickString str;
|
||
str.SetLength(100);
|
||
|
||
TCHAR ch = GetNextChar();
|
||
while (ch)
|
||
{
|
||
TCHAR chNextChar1 = GetNextChar();
|
||
TCHAR chNextChar2 = GetNextChar();
|
||
|
||
if (!chNextChar1 || !chNextChar2)
|
||
{
|
||
ReportError(ERROR_END_OF_BUFFER,
|
||
TEXT("Unexpected end of buffer while parsing comment."));
|
||
return NULL;
|
||
}
|
||
if (ch == m_chDash && chNextChar1 == m_chDash && chNextChar2 == m_chEndDelim)
|
||
ch = m_chNULL;
|
||
else
|
||
{
|
||
str.Append(ch);
|
||
ch = chNextChar1;
|
||
UngetChar();
|
||
}
|
||
}
|
||
|
||
return new COXParserObject(pParent, COXParserObject::COMMENT, str);
|
||
}
|
||
|
||
LPCTSTR COXParser::GetLiteralString(LPCTSTR szStr)
|
||
{
|
||
static TCHAR szLiteral[2] = { 0,0 };
|
||
szLiteral[0] = TCHAR(0);
|
||
|
||
// Decode numeric character references
|
||
if (szStr[0] == TEXT('#'))
|
||
{
|
||
BOOL bHexValue = ( szStr[1] == TEXT('x') || szStr[1] == TEXT('X') );
|
||
LPCTSTR szNumber = (bHexValue)? szStr+2 : szStr+1;
|
||
|
||
// Check it's valid
|
||
for (LPCTSTR ptr = szNumber; *ptr; ptr++)
|
||
{
|
||
if (! ( (bHexValue)? _istxdigit(*ptr) : _istdigit(*ptr) ) )
|
||
return NULL;
|
||
}
|
||
|
||
TCHAR ch;
|
||
#if _MSC_VER >= 1400
|
||
if (!_stscanf_s(szNumber, (bHexValue)? TEXT("%x") : TEXT("%d"), &ch))
|
||
#else
|
||
if (!_stscanf(szNumber, (bHexValue)? TEXT("%x") : TEXT("%d"), &ch))
|
||
#endif
|
||
return NULL;
|
||
|
||
szLiteral[0] = ch;
|
||
|
||
return szLiteral;
|
||
}
|
||
|
||
// Decode character entity references
|
||
DWORD dw;
|
||
if (!m_EntityTable.Lookup(szStr, dw))
|
||
return NULL;
|
||
|
||
return (LPCTSTR) (INT_PTR)dw;
|
||
}
|
||
|
||
LPTSTR COXParser::GetCharEntity(TCHAR ch)
|
||
{
|
||
static TCHAR szEntity[256];
|
||
|
||
HASH_POS pos = m_EntityTable.GetStartPosition();
|
||
while (pos)
|
||
{
|
||
LPCTSTR szName = NULL;
|
||
DWORD dwData = 0;
|
||
m_EntityTable.GetNextAssoc(pos, szName, dwData);
|
||
|
||
LPCTSTR szLiteral = (LPCTSTR)(INT_PTR) dwData;
|
||
|
||
if (ch == szLiteral[0] && szLiteral[1] == m_chNULL)
|
||
{
|
||
UTBStr::stprintf(szEntity, 256, TEXT("&%s;"), szName);
|
||
return szEntity;
|
||
}
|
||
}
|
||
|
||
// unable to find - so just return the character as a string
|
||
szEntity[0] = ch;
|
||
szEntity[1] = TEXT('\0');
|
||
|
||
return szEntity;
|
||
}
|
||
|
||
const COXQuickString COXParser::EncodeText(LPCTSTR szStr)
|
||
{
|
||
COXQuickString str;
|
||
|
||
if (!szStr || !szStr[0])
|
||
return str;
|
||
|
||
// An initial guess of the string size
|
||
str.SetLength(PtrToUint(_tcslen(szStr)+1));
|
||
|
||
while (*szStr)
|
||
str.AddString(GetCharEntity(*szStr++));
|
||
|
||
return str;
|
||
}
|
||
|
||
COXParserObject* COXParser::ParseText(COXParserElement* pParent)
|
||
{
|
||
COXQuickString str;
|
||
str.SetLength(100);
|
||
|
||
TCHAR ch = GetNextChar();
|
||
while (ch && ch != m_chStartDelim)
|
||
{
|
||
// Process any entities found
|
||
if (ch == m_chEsc)
|
||
{
|
||
// Move past the '&'
|
||
ch = GetNextChar();
|
||
|
||
COXQuickString strEntity;
|
||
while (ch && ch != m_chStartDelim && ch != TEXT(';') && !_istspace(ch))
|
||
{
|
||
strEntity.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
|
||
// Final char should be the ';'
|
||
if (ch != TEXT(';'))
|
||
{
|
||
ReportError(ERROR_BAD_ENTITY,
|
||
TEXT("Missing ';' on character entity '&%s'."),
|
||
(LPCTSTR) strEntity);
|
||
str.Append(TEXT('&'));
|
||
str += strEntity;
|
||
}
|
||
else
|
||
{
|
||
LPCTSTR szEntity = GetLiteralString(strEntity);
|
||
if (szEntity)
|
||
InsertEntityValue(str,szEntity);
|
||
else
|
||
{
|
||
ReportError(ERROR_BAD_ENTITY,
|
||
TEXT("Bad character entity '&%s;' encountered."),
|
||
(LPCTSTR) strEntity);
|
||
str.Append(TEXT('&'));
|
||
str += strEntity;
|
||
str.Append(TEXT(';'));
|
||
}
|
||
ch = GetNextChar();
|
||
}
|
||
|
||
// finished processing entity - ch will be the char directly
|
||
// after the entity
|
||
}
|
||
else
|
||
{
|
||
str.Append(ch);
|
||
ch = GetNextChar();
|
||
}
|
||
}
|
||
|
||
if (!ch)
|
||
{
|
||
//ReportError(ERROR_END_OF_BUFFER,
|
||
// TEXT("Unexpected end of buffer while parsing text."));
|
||
//return NULL;
|
||
}
|
||
else
|
||
UngetChar();
|
||
|
||
str.Trim();
|
||
|
||
return new COXParserObject(pParent,
|
||
COXParserObject::PLAINTEXT, str.GetString());
|
||
}
|
||
|
||
|
||
BOOL COXParser::ParseElement(COXParserElement *pElement, int nLevel)
|
||
{
|
||
SAVEPOS pos;
|
||
SaveBufferPos(pos);
|
||
|
||
COXParserObject* pObject;
|
||
BOOL bFoundMatchingEndTag;
|
||
|
||
BOOL bContinue = TRUE;
|
||
BOOL bResult = FALSE;
|
||
|
||
do
|
||
{
|
||
// The new object that will be added to this element
|
||
pObject = NULL;
|
||
bFoundMatchingEndTag = FALSE;
|
||
|
||
// Get the first token of the next object. Only report errors for
|
||
// elements that are at least a level above the root element
|
||
bResult = GetToken(m_Token, FALSE);
|
||
if (!bResult)
|
||
{
|
||
bContinue = FALSE;
|
||
bResult = TRUE;
|
||
break;
|
||
}
|
||
|
||
switch (m_Token.GetType())
|
||
{
|
||
case COXToken::OPEN_PROCINSTR_BRACKET: // Processing instruction
|
||
pObject = ParseProcessingInstruction(pElement);
|
||
bResult = bContinue = (pObject != NULL);
|
||
break;
|
||
|
||
case COXToken::OPEN_MARKUP_BRACKET: // Markup
|
||
pObject = ParseMarkup(pElement);
|
||
bResult = bContinue = (pObject != NULL);
|
||
break;
|
||
|
||
case COXToken::OPEN_COMMENT_BRACKET: // Comment
|
||
pObject = ParseComment(pElement);
|
||
bResult = bContinue = (pObject != NULL);
|
||
break;
|
||
|
||
case COXToken::QUOTE:
|
||
case COXToken::APOSTROPHE:
|
||
UngetChar();
|
||
// Fall through
|
||
case COXToken::STRING: // Plain ol' text
|
||
case COXToken::EQUAL_SIGN:
|
||
pObject = ParseText(pElement);
|
||
bResult = bContinue = (pObject != NULL);
|
||
break;
|
||
|
||
case COXToken::OPEN_TAG_BRACKET: // New element
|
||
{
|
||
BOOL bEmptyTag = FALSE;
|
||
pObject = ParseStartTag(pElement, bEmptyTag);
|
||
if (!pObject)
|
||
{
|
||
bResult = bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
// Allow derived classes to simply ignore this element. This is
|
||
// useful if you wish to treat tags as "toggles" (eg <b> in
|
||
// HTML), instead of actual XML nodes.
|
||
if (IgnoreStartTag((COXParserElement*) pObject,
|
||
bEmptyTag))
|
||
{
|
||
delete pObject;
|
||
pObject = NULL;
|
||
break;
|
||
}
|
||
|
||
// Allow derived classes to close off this element and start a
|
||
// new one if there is a missing end tag.
|
||
// If we have a missing end tag, then we end processing of the
|
||
// current element, restore the buffer to the point just before
|
||
// this open tag, and return from this function
|
||
if (nLevel > 0 &&
|
||
IsEndTagMissing(pElement->GetName(),
|
||
pObject->GetText(), FALSE))
|
||
{
|
||
// Pretend we never saw this tag, and just quit as if
|
||
// we had reached an end tag
|
||
RestoreBufferPos(pos);
|
||
|
||
delete pObject;
|
||
pObject = NULL;
|
||
|
||
bFoundMatchingEndTag = TRUE;
|
||
bResult = TRUE;
|
||
bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
// If not an empty element, then try and parse the element.
|
||
if ( !bEmptyTag &&
|
||
!ParseElement((COXParserElement*) pObject,
|
||
nLevel+1) )
|
||
{
|
||
delete pObject;
|
||
pObject = NULL;
|
||
|
||
bResult = bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
bResult = TRUE;
|
||
}
|
||
break;
|
||
|
||
// An end tag should halt processing - but we allow some leniency here.
|
||
// We allow derived classes to decide when to cease processing this
|
||
// element, by either return FALSE in ParseEndTag, or setting
|
||
// bFoundMatchingEndTag to TRUE (useful for HTML...)
|
||
case COXToken::OPEN_ENDTAG_BRACKET: // </...
|
||
{
|
||
// Get the end tag
|
||
COXQuickString strEndTag;
|
||
if (!ParseEndTag(pElement, strEndTag))
|
||
{
|
||
bResult = bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
// Allow derived classes to simply ignore this tag. This is
|
||
// useful if you wish to treat tags as "toggles" (eg </b> in
|
||
// HTML), instead of actual XML nodes.
|
||
if (IgnoreEndTag(strEndTag))
|
||
break;
|
||
|
||
// If we are at top level, then issue a warning but continue
|
||
if (nLevel == 0)
|
||
{
|
||
ReportError(WARNING_UNEXPECTED_END_TAG,
|
||
TEXT("Unexpected end tag '%s'."), (LPCTSTR) strEndTag);
|
||
break;
|
||
}
|
||
|
||
// If it matches the current element, then we finish processing here
|
||
if (strEndTag.Compare(pElement->GetName(), FALSE))
|
||
{
|
||
bFoundMatchingEndTag = TRUE;
|
||
bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (IsEndTagMissing(pElement->GetName(),
|
||
strEndTag, TRUE))
|
||
{
|
||
// Pretend we never saw this tag, and just quit as if
|
||
// we had reached an end tag
|
||
RestoreBufferPos(pos);
|
||
bFoundMatchingEndTag = TRUE;
|
||
bContinue = FALSE;
|
||
break;
|
||
}
|
||
|
||
ReportError(ERROR_MISSING_END_TAG,
|
||
TEXT("Expecting end tag '%s'. Found end tag '%s' instead."),
|
||
pElement->GetName(), (LPCTSTR) strEndTag);
|
||
|
||
if (m_bErrorOnMissingTag)
|
||
bContinue = bResult = FALSE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ReportError(ERROR_UNEXPECTED_TOKEN, TEXT("Unexpected token."));
|
||
//ReportError(ERROR_UNEXPECTED_TOKEN, TEXT("Unexpected token '%s'."),
|
||
// m_Token.GetText());
|
||
bResult = bContinue = FALSE;
|
||
}
|
||
|
||
if (bResult)
|
||
AddObjectToElement(pElement, pObject);
|
||
|
||
SaveBufferPos(pos);
|
||
|
||
} while (bContinue);
|
||
|
||
// Error?
|
||
if (!bResult)
|
||
{
|
||
delete pObject;
|
||
return FALSE;
|
||
}
|
||
|
||
// Will only get this far if all tags for all elements have been closed, or
|
||
// if the trailing end tags for elements are missing
|
||
|
||
// If this is the root element, then there is no need to check for missing end tags
|
||
if (nLevel == 0)
|
||
return TRUE;
|
||
else
|
||
{
|
||
// Derived classes may wish to allow missing end tags
|
||
if (!bFoundMatchingEndTag && !IsEndTagMissing(pElement->GetName(), NULL, TRUE))
|
||
{
|
||
ReportError(ERROR_MISSING_END_TAG, TEXT("Missing end tag '%s'."),
|
||
pElement->GetText());
|
||
return FALSE;
|
||
}
|
||
else
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
BOOL COXParser::ParseFile(LPCTSTR szFile)
|
||
{
|
||
LPTSTR ptr = LoadFile(szFile);
|
||
if (!ptr)
|
||
return FALSE;
|
||
|
||
BOOL bResult = Parse(ptr);
|
||
|
||
GlobalFree(ptr);
|
||
|
||
return bResult;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// File handling routines
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
BOOL COXParser::WriteFile(LPCTSTR szFile)
|
||
{
|
||
if (!Root() || !Root()->NumObjects())
|
||
return FALSE;
|
||
|
||
HANDLE hFile = CreateFile(szFile, GENERIC_WRITE,
|
||
0, NULL, CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL);
|
||
|
||
if (INVALID_HANDLE_VALUE == hFile)
|
||
return FALSE;
|
||
|
||
for (int i = 0; i < Root()->NumObjects(); i++)
|
||
WriteObject(hFile, Root()->Object(i), 0);
|
||
|
||
CloseHandle(hFile);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteTabs(HANDLE hFile, int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
DWORD nCount;
|
||
for (int i = 0; i < nLevel; i++)
|
||
{
|
||
if (!::WriteFile(hFile, T2A((LPTSTR) m_szTab),
|
||
PtrToLong(_tcslen(m_szTab)), &nCount, NULL))
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteAttributes(HANDLE hFile,
|
||
COXParserElement* pElement)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
static char buffer[512];
|
||
static DWORD nCount;
|
||
|
||
for (int i = 0; i < pElement->NumAttributes(); i++)
|
||
{
|
||
COXAttribute* pAttribute = pElement->Attribute(i);
|
||
if (!pAttribute) continue;
|
||
|
||
UTBStr::sprintf(buffer, 512, " %s=", T2A((LPTSTR) pAttribute->GetName()));
|
||
if (!::WriteFile(hFile, buffer, PtrToLong(strlen(buffer)), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (pAttribute->GetAttributeType() == COXAttribute::ATTR_STRING)
|
||
UTBStr::sprintf(buffer, 512, "\"%s\"", T2A((LPTSTR) pAttribute->GetStringValue()));
|
||
else if (pAttribute->GetAttributeType() == COXAttribute::ATTR_INTEGER)
|
||
UTBStr::sprintf(buffer, 512, "%d", pAttribute->GetIntValue());
|
||
else
|
||
UTBStr::sprintf(buffer, 512, "\"\"");
|
||
|
||
if (!::WriteFile(hFile, buffer, PtrToLong(strlen(buffer)), &nCount, NULL))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteCData(HANDLE hFile, COXParserObject* pObject,
|
||
int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
if (pObject->GetType() != COXParserObject::CDATA)
|
||
return FALSE;
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
DWORD nCount;
|
||
if (!::WriteFile(hFile, "<![CDATA[", 9, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, T2A((LPTSTR)pObject->GetText()),
|
||
PtrToLong(_tcslen(pObject->GetText())), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, "]]>\r\n", 5, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteComment(HANDLE hFile, COXParserObject* pObject,
|
||
int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
if (pObject->GetType() != COXParserObject::COMMENT)
|
||
return FALSE;
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
DWORD nCount;
|
||
if (!::WriteFile(hFile, "<!--", 4, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, T2A((LPTSTR)pObject->GetText()),
|
||
PtrToLong(_tcslen(pObject->GetText())), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, "-->\r\n", 5, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteElement(HANDLE hFile, COXParserElement* pElement,
|
||
int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
static char buffer[512];
|
||
static DWORD nCount;
|
||
|
||
if (pElement->GetType() != COXParserObject::ELEMENT)
|
||
return FALSE;
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
UTBStr::sprintf(buffer, 512, "<%s", T2A((LPTSTR)pElement->GetName()));
|
||
if (!::WriteFile(hFile, buffer, PtrToLong(strlen(buffer)), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!WriteAttributes(hFile, pElement))
|
||
return FALSE;
|
||
|
||
if (pElement->NumObjects() == 0) // empty tag
|
||
{
|
||
if (!::WriteFile(hFile, "/>\r\n", 4, &nCount, NULL))
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
if (!::WriteFile(hFile, ">\r\n", 3, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
for (int i = 0; i < pElement->NumObjects(); i++)
|
||
WriteObject(hFile, pElement->Object(i), nLevel+1);
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
UTBStr::sprintf(buffer, 512, "</%s>\r\n", T2A((LPTSTR)pElement->GetName()));
|
||
if (!::WriteFile(hFile, buffer, PtrToLong(strlen(buffer)), &nCount, NULL))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteMarkup(HANDLE hFile, COXParserObject* pObject,
|
||
int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
if (pObject->GetType() != COXParserObject::MARKUP)
|
||
return FALSE;
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
DWORD nCount;
|
||
if (!::WriteFile(hFile, "<!", 2, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, T2A((LPTSTR)pObject->GetText()),
|
||
PtrToLong(_tcslen(pObject->GetText())), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, ">\r\n", 3, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteProcessingInstruction(HANDLE hFile,
|
||
COXParserObject* pObject, int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
if (pObject->GetType() != COXParserObject::PROCINSTR)
|
||
return FALSE;
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
DWORD nCount;
|
||
if (!::WriteFile(hFile, "<?", 2, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, T2A((LPTSTR)pObject->GetText()),
|
||
PtrToLong(_tcslen(pObject->GetText())), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, "?>\r\n", 4, &nCount, NULL))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteText(HANDLE hFile, COXParserObject* pObject,
|
||
int nLevel)
|
||
{
|
||
USES_CONVERSION;
|
||
|
||
if (pObject->GetType() != COXParserObject::PLAINTEXT &&
|
||
pObject->GetType() != COXParserObject::RAWTEXT )
|
||
return FALSE;
|
||
|
||
COXQuickString str;
|
||
if ( pObject->GetType() == COXParserObject::RAWTEXT )
|
||
str = pObject->GetText();
|
||
else
|
||
str = EncodeText(pObject->GetText());
|
||
|
||
str.Trim();
|
||
if (str.IsEmpty())
|
||
return FALSE;
|
||
|
||
LPCTSTR ptr = str.GetString();
|
||
COXQuickString strLine;
|
||
LPCTSTR pStart;
|
||
|
||
while (*ptr)
|
||
{
|
||
strLine.Empty();
|
||
|
||
BOOL bContinue = TRUE;
|
||
while (bContinue)
|
||
{
|
||
pStart = ptr;
|
||
|
||
// Search for the next white space
|
||
while (*ptr && !_istspace(*ptr)) ptr++;
|
||
//if (*ptr) ptr++;
|
||
|
||
int nCharsStepped = PtrToInt(ptr - pStart + 1);
|
||
|
||
// If we have less than a lines worth, or if we have more than a lines
|
||
// worth and have not added anything previously, add to the current line
|
||
if ( strLine.GetLength() + nCharsStepped < m_nLineLength ||
|
||
strLine.IsEmpty() )
|
||
{
|
||
strLine.AddString(pStart, nCharsStepped);
|
||
|
||
// If we've still got room on the line, and we are not at EOF or EOL
|
||
// then continue on
|
||
bContinue = (strLine.GetLength() < m_nLineLength &&
|
||
*ptr && *ptr != TEXT('\n'));
|
||
|
||
if (*ptr) ptr++;
|
||
|
||
bContinue = (bContinue && *ptr);
|
||
}
|
||
else
|
||
{
|
||
// We could not add this section - so go back to the start of it
|
||
// and print out what we already have.
|
||
ptr = pStart;
|
||
bContinue = FALSE;
|
||
}
|
||
}
|
||
|
||
if (!WriteTabs(hFile, nLevel))
|
||
return FALSE;
|
||
|
||
DWORD nCount;
|
||
if (!::WriteFile(hFile, T2A((LPTSTR)strLine.GetString()),
|
||
strLine.GetLength(), &nCount, NULL))
|
||
return FALSE;
|
||
|
||
if (!::WriteFile(hFile, "\r\n", 2, &nCount, NULL))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXParser::WriteObject(HANDLE hFile, COXParserObject* pObject,
|
||
int nLevel)
|
||
{
|
||
BOOL bResult = FALSE;
|
||
switch (pObject->GetType())
|
||
{
|
||
case COXParserObject::CDATA:
|
||
bResult = WriteCData(hFile, pObject, nLevel);
|
||
break;
|
||
|
||
case COXParserObject::COMMENT:
|
||
bResult = WriteComment(hFile, pObject, nLevel);
|
||
break;
|
||
|
||
case COXParserObject::ELEMENT:
|
||
bResult = WriteElement(hFile, (COXParserElement*) pObject,
|
||
nLevel);
|
||
break;
|
||
|
||
case COXParserObject::MARKUP:
|
||
bResult = WriteMarkup(hFile, pObject, nLevel);
|
||
break;
|
||
|
||
case COXParserObject::PLAINTEXT:
|
||
case COXParserObject::RAWTEXT:
|
||
bResult = WriteText(hFile, pObject, nLevel);
|
||
break;
|
||
|
||
case COXParserObject::PROCINSTR:
|
||
bResult = WriteProcessingInstruction(hFile, pObject, nLevel);
|
||
break;
|
||
|
||
default: /*nothing*/;
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
LPTSTR COXParser::LoadFile(LPCTSTR szFile)
|
||
{
|
||
HANDLE hFile = CreateFile(szFile, GENERIC_READ,
|
||
0, NULL, OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
||
NULL);
|
||
|
||
if (INVALID_HANDLE_VALUE == hFile)
|
||
return NULL;
|
||
|
||
DWORD size = GetFileSize(hFile, NULL);
|
||
if (-1 == size)
|
||
{
|
||
CloseHandle(hFile);
|
||
return NULL;
|
||
}
|
||
|
||
LPSTR ptr = (LPSTR) ::GlobalAlloc(GMEM_FIXED, size + 1);
|
||
|
||
DWORD nCount;
|
||
if (!ReadFile(hFile, ptr, size, &nCount, NULL))
|
||
{
|
||
CloseHandle(hFile);
|
||
return NULL;
|
||
}
|
||
CloseHandle(hFile);
|
||
|
||
ptr[nCount] = '\0';
|
||
|
||
#ifdef _UNICODE
|
||
LPTSTR tptr = (LPTSTR) ::GlobalAlloc(GMEM_FIXED,
|
||
(nCount + 1) * sizeof(TCHAR));
|
||
UTBStr::mbstowcs(tptr, nCount+1, ptr, nCount);
|
||
::GlobalFree(ptr);
|
||
|
||
tptr[nCount] = m_chNULL;
|
||
|
||
return tptr;
|
||
#else
|
||
return ptr;
|
||
#endif
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
// Error handling routines
|
||
//////////////////////////////////////////////////////////////////////
|
||
|
||
BOOL COXParser::ReportError(UINT nErrorCode, LPCTSTR fmt, ...)
|
||
{
|
||
va_list args;
|
||
TCHAR buffer[512];
|
||
|
||
va_start(args, fmt);
|
||
|
||
#if _MSC_VER >= 1400
|
||
_vstprintf_s_l(buffer, 512, fmt, NULL, args);
|
||
#else
|
||
_vstprintf(buffer, fmt, args);
|
||
# endif
|
||
|
||
|
||
va_end(args);
|
||
|
||
if (m_pfnErrorFn)
|
||
return (*m_pfnErrorFn)(nErrorCode, buffer, m_nLine,
|
||
m_nColumn, m_dwErrData);
|
||
else
|
||
return DefErrorHandler(nErrorCode, buffer, m_nLine,
|
||
m_nColumn, m_dwErrData);
|
||
}
|
||
|
||
BOOL COXParser::DefErrorHandler(UINT nErrorCode, LPCTSTR szError,
|
||
int nLine, int nColumn,
|
||
DWORD dwData)
|
||
{
|
||
UNUSED_ALWAYS(dwData);
|
||
UNUSED(nErrorCode);
|
||
UNUSED(szError);
|
||
UNUSED(nLine);
|
||
UNUSED(nColumn);
|
||
|
||
#ifdef _DEBUG
|
||
#if _MSC_VER >= 1200
|
||
#pragma warning(push)
|
||
#endif
|
||
#pragma warning(disable:4127) // conditional expression is constant
|
||
if (ERROR_FIRST < nErrorCode && nErrorCode < ERROR_LAST)
|
||
{
|
||
_RPT4(_CRT_WARN, "COXParser: Error (%d) at line %d, col %d: %s\n",
|
||
nErrorCode, nLine, nColumn, szError);
|
||
}
|
||
else if (WARNING_FIRST < nErrorCode && nErrorCode < WARNING_LAST)
|
||
{
|
||
_RPT4(_CRT_WARN, "COXParser: Warning (%d) at line %d, col %d: %s\n",
|
||
nErrorCode, nLine, nColumn, szError);
|
||
}
|
||
else
|
||
_RPT4(_CRT_WARN, "COXParser: line %d, col %d: %s\n",
|
||
nErrorCode, nLine, nColumn, szError);
|
||
#if _MSC_VER >= 1200
|
||
#pragma warning(pop)
|
||
#else
|
||
#pragma warning(default:4127)
|
||
#endif
|
||
#endif
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
LPCTSTR COXParser::TranslateErrorCode(int nErrorCode)
|
||
{
|
||
switch (nErrorCode)
|
||
{
|
||
case ERROR_NULL_BUFFER:
|
||
return TEXT("NULL buffer passed in");
|
||
case ERROR_END_OF_BUFFER:
|
||
return TEXT("Unexpected end of buffer");
|
||
case ERROR_OUT_OF_MEMORY:
|
||
return TEXT("Out of memory");
|
||
case ERROR_BAD_TOKEN:
|
||
return TEXT("Unable to retrieve a token");
|
||
case ERROR_ILLEGAL_CHAR:
|
||
return TEXT("Illegal characters encountered in token");
|
||
case ERROR_UNEXPECTED_TOKEN:
|
||
return TEXT("Unexpected token type");
|
||
case ERROR_MISSING_END_TAG:
|
||
return TEXT("Missing end tag");
|
||
case ERROR_BAD_ENTITY:
|
||
return TEXT("Bad entity string");
|
||
case ERROR_BAD_INTEGER:
|
||
return TEXT("Bad integer value");
|
||
case ERROR_MISSING_END_BRACKET:
|
||
return TEXT("Missing end bracket in tag");
|
||
case WARNING_UNEXPECTED_END_TAG:
|
||
return TEXT("Unexpected end tag");
|
||
|
||
default:
|
||
return TEXT("Unknown Error");
|
||
}
|
||
}
|
||
|
||
|
||
void COXParser::InsertEntityValue(COXQuickString& str,
|
||
LPCTSTR lpszEntity)
|
||
{
|
||
LPCTSTR lpszSeek=lpszEntity;
|
||
TCHAR chSpace=TEXT(' ');
|
||
while(*(lpszSeek) && *(lpszSeek)!=m_chEndDelim && *(lpszSeek)!=chSpace)
|
||
{
|
||
str.Append(*lpszSeek);
|
||
lpszSeek++;
|
||
}
|
||
}
|