/******************************************************************************* | |
* Copyright 2009, Marvell Technology Group Ltd. | |
* | |
* THIS CODE CONTAINS CONFIDENTIAL INFORMATION OF MARVELL. NO RIGHTS ARE GRANTED | |
* HEREIN UNDER ANY PATENT, MASK WORK RIGHT OR COPYRIGHT OF MARVELL OR ANY THIRD | |
* PARTY. MARVELL RESERVES THE RIGHT AT ITS SOLE DISCRETION TO REQUEST THAT THIS | |
* CODE BE IMMEDIATELY RETURNED TO MARVELL. THIS CODE IS PROVIDED "AS IS". | |
* MARVELL MAKES NO WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS | |
* ACCURACY, COMPLETENESS OR PERFORMANCE. MARVELL COMPRISES MARVELL TECHNOLOGY | |
* GROUP LTD. (MTGL) AND ITS SUBSIDIARIES, MARVELL INTERNATIONAL LTD. (MIL), | |
* MARVELL TECHNOLOGY, INC. (MTI), MARVELL SEMICONDUCTOR, INC. (MSI), MARVELL | |
* ASIA PTE LTD. (MAPL), MARVELL JAPAN K.K. (MJKK), GALILEO TECHNOLOGY LTD. (GTL) | |
* GALILEO TECHNOLOGY, INC. (GTI) AND RADLAN Computer Communications, LTD. | |
******************************************************************************** | |
*/ | |
/* | |
**************************************************************************** | |
* Include all necessary include files | |
**************************************************************************** | |
*/ | |
#include "mrvxml.h" | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <httpclient_sys.h> | |
#define duster_malloc malloc | |
#define duster_free free | |
#define ASSERT assert | |
#define TRUE 1 | |
#define FALSE 0 | |
#ifndef XML_FILE_SIZE | |
#define XML_FILE_SIZE 5120 | |
#endif | |
//#define mrvxml_log(fmt,args...) RDPRINTF(format, ##args); | |
//#define mrvxml_log PRINTINFO | |
#define mrvxml_log(fmt,args...) | |
/* | |
**************************************************************************** | |
* Enumeration used to decipher what type a token is | |
**************************************************************************** | |
*/ | |
typedef enum MrvTokenType | |
{ | |
eTokenText = 0, | |
eTokenQuotedText, | |
eTokenTagStart, /* "<" */ | |
eTokenTagEnd, /* "</" */ | |
eTokenCloseTag, /* ">" */ | |
eTokenEquals, /* "=" */ | |
eTokenDeclaration, /* "<?" */ | |
eTokenShortHandClose, /* "/>" */ | |
eTokenClear, | |
eTokenError | |
} MrvTokenType; | |
/* | |
**************************************************************************** | |
* Defines to dictate grow by value when adding nodes. | |
**************************************************************************** | |
*/ | |
#define MrvGROWBY 5 | |
//#define MrvINDENTCHAR _T('\t') | |
#define MrvINDENTCHAR '\t' | |
typedef struct MrvClearTag | |
{ | |
MRV_LPTSTR lpszOpen; | |
MRV_LPTSTR lpszClose; | |
} MrvClearTag; | |
typedef struct MrvNextToken | |
{ | |
MrvClearTag *pClr; | |
MRV_LPCTSTR pStr; | |
} MrvNextToken; | |
/* | |
**************************************************************************** | |
* Main structure used for parsing XML | |
**************************************************************************** | |
*/ | |
typedef struct MrvXML | |
{ | |
MRV_LPCTSTR lpXML; | |
int nIndex; | |
enum MrvXMLError error; | |
MRV_LPCTSTR lpEndTag; | |
int cbEndTag; | |
MRV_LPCTSTR lpNewElement; | |
int cbNewElement; | |
int nFirst; | |
MrvClearTag *pClrTags; | |
} MrvXML; | |
/* | |
**************************************************************************** | |
* Enumeration used when parsing attributes | |
**************************************************************************** | |
*/ | |
typedef enum MrvAttrib | |
{ | |
eAttribName = 0, | |
eAttribEquals, | |
eAttribValue | |
} MrvAttrib; | |
/* | |
**************************************************************************** | |
* Enumeration used when parsing elements to dictate whether we are currently | |
* inside a tag | |
**************************************************************************** | |
*/ | |
typedef enum MrvStatus | |
{ | |
eInsideTag = 0, | |
eOutsideTag | |
} MrvStatus; | |
/* | |
**************************************************************************** | |
* Structure used to obtain List. | |
**************************************************************************** | |
*/ | |
typedef struct List_ST{ | |
struct List_ST *pNext; | |
struct List_ST *pPrev; | |
}List_st; | |
typedef struct{ | |
List_st pList; | |
int iNum; | |
}ListHead_st; | |
/* | |
**************************************************************************** | |
* Structure used to obtain List Node. | |
**************************************************************************** | |
*/ | |
#ifndef DYNAMIC_MEM | |
#define DYNAMIC_MEM | |
#endif | |
#ifdef DYNAMIC_MEM | |
typedef struct{ | |
List_st List; | |
MRV_LPTSTR lpszText; | |
}NodeText_st; | |
#else | |
typedef struct{ | |
List_st List; | |
char lpszText[256]; | |
}NodeText_st; | |
#endif | |
#if 0 | |
static ListHead_st g_StrList; | |
static void InitList(ListHead_st *pListHead) | |
{ | |
pListHead->pList.pNext = &(pListHead->pList); | |
pListHead->pList.pPrev = &(pListHead->pList); | |
pListHead->iNum = 0; | |
}/* InitList */ | |
static void AddToList(ListHead_st *pListHead,List_st *pListNode) | |
{ | |
pListNode->pPrev = &(pListHead->pList); | |
pListNode->pNext = pListHead->pList.pNext; | |
pListHead->pList.pNext->pPrev = pListNode; | |
pListHead->pList.pNext = pListNode; | |
pListHead->iNum++; | |
}/* AddToList */ | |
static List_st *DeleteFromListTial(ListHead_st *pListHead) | |
{ | |
List_st *pTemp; | |
//Empty List? | |
if(pListHead->pList.pPrev == &(pListHead->pList)) | |
return 0; | |
pTemp = pListHead->pList.pPrev; | |
pTemp->pPrev->pNext = pTemp->pNext; | |
pTemp->pNext->pPrev = pTemp->pPrev; | |
pListHead->iNum--; | |
return pTemp; | |
}/* DeleteFromListTial */ | |
#endif | |
static void DeleteFromList(List_st *pList) | |
{ | |
pList->pNext->pPrev = pList->pPrev; | |
pList->pPrev->pPrev = pList->pNext; | |
}/* DeleteFromList */ | |
#if 0 | |
static void FreeList(ListHead_st *pListHead) | |
{ | |
List_st *pList; | |
NodeText_st *pNodeText; | |
//empty List | |
if(pListHead->pList.pNext == &(pListHead->pList)) | |
return; | |
while(pListHead->iNum){ | |
pList = DeleteFromListTial(pListHead); | |
pNodeText = ListEntry(pList,NodeText_st,List); | |
#ifdef DYNAMIC_MEM | |
duster_free(pNodeText->lpszText); | |
#endif | |
duster_free(pNodeText); | |
} | |
}/*FreeList*/ | |
#endif | |
/** | |
**************************************************************************** | |
* <P> Initialise an element. </P> | |
* | |
* @methodName MrvInitElement | |
* | |
* @param *pEntry | |
* @param lpszName | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvInitElement(MrvXMLElement *pEntry, MrvXMLElement *pParent, | |
MRV_LPTSTR lpszName, int nIsDeclaration) | |
{ | |
ASSERT(pEntry); | |
pEntry->nMax = 0; | |
pEntry->nSize = 0; | |
pEntry->pEntries = NULL; | |
pEntry->pParent = pParent; | |
pEntry->nIsDeclaration = nIsDeclaration; | |
pEntry->lpszName = lpszName; | |
}/* MrvInitElement */ | |
/** | |
**************************************************************************** | |
* <P> Create the root element. </P> | |
* | |
* @methodName * MrvCreateRoot | |
* | |
* @param none | |
* | |
* @return MrvXMLElement | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvCreateRoot() | |
{ | |
MrvXMLElement * pElement; | |
pElement = (MrvXMLElement*)duster_malloc(sizeof(MrvXMLElement)); | |
/*modified by shoujunl*/ | |
MrvInitElement(pElement, NULL, NULL, 0); | |
//Init the NodeName LinkList | |
//Toby add 2006/7/3/9:31 | |
//InitList(&g_StrList); | |
return pElement; | |
}/* MrvCreateRoot */ | |
/** | |
**************************************************************************** | |
* <P> Delete the root element and set it to NULL. </P> | |
* | |
* @methodName MrvDeleteRoot | |
* | |
* @param ** ppElement | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
void MrvDeleteRoot(MrvXMLElement * pElement) | |
{ | |
mrvxml_log("enter %s",__FUNCTION__); | |
MrvDeleteElement(pElement); | |
mrvxml_log("%s middle",__FUNCTION__); | |
if(pElement) | |
duster_free(pElement); | |
//FreeList(&g_StrList); | |
mrvxml_log("leave %s",__FUNCTION__); | |
}/* MrvDeleteRoot */ | |
/** | |
**************************************************************************** | |
* <P> Delete an attribute. </P> | |
* | |
* @methodName MrvDeleteAttribute | |
* | |
* @param *pEntry | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvDeleteAttribute(MrvXMLAttribute *pEntry) | |
{ | |
ASSERT(pEntry); | |
if (pEntry->lpszName) | |
{ | |
duster_free(pEntry->lpszName); | |
pEntry->lpszName = NULL; | |
} | |
if (pEntry->lpszValue) | |
{ | |
duster_free(pEntry->lpszValue); | |
pEntry->lpszValue= NULL; | |
} | |
}/* MrvDeleteAttribute */ | |
/** | |
**************************************************************************** | |
* <P> Attach attributes from one list to another. </P> | |
* | |
* @methodName MrvAttributeAttach | |
* | |
* @param *pDst | |
* @param *pSrc | |
* @param nNum | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
#if 0 | |
static void MrvAttributeAttach(MrvXMLAttribute *pDst, MrvXMLAttribute *pSrc, int nNum) | |
{ | |
int n; | |
for (n=0; n<nNum; n++) | |
{ | |
pDst[n].lpszName = pSrc[n].lpszName; | |
pDst[n].lpszValue = pSrc[n].lpszValue; | |
pSrc[n].lpszName = NULL; | |
pSrc[n].lpszValue = NULL; | |
} | |
}/* MrvAttributeAttach */ | |
#endif | |
/** | |
**************************************************************************** | |
* <P> Obtain the next character from the string. </P> | |
* | |
* @methodName MrvGetNextChar | |
* | |
* @param *pXML | |
* | |
* @return MRV_TCHAR | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static MRV_TCHAR MrvGetNextChar(MrvXML *pXML) | |
{ | |
MRV_TCHAR ch; | |
ch = pXML->lpXML[pXML->nIndex]; | |
if (ch != 0) pXML->nIndex++; | |
return ch; | |
}/* MrvGetNextChar */ | |
/** | |
**************************************************************************** | |
* <P> Find next non-white space character. </P> | |
* | |
* @methodName MrvFindNonWhiteSpace | |
* | |
* @param *pXML | |
* | |
* @return MRV_TCHAR | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static MRV_TCHAR MrvFindNonWhiteSpace(MrvXML *pXML) | |
{ | |
MRV_TCHAR ch; | |
//MRV_LPCTSTR lpXML = pXML->lpXML; | |
int nExit = FALSE; | |
ASSERT(pXML); | |
/* | |
************************************************************************* | |
* Iterate through characters in the string until we find a NULL or a | |
* non-white space character | |
************************************************************************* | |
*/ | |
while((nExit == FALSE) && (ch = MrvGetNextChar(pXML))) | |
{ | |
switch(ch) | |
{ | |
/* | |
********************************************************************* | |
* Ignore white space | |
********************************************************************* | |
*/ | |
case '\n': | |
case ' ': | |
case '\t': | |
case '\r': | |
continue; | |
default: | |
nExit = TRUE; | |
} | |
} | |
return ch; | |
}/* MrvFindNonWhiteSpace */ | |
/** | |
**************************************************************************** | |
* <P> Duplicate a given string. </P> | |
* | |
* @methodName MrvStrdup | |
* | |
* @param lpszData | |
* @param cbData | |
* | |
* @return MRV_LPTSTR | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MRV_LPTSTR MrvStrdup(MRV_LPCTSTR lpszData, int cbData) | |
{ | |
MRV_LPTSTR lpszNew; | |
ASSERT(lpszData); | |
if (cbData == 0) cbData = strlen(lpszData); | |
lpszNew = duster_malloc((cbData+1) * sizeof(MRV_TCHAR)); | |
if (lpszNew) | |
{ | |
memcpy(lpszNew, lpszData, (cbData) * sizeof(MRV_TCHAR)); | |
//lpszNew[cbData] = (MRV_TCHAR)NULL; | |
lpszNew[cbData] = '\0'; | |
} | |
return lpszNew; | |
}/* MrvStrdup */ | |
/** | |
**************************************************************************** | |
* <P> Find the next token in a string. </P> | |
* | |
* @methodName MrvGetNextToken | |
* | |
* @param *pXML | |
* @param *pcbToken | |
* @param *pType | |
* | |
* @return MRV_LPCTSTR | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static int MrvGetNextToken(MrvXML *pXML, int *pcbToken, MrvTokenType *pType , MrvNextToken *result) | |
{ | |
//MrvNextToken result; | |
MRV_LPCTSTR lpXML; | |
MRV_TCHAR ch; | |
MRV_TCHAR chTemp = 0; | |
int nSize; | |
int nFoundMatch; | |
int nExit; | |
int n; | |
MRV_LPCTSTR lpszOpen; | |
int cbOpen; | |
int nIsText = FALSE; | |
/* | |
************************************************************************* | |
* Find next non-whte space character | |
************************************************************************* | |
*/ | |
ch = MrvFindNonWhiteSpace(pXML); | |
if (ch) | |
{ | |
/* | |
********************************************************************* | |
* Cache the current string pointer | |
********************************************************************* | |
*/ | |
lpXML = pXML->lpXML; | |
result->pStr = &lpXML[pXML->nIndex-1]; | |
/* | |
********************************************************************* | |
* First check whether the token is in the clear tag list (meaning it | |
* does not need formatting). | |
********************************************************************* | |
*/ | |
n = 0; | |
while(TRUE) | |
{ | |
/* | |
***************************************************************** | |
* Obtain the name of the open part of the clear tag | |
***************************************************************** | |
*/ | |
lpszOpen = pXML->pClrTags[n].lpszOpen; | |
if (lpszOpen) | |
{ | |
/* | |
************************************************************* | |
* Compare the open tag with the current token | |
************************************************************* | |
*/ | |
cbOpen = strlen(lpszOpen); | |
if (strncmp(lpszOpen, result->pStr, cbOpen) == 0) | |
{ | |
result->pClr = &pXML->pClrTags[n]; | |
pXML->nIndex += cbOpen-1; | |
*pType = eTokenClear; | |
//return result; | |
return 0; | |
} | |
n++; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
/* | |
********************************************************************* | |
* If we didn't find a clear tag then check for standard tokens | |
********************************************************************* | |
*/ | |
chTemp = 0; | |
lpXML = pXML->lpXML; | |
switch(ch) | |
{ | |
/* | |
********************************************************************* | |
* Check for quotes | |
********************************************************************* | |
*/ | |
case '\'': | |
case '\"': | |
/* | |
***************************************************************** | |
* Type of token | |
***************************************************************** | |
*/ | |
*pType = eTokenQuotedText; | |
chTemp = ch; | |
/* | |
***************************************************************** | |
* Set the size | |
***************************************************************** | |
*/ | |
nSize = 1; | |
nFoundMatch = FALSE; | |
/* | |
***************************************************************** | |
* Search through the string to find a matching quote | |
***************************************************************** | |
*/ | |
while((ch = MrvGetNextChar(pXML))) | |
{ | |
nSize++; | |
if (ch == chTemp) | |
{ | |
nFoundMatch = TRUE; | |
break; | |
} | |
} | |
/* | |
***************************************************************** | |
* If we failed to find a matching quote | |
***************************************************************** | |
*/ | |
if (nFoundMatch == FALSE) | |
{ | |
/* | |
************************************************************* | |
* Indicate error | |
************************************************************* | |
*/ | |
pXML->error = eXMLErrorNoMatchingQuote; | |
*pType = eTokenError; | |
} | |
/* MCB 4.02.2002 */ | |
if (MrvFindNonWhiteSpace(pXML)) | |
{ | |
pXML->nIndex--; | |
} | |
break; | |
/* | |
********************************************************************* | |
* Equals (used with attribute values) | |
********************************************************************* | |
*/ | |
case '=': | |
nSize = 1; | |
*pType = eTokenEquals; | |
break; | |
/* | |
********************************************************************* | |
* Close tag | |
********************************************************************* | |
*/ | |
case '>': | |
nSize = 1; | |
*pType = eTokenCloseTag; | |
break; | |
/* | |
********************************************************************* | |
* Check for tag start and tag end | |
********************************************************************* | |
*/ | |
case '<': | |
/* | |
***************************************************************** | |
* Peek at the next character to see if we have an end tag '</', | |
* or an xml declaration '<?' | |
***************************************************************** | |
*/ | |
chTemp = pXML->lpXML[pXML->nIndex]; | |
/* | |
***************************************************************** | |
* If we have a tag end... | |
***************************************************************** | |
*/ | |
if (chTemp == '/') | |
{ | |
/* | |
************************************************************* | |
* Set the type and ensure we point at the next character | |
************************************************************* | |
*/ | |
MrvGetNextChar(pXML); | |
*pType = eTokenTagEnd; | |
nSize = 2; | |
} | |
/* | |
***************************************************************** | |
* If we have an XML declaration tag | |
***************************************************************** | |
*/ | |
else if (chTemp == '?') | |
{ | |
/* | |
************************************************************* | |
* Set the type and ensure we point at the next character | |
************************************************************* | |
*/ | |
MrvGetNextChar(pXML); | |
*pType = eTokenDeclaration; | |
nSize = 2; | |
} | |
/* | |
***************************************************************** | |
* Otherwise we must have a start tag | |
***************************************************************** | |
*/ | |
else | |
{ | |
*pType = eTokenTagStart; | |
nSize = 1; | |
} | |
break; | |
/* | |
********************************************************************* | |
* Check to see if we have a short hand type end tag ('/>'). | |
********************************************************************* | |
*/ | |
case '/': | |
/* | |
***************************************************************** | |
* Peek at the next character to see if we have an end tag '</' | |
* or an xml declaration '<?' | |
***************************************************************** | |
*/ | |
chTemp = pXML->lpXML[pXML->nIndex]; | |
/* | |
***************************************************************** | |
* If we have a short hand end tag... | |
***************************************************************** | |
*/ | |
if (chTemp == '>') | |
{ | |
/* | |
************************************************************* | |
* Set the type and ensure we point at the next character | |
************************************************************* | |
*/ | |
MrvGetNextChar(pXML); | |
*pType = eTokenShortHandClose; | |
nSize = 2; | |
break; | |
} | |
/* | |
***************************************************************** | |
* If we haven't found a short hand closing tag then drop into the | |
* text process | |
***************************************************************** | |
*/ | |
/* | |
********************************************************************* | |
* Other characters | |
********************************************************************* | |
*/ | |
default: | |
nIsText = TRUE; | |
} | |
/* | |
********************************************************************* | |
* If this is a TEXT node | |
********************************************************************* | |
*/ | |
if (nIsText) | |
{ | |
/* | |
***************************************************************** | |
* Indicate we are dealing with text | |
***************************************************************** | |
*/ | |
*pType = eTokenText; | |
nSize = 1; | |
nExit = FALSE; | |
while((nExit == FALSE) && (ch = MrvGetNextChar(pXML))) | |
{ | |
switch(ch) | |
{ | |
/* | |
************************************************************* | |
* Break when we find white space | |
************************************************************* | |
*/ | |
case '\n': | |
case ' ': | |
case '\t': | |
case '\r': | |
nExit = TRUE; | |
break; | |
/* | |
************************************************************* | |
* If we find a slash then this maybe text or a short hand end | |
* tag. | |
************************************************************* | |
*/ | |
case '/': | |
/* | |
********************************************************* | |
* Peek at the next character to see it we have short hand | |
* end tag | |
********************************************************* | |
*/ | |
chTemp = pXML->lpXML[pXML->nIndex]; | |
/* | |
********************************************************* | |
* If we found a short hand end tag then we need to exit | |
* the loop | |
********************************************************* | |
*/ | |
if (chTemp == '>') | |
{ | |
pXML->nIndex--; /* MCB 03.02.2002 */ | |
nExit = TRUE; | |
} | |
else | |
{ | |
nSize++; | |
} | |
break; | |
/* | |
************************************************************* | |
* Break when we find a terminator and decrement the index and | |
* column count so that we are pointing at the right character | |
* the next time we are called. | |
************************************************************* | |
*/ | |
case '<': | |
case '>': | |
case '=': | |
pXML->nIndex--; | |
nExit = TRUE; | |
break; | |
case 0: | |
nExit = TRUE; | |
break; | |
default: | |
nSize++; | |
} | |
} | |
} | |
*pcbToken = nSize; | |
} | |
/* | |
************************************************************************* | |
* If we failed to obtain a valid character | |
************************************************************************* | |
*/ | |
else | |
{ | |
*pcbToken = 0; | |
*pType = eTokenError; | |
} | |
mrvxml_log("%s chTemp =0x%x *pType = %d",__FUNCTION__,chTemp,*pType); | |
return 0; | |
}/* MrvGetNextToken */ | |
/** | |
**************************************************************************** | |
* <P> Parse XML errors into a user friendly string. </P> | |
* | |
* @methodName MrvGetError | |
* | |
* @param error | |
* | |
* @return MRV_LPCTSTR | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MRV_LPCTSTR MrvGetError(MrvXMLError error) | |
{ | |
MRV_LPCTSTR lpszErr = "Unknown"; | |
int n; | |
/* | |
************************************************************************* | |
* Structure for errors array | |
************************************************************************* | |
*/ | |
typedef struct McbErrorList | |
{ | |
enum MrvXMLError err; | |
MRV_LPCTSTR lpszErr; | |
} McbErrorList; | |
/* | |
************************************************************************* | |
* Static array containing helpful error text. | |
************************************************************************* | |
*/ | |
static struct McbErrorList errs[] = | |
{ | |
{ eXMLErrorNone, "No error" }, | |
{ eXMLErrorEmpty, "No XML data" }, | |
{ eXMLErrorFirstNotStartTag, "First token not start tag" }, | |
{ eXMLErrorMissingTagName, "Missing start tag name" }, | |
{ eXMLErrorMissingEndTagName, "Missing end tag name" }, | |
{ eXMLErrorNoMatchingQuote, "Unmatched quote" }, | |
{ eXMLErrorUnmatchedEndTag, "Unmatched end tag" }, | |
{ eXMLErrorUnexpectedToken, "Unexpected token found" }, | |
{ eXMLErrorInvalidTag, "Invalid tag found" }, | |
{ eXMLErrorNoElements, "No elements found" }, | |
{ 0, NULL } | |
}; | |
/* | |
************************************************************************* | |
* Iterate through the list of errors to find a matching error | |
************************************************************************* | |
*/ | |
for(n = 0; errs[n].lpszErr; n++) | |
{ | |
if (errs[n].err == error) | |
{ | |
lpszErr = errs[n].lpszErr; | |
break; | |
} | |
} | |
return lpszErr; | |
}/* MrvGetError */ | |
/** | |
**************************************************************************** | |
* <P> Delete memory associated with a text node. </P> | |
* | |
* @methodName MrvDeleteText | |
* | |
* @param *pText | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvDeleteText(MrvXMLText *pText) | |
{ | |
ASSERT(pText); | |
if (pText->lpszValue) | |
{ | |
duster_free(pText->lpszValue); | |
pText->lpszValue = NULL; | |
} | |
}/* MrvDeleteText */ | |
/** | |
**************************************************************************** | |
* <P> Delete memory associated with a clear (unformatted) node. </P> | |
* | |
* @methodName MrvDeleteClear | |
* | |
* @param *pClear | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvDeleteClear(MrvXMLClear *pClear) | |
{ | |
ASSERT(pClear); | |
if (pClear->lpszValue) | |
{ | |
duster_free(pClear->lpszValue); | |
pClear->lpszValue = NULL; | |
} | |
}/* MrvDeleteClear */ | |
/** | |
**************************************************************************** | |
* <P> Delete a given node. </P> | |
* | |
* @methodName MrvDeleteNode | |
* | |
* @param *pEntry | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
void MrvDeleteNode(MrvXMLNode *pEntry) | |
{ | |
mrvxml_log("enter %s",__FUNCTION__); | |
if (pEntry) | |
{ | |
if (pEntry->type == eNodeEmpty) | |
{ | |
return; | |
} | |
/* | |
********************************************************************* | |
* Delete the appropriate node | |
********************************************************************* | |
*/ | |
mrvxml_log("%s ,pEntry->type is %d",__FUNCTION__,pEntry->type); | |
switch(pEntry->type) | |
{ | |
case eNodeAttribute: | |
MrvDeleteAttribute(pEntry->node.pAttrib); | |
/*modified by shoujunl*/ | |
duster_free(pEntry->node.pAttrib); | |
pEntry->node.pAttrib = NULL; | |
break; | |
case eNodeElement: | |
MrvDeleteElement(pEntry->node.pElement); | |
duster_free(pEntry->node.pElement); | |
pEntry->node.pElement = NULL; | |
break; | |
case eNodeText: | |
MrvDeleteText(pEntry->node.pText); | |
/*modified by shoujunl*/ | |
duster_free(pEntry->node.pText); | |
pEntry->node.pText = NULL; | |
break; | |
case eNodeClear: | |
MrvDeleteClear(pEntry->node.pClear); | |
/*modified by shoujunl*/ | |
duster_free(pEntry->node.pClear); | |
pEntry->node.pClear = NULL; | |
break; | |
default: | |
ASSERT(TRUE); | |
} | |
/*modified by shoujunl*/ | |
// duster_free(pEntry->node.pAttrib); | |
pEntry->type = eNodeEmpty; | |
} | |
mrvxml_log("leave %s",__FUNCTION__); | |
}/* MrvDeleteNode */ | |
/** | |
**************************************************************************** | |
* <P> Delete an element and all it's contained nodes. </P> | |
* | |
* @methodName MrvDeleteElement | |
* | |
* @param *pEntry | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
void MrvDeleteElement(MrvXMLElement *pEntry) | |
{ | |
mrvxml_log("enter %s,pEntry->lpszName is %s",__FUNCTION__,pEntry->lpszName); | |
int n; | |
ASSERT(pEntry); | |
/* | |
************************************************************************* | |
* Delete each node (this may recurse) | |
************************************************************************* | |
*/ | |
for(n = 0; n<pEntry->nSize; n++) | |
{ | |
MrvDeleteNode(&pEntry->pEntries[n]); | |
} | |
/* | |
************************************************************************* | |
* Cleanup the list of nodes | |
************************************************************************* | |
*/ | |
pEntry->nMax = 0; | |
pEntry->nSize = 0; | |
/*modified by shoujunl*/ | |
if(pEntry->pEntries) | |
duster_free(pEntry->pEntries); | |
pEntry->pEntries = NULL; | |
/* | |
************************************************************************* | |
* Delete the name of the element | |
************************************************************************* | |
*/ | |
if (pEntry->lpszName) | |
{ | |
duster_free(pEntry->lpszName); | |
pEntry->lpszName = NULL; | |
} | |
/* duster_free(pEntry); */ | |
mrvxml_log("leave %s",__FUNCTION__); | |
}/* MrvDeleteElement */ | |
/** | |
**************************************************************************** | |
* <P> Attach nodes from one list to another. </P> | |
* | |
* @methodName MrvAttachNodes | |
* | |
* @param *pDst | |
* @param *pSrc | |
* @param nNum | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvAttachNodes(MrvXMLNode *pDst, MrvXMLNode *pSrc, int nNum) | |
{ | |
int n; | |
for (n=0; n<nNum; n++) | |
{ | |
pDst[n] = pSrc[n]; | |
pSrc[n].type = eNodeEmpty; | |
} | |
}/* MrvAttachNodes */ | |
/** | |
**************************************************************************** | |
* <P> Reserve memory for additional nodes. </P> | |
* | |
* @methodName MrvAllocNodes | |
* | |
* @param *pEntry | |
* @param nGrowBy | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvAllocNodes(MrvXMLElement *pEntry, int nGrowBy) | |
{ | |
int nMax, nIndex = 0; | |
MrvXMLNode * pNew; | |
ASSERT(pEntry); | |
ASSERT(nGrowBy > 0); | |
/* | |
************************************************************************* | |
* Allocate storage for new nodes | |
************************************************************************* | |
*/ | |
nMax = pEntry->nMax += nGrowBy; | |
pNew = duster_malloc(sizeof(MrvXMLNode) * nMax); | |
/* | |
************************************************************************* | |
* Attach old entries to the new storage | |
************************************************************************* | |
*/ | |
MrvAttachNodes(pNew, pEntry->pEntries, pEntry->nSize); | |
/*modified by shoujunl*/ | |
for(nIndex = 0;nIndex < pEntry->nSize;nIndex++) | |
{ | |
MrvDeleteNode(&pEntry->pEntries[nIndex]); | |
} | |
if (pEntry->pEntries) | |
{ | |
duster_free(pEntry->pEntries); | |
} | |
pEntry->pEntries = pNew; | |
}/* MrvAllocNodes */ | |
/** | |
**************************************************************************** | |
* <P> Add an element node to the element. </P> | |
* | |
* @methodName MrvAddElement | |
* | |
* @param *pEntry | |
* @param lpszName | |
* @param nIsDeclaration | |
* @param nGrowBy | |
* | |
* @return MrvXMLElement * | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvAddElement(MrvXMLElement *pEntry, MRV_LPTSTR lpszName, | |
int nIsDeclaration, int nGrowBy) | |
{ | |
//mrvxml_log("enter %s",__FUNCTION__); | |
MrvXMLNode *pNode; | |
MrvXMLElement * pElement; | |
ASSERT(pEntry); | |
ASSERT(nGrowBy > 0); | |
/* | |
************************************************************************* | |
* Check we have enough storage | |
************************************************************************* | |
*/ | |
if (pEntry->nSize == pEntry->nMax) | |
{ | |
MrvAllocNodes(pEntry, nGrowBy); | |
} | |
/* | |
************************************************************************* | |
* Obtain the new node, set the type and initialise it. | |
************************************************************************* | |
*/ | |
pNode = &pEntry->pEntries[pEntry->nSize]; | |
pNode->type = eNodeElement; | |
pElement = duster_malloc(sizeof(MrvXMLElement)); | |
pNode->node.pElement = pElement; | |
MrvInitElement(pElement, pEntry, lpszName, nIsDeclaration); | |
/* | |
************************************************************************* | |
* Increment the number of contained nodes | |
************************************************************************* | |
*/ | |
pEntry->nSize++; | |
/* | |
************************************************************************* | |
* Return the new element. | |
************************************************************************* | |
*/ | |
// mrvxml_log("leave %s",__FUNCTION__); | |
return pElement; | |
}/* MrvAddElement */ | |
/** | |
**************************************************************************** | |
* <P> Add an attribute to an element. </P> | |
* | |
* @methodName MrvAddAttribute | |
* | |
* @param *pEntry | |
* @param lpszName | |
* @param lpszValue | |
* @param nGrowBy | |
* | |
* @return MrvXMLAttribute * | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLAttribute * MrvAddAttribute(MrvXMLElement *pEntry, MRV_LPTSTR lpszName, | |
MRV_LPTSTR lpszValue, int nGrowBy) | |
{ | |
MrvXMLNode *pNode; | |
MrvXMLAttribute *pAttr; | |
ASSERT(pEntry); | |
ASSERT(nGrowBy > 0); | |
/* | |
************************************************************************* | |
* Check we have enough storage | |
************************************************************************* | |
*/ | |
if (pEntry->nSize == pEntry->nMax) | |
{ | |
MrvAllocNodes(pEntry, nGrowBy); | |
} | |
/* | |
************************************************************************* | |
* Obtain the new node, set the type and initialise it. | |
************************************************************************* | |
*/ | |
pNode = &pEntry->pEntries[pEntry->nSize]; | |
pNode->type = eNodeAttribute; | |
pAttr = duster_malloc(sizeof(MrvXMLAttribute)); | |
pNode->node.pAttrib = pAttr; | |
//pAttr->lpszName = lpszName; | |
//pAttr->lpszValue = lpszValue; | |
//toby add 2006/7/4 10:03 | |
pAttr->lpszName = lpszName;//MrvStrdup(lpszName,0); | |
pAttr->lpszValue = lpszValue;//MrvStrdup(lpszValue,0); | |
/* | |
************************************************************************* | |
* Increment the number of contained nodes | |
************************************************************************* | |
*/ | |
pEntry->nSize++; | |
/* | |
************************************************************************* | |
* Return the new attribute. | |
************************************************************************* | |
*/ | |
return pAttr; | |
}/* MrvAddAttribute */ | |
/** | |
**************************************************************************** | |
* <P> Add text to the element. </P> | |
* | |
* @methodName * MrvAddText | |
* | |
* @param *pEntry | |
* @param lpszValue | |
* @param nGrowBy | |
* | |
* @return MrvAddText | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLText * MrvAddText(MrvXMLElement *pEntry, MRV_LPTSTR lpszValue, int nGrowBy) | |
{ | |
MrvXMLNode *pNode; | |
MrvXMLText *pText; | |
ASSERT(pEntry); | |
ASSERT(nGrowBy > 0); | |
/* | |
************************************************************************* | |
* Check we have enough storage | |
************************************************************************* | |
*/ | |
if (pEntry->nSize == pEntry->nMax) | |
{ | |
MrvAllocNodes(pEntry, nGrowBy); | |
} | |
/* | |
************************************************************************* | |
* Obtain the new node, set the type and initialise it. | |
************************************************************************* | |
*/ | |
pNode = &pEntry->pEntries[pEntry->nSize]; | |
pNode->type = eNodeText; | |
pText = duster_malloc(sizeof(MrvXMLText)); | |
pNode->node.pText = pText; | |
pText->lpszValue = lpszValue; | |
/* | |
************************************************************************* | |
* Increment the number of contained nodes | |
************************************************************************* | |
*/ | |
pEntry->nSize++; | |
/* | |
************************************************************************* | |
* Return the new attribute. | |
************************************************************************* | |
*/ | |
return pText; | |
}/* MrvAddText */ | |
/** | |
**************************************************************************** | |
* <P> Add clear (unformatted) to the element. </P> | |
* | |
* @methodName * MrvAddClear | |
* | |
* @param *pEntry | |
* @param lpszValue | |
* @param nGrowBy | |
* | |
* @return MrvXMLClear | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static MrvXMLClear * MrvAddClear(MrvXMLElement *pEntry, MRV_LPTSTR lpszValue, | |
MrvClearTag *pClear, int nGrowBy) | |
{ | |
MrvXMLNode *pNode; | |
MrvXMLClear *pNewClear; | |
ASSERT(pEntry); | |
ASSERT(nGrowBy > 0); | |
/* | |
************************************************************************* | |
* Check we have enough storage | |
************************************************************************* | |
*/ | |
if (pEntry->nSize == pEntry->nMax) | |
{ | |
MrvAllocNodes(pEntry, nGrowBy); | |
} | |
/* | |
************************************************************************* | |
* Obtain the new node, set the type and initialise it. | |
************************************************************************* | |
*/ | |
pNode = &pEntry->pEntries[pEntry->nSize]; | |
pNode->type = eNodeClear; | |
pNewClear = duster_malloc(sizeof(MrvXMLClear)); | |
pNode->node.pClear = pNewClear; | |
pNewClear->lpszValue = lpszValue; | |
pNewClear->lpszOpenTag = pClear->lpszOpen; | |
pNewClear->lpszCloseTag = pClear->lpszClose; | |
/* | |
************************************************************************* | |
* Increment the number of contained nodes | |
************************************************************************* | |
*/ | |
pEntry->nSize++; | |
/* | |
************************************************************************* | |
* Return the new attribute. | |
************************************************************************* | |
*/ | |
return pNewClear; | |
}/* MrvAddClear */ | |
/** | |
**************************************************************************** | |
* <P> Enumerate nodes in the list. </P> | |
* | |
* @methodName MrvEnumNodes | |
* | |
* @param *pEntry | |
* @param *pnIndex | |
* | |
* @return MrvEnumNodes | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLNode * MrvEnumNodes(MrvXMLElement *pEntry, int *pnIndex) | |
{ | |
MrvXMLNode * pResult = NULL; | |
ASSERT(pnIndex); | |
ASSERT(pEntry); | |
if (*pnIndex < pEntry->nSize) | |
{ | |
pResult = &pEntry->pEntries[*pnIndex]; | |
(*pnIndex)++; | |
} | |
return pResult; | |
}/* MrvXMLNode */ | |
/** | |
**************************************************************************** | |
* <P> Enumerate elements on a base element. </P> | |
* | |
* @methodName MrvEnumElements | |
* | |
* @param *pEntry | |
* @param *pnIndex | |
* | |
* @return MrvXMLNode | |
* | |
* @exception none | |
* | |
* @author Martyn C Brown | |
* | |
* @changeHistory | |
* 20th August 2001 - (V1.0) Creation (MCB) | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvEnumElements(MrvXMLElement *pEntry, int *pnIndex) | |
{ | |
MrvXMLElement * pResult = NULL; | |
int nIndex; | |
ASSERT(pnIndex); | |
ASSERT(pEntry); | |
nIndex = *pnIndex; | |
for (;nIndex < pEntry->nSize && !pResult; nIndex++) | |
{ | |
if (pEntry->pEntries[nIndex].type == eNodeElement) | |
{ | |
pResult = pEntry->pEntries[nIndex].node.pElement; | |
//mrvxml_log("%s Element type pResult.lpszName is %s,nsize is %d",__FUNCTION__,pResult->lpszName,pResult->nSize); | |
} | |
} | |
*pnIndex = nIndex; | |
return pResult; | |
}/* MrvEnumElements */ | |
/** | |
**************************************************************************** | |
* <P> Enumerate attributes on a base element. </P> | |
* | |
* @methodName MrvEnumAttributes | |
* | |
* @param *pEntry | |
* @param *pnIndex | |
* | |
* @return MrvXMLNode | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLAttribute * MrvEnumAttributes(MrvXMLElement *pEntry, int *pnIndex) | |
{ | |
MrvXMLAttribute * pResult = NULL; | |
int nIndex; | |
ASSERT(pnIndex); | |
ASSERT(pEntry); | |
nIndex = *pnIndex; | |
for (;nIndex < pEntry->nSize && !pResult; nIndex++) | |
{ | |
if (pEntry->pEntries[nIndex].type == eNodeAttribute) | |
{ | |
pResult = pEntry->pEntries[nIndex].node.pAttrib; | |
} | |
} | |
*pnIndex = nIndex; | |
return pResult; | |
}/* MrvEnumAttributes */ | |
/** | |
**************************************************************************** | |
* <P> Trim the end of the text to remove white space characters. </P> | |
* | |
* @methodName MrvFindEndOfText | |
* | |
* @param lpszToken | |
* @param *pcbText | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvFindEndOfText(MRV_LPCTSTR lpszToken, int *pcbText) | |
{ | |
MRV_TCHAR ch; | |
int cbText; | |
ASSERT(lpszToken); | |
ASSERT(pcbText); | |
cbText = (*pcbText)-1; | |
while(TRUE) | |
{ | |
ASSERT(cbText >= 0); | |
ch = lpszToken[cbText]; | |
switch(ch) | |
{ | |
case '\r': | |
case '\n': | |
case '\t': | |
case ' ': | |
cbText--; | |
break; | |
default: | |
*pcbText = cbText+1; | |
return; | |
} | |
} | |
}/* MrvFindEndOfText */ | |
/** | |
**************************************************************************** | |
* <P> Parse a clear (unformatted) type node. </P> | |
* | |
* @methodName MrvParseClearTag | |
* | |
* @param *pXML | |
* @param *pElement | |
* @param lpszClose | |
* @param lpszToken | |
* | |
* @return int | |
* | |
* @exception none | |
**************************************************************************** | |
*/ | |
static int MrvParseClearTag(MrvXML *pXML, MrvXMLElement *pElement, MrvClearTag * pClear) | |
{ | |
int cbTemp = 0; | |
MRV_LPTSTR lpszTemp; | |
MRV_LPCTSTR lpszXML = &pXML->lpXML[pXML->nIndex]; | |
/* | |
************************************************************************* | |
* Find the closing tag | |
************************************************************************* | |
*/ | |
//lpszTemp = _tcsstr(lpszXML, pClear->lpszClose); | |
lpszTemp = strstr(lpszXML, pClear->lpszClose); | |
/* | |
************************************************************************* | |
* Iterate through the tokens until we find the closing tag. | |
************************************************************************* | |
*/ | |
if (lpszTemp) | |
{ | |
/* | |
********************************************************************* | |
* Cache the size and increment the index | |
********************************************************************* | |
*/ | |
cbTemp = lpszTemp - lpszXML; | |
pXML->nIndex += cbTemp; | |
pXML->nIndex += strlen(pClear->lpszClose); | |
/* | |
********************************************************************* | |
* Add the clear node to the current element | |
********************************************************************* | |
*/ | |
lpszTemp = MrvStrdup(lpszXML, cbTemp); | |
MrvAddClear(pElement, lpszTemp, pClear, MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset = (lpszXML - | |
strlen(pClear->lpszOpen)) - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
return TRUE; | |
} | |
/* | |
************************************************************************* | |
* If we failed to find the end tag | |
************************************************************************* | |
*/ | |
else | |
{ | |
mrvxml_log("%s pClear is 0x%x,%c",__FUNCTION__,pClear,pClear); | |
pXML->error = eXMLErrorUnmatchedEndTag; | |
return FALSE; | |
} | |
}/* MrvParseClearTag */ | |
/** | |
**************************************************************************** | |
* <P> Recursively parse an XML element. </P> | |
* | |
* @methodName MrvParseXMLElement | |
* | |
* @param *pXML | |
* @param * pElement | |
* | |
* @return int | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static int MrvParseXMLElement(MrvXML *pXML, MrvXMLElement * pElement) | |
{ | |
//mrvxml_log("enter %s"); | |
MRV_LPCTSTR lpszToken; | |
int cbToken; | |
enum MrvTokenType type; | |
MrvNextToken token; | |
MRV_LPCTSTR lpszTemp = NULL; | |
int cbTemp; | |
int nDeclaration; | |
MRV_LPCTSTR lpszText = NULL; | |
MrvXMLElement *pNew; | |
enum MrvStatus status; | |
enum MrvAttrib attrib = eAttribName; | |
ASSERT(pXML); | |
ASSERT(pElement); | |
if(pElement->lpszName){ | |
mrvxml_log("%s,pElement->lpszname is %s",__FUNCTION__,pElement->lpszName); | |
} | |
/* | |
************************************************************************* | |
* If this is the first call to the function | |
************************************************************************* | |
*/ | |
if (pXML->nFirst) | |
{ | |
/* | |
********************************************************************* | |
* Assume we are outside of a tag definition | |
********************************************************************* | |
*/ | |
pXML->nFirst = FALSE; | |
status = eOutsideTag; | |
} | |
/* | |
************************************************************************* | |
* If this is not the first call then we should only be called when inside | |
* a tag. | |
************************************************************************* | |
*/ | |
else | |
{ | |
status = eInsideTag; | |
} | |
/* | |
************************************************************************* | |
* Iterate through the tokens in the document | |
************************************************************************* | |
*/ | |
while(TRUE) | |
{ | |
/* | |
********************************************************************* | |
* Obtain the next token | |
********************************************************************* | |
*/ | |
MrvGetNextToken(pXML, &cbToken, &type , &token); | |
mrvxml_log("%s,type = %d,status = %d",__FUNCTION__,type,status); | |
if (type != eTokenError) | |
{ | |
/* | |
***************************************************************** | |
* Check the current status | |
***************************************************************** | |
*/ | |
switch(status) | |
{ | |
/* | |
***************************************************************** | |
* If we outside of a tag definition | |
***************************************************************** | |
*/ | |
case eOutsideTag: | |
/* | |
************************************************************* | |
* Check what type of token we obtained | |
************************************************************* | |
*/ | |
switch(type) | |
{ | |
/* | |
************************************************************* | |
* If we have found text or quoted text | |
************************************************************* | |
*/ | |
case eTokenText: | |
case eTokenQuotedText: | |
case eTokenEquals: | |
if (!lpszText) | |
{ | |
lpszText = token.pStr; | |
} | |
break; | |
/* | |
************************************************************* | |
* If we found a start tag '<' and declarations '<?' | |
************************************************************* | |
*/ | |
case eTokenTagStart: | |
case eTokenDeclaration: | |
/* | |
********************************************************* | |
* Cache whether this new element is a declaration or not | |
********************************************************* | |
*/ | |
nDeclaration = type == eTokenDeclaration; | |
/* | |
********************************************************* | |
* If we have node text then add this to the element | |
********************************************************* | |
*/ | |
if (lpszText) | |
{ | |
cbTemp = token.pStr - lpszText; | |
MrvFindEndOfText(lpszText, &cbTemp); | |
MrvAddText(pElement, MrvStrdup(lpszText, cbTemp), | |
MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset | |
= lpszText - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
lpszText = NULL; | |
} | |
/* | |
********************************************************* | |
* Find the name of the tag | |
********************************************************* | |
*/ | |
//token = MrvGetNextToken(pXML, &cbToken, &type); | |
MrvGetNextToken(pXML, &cbToken, &type , &token); | |
mrvxml_log("%s, find name of tag type = %d,status = %d",__FUNCTION__,type,status); | |
/* | |
********************************************************* | |
* Return an error if we couldn't obtain the next token or | |
* it wasnt text | |
********************************************************* | |
*/ | |
if (type != eTokenText) | |
{ | |
pXML->error = eXMLErrorMissingTagName; | |
return FALSE; | |
} | |
/* | |
********************************************************* | |
* If we found a new element which has the same as this | |
* element then we need to pass this back to the caller.. | |
********************************************************* | |
*/ | |
/*modified by shoujunl*/ | |
/* | |
if (pElement->lpszName && | |
strncmp(pElement->lpszName, token.pStr, | |
strlen(pElement->lpszName)) == 0)*/ | |
mrvxml_log("%s,strlen(pElement->lpszName) is %d,cbToken =%d",__FUNCTION__,pElement->lpszName,cbToken); | |
mrvxml_log("%s,pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
if (pElement->lpszName && (strlen(pElement->lpszName) == (unsigned int)cbToken)&& | |
strncmp(pElement->lpszName, token.pStr, | |
strlen(pElement->lpszName)) == 0) | |
{ | |
/* | |
***************************************************** | |
* Indicate to the caller that it needs to create a | |
* new element. | |
***************************************************** | |
*/ | |
mrvxml_log("%s need to add a new element,pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
pXML->lpNewElement = token.pStr; | |
pXML->cbNewElement = cbToken; | |
return TRUE; | |
} | |
/* | |
********************************************************* | |
* If the name of the new element differs from the name of | |
* the current element we need to add the new element to | |
* the current one and recurse | |
********************************************************* | |
*/ | |
else | |
{ | |
/* | |
***************************************************** | |
* Now we need to add the new element and recurse | |
***************************************************** | |
*/ | |
pNew = MrvAddElement(pElement, | |
MrvStrdup(token.pStr, cbToken), nDeclaration, | |
MrvGROWBY); | |
mrvxml_log("%s add a new element pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset = | |
token.pStr - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
while(pNew) | |
{ | |
/* | |
************************************************* | |
* Callself to process the new node. If we return | |
* FALSE this means we dont have any more | |
* processing to do... | |
************************************************* | |
*/ | |
if (!MrvParseXMLElement(pXML, pNew)) | |
{ | |
return FALSE; | |
} | |
else | |
{ | |
mrvxml_log("%s recurse back,OK,pElement->lpszname is %s,pElement->nSize is %d",__FUNCTION__,pElement->lpszName,pElement->nSize); | |
/* | |
********************************************* | |
* If the call to recurse this function | |
* evented in a end tag specified in XML then | |
* we need to unwind the calls to this | |
* function until we find the appropriate node | |
* (the element name and end tag name must | |
* match) | |
********************************************* | |
*/ | |
if (pXML->cbEndTag) | |
{ | |
/* | |
***************************************** | |
* If we are back at the root node then we | |
* have an unmatched end tag | |
***************************************** | |
*/ | |
if (!pElement->lpszName) | |
{ | |
pXML->error = eXMLErrorUnmatchedEndTag; | |
mrvxml_log("%s ,back at the root node",__FUNCTION__); | |
if(pElement->pParent){ | |
mrvxml_log("%s pParent->lpszName is %s",__FUNCTION__,pElement->pParent->lpszName); | |
} | |
return FALSE; | |
} | |
/* | |
***************************************** | |
* If the end tag matches the name of this | |
* element then we only need to unwind | |
* once more... | |
***************************************** | |
*/ | |
/*modified by shoujunl*/ | |
/* | |
if (strncmp(pXML->lpEndTag, | |
pElement->lpszName, | |
strlen(pElement->lpszName)) == 0) | |
*/ | |
mrvxml_log("%s ,pXML->cbEndTag = %d,strlen(pElement->lpszName) = %d",__FUNCTION__,pXML->cbEndTag,strlen(pElement->lpszName)); | |
mrvxml_log("%s,pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
if (((unsigned int)pXML->cbEndTag == strlen(pElement->lpszName))&& | |
strncmp(pXML->lpEndTag, pElement->lpszName, strlen(pElement->lpszName)) == 0) | |
{ | |
pXML->cbEndTag = 0; | |
} | |
return TRUE; | |
} | |
/* | |
********************************************* | |
* If the call indicated a new element is to | |
* be created on THIS element. | |
********************************************* | |
*/ | |
else if (pXML->cbNewElement) | |
{ | |
/* | |
***************************************** | |
* If the name of this element matches the | |
* name of the element we need to create | |
* then we need to return to the caller | |
* and let it process the element. | |
***************************************** | |
*/ | |
/*modified by shoujunl*/ | |
/* | |
if (strncmp(pXML->lpNewElement, | |
pElement->lpszName, | |
strlen(pElement->lpszName)) == 0) | |
*/ | |
mrvxml_log("%s,strlen(pElement->lpszName) = %d,pXML->cbNewElement =%d",__FUNCTION__,strlen(pElement->lpszName),pXML->cbNewElement); | |
mrvxml_log("%s,pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
if ((strlen(pElement->lpszName) == (unsigned int)pXML->cbNewElement)&& | |
(strncmp(pXML->lpNewElement, pElement->lpszName, strlen(pElement->lpszName)) == 0)) | |
{ | |
return TRUE; | |
} | |
/* | |
***************************************** | |
* Add the new element and recurse | |
***************************************** | |
*/ | |
pNew = MrvAddElement(pElement, | |
MrvStrdup(pXML->lpNewElement, | |
pXML->cbNewElement), FALSE, | |
MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1]. | |
nStringOffset = | |
pXML->lpNewElement - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
pXML->cbNewElement = 0; | |
} | |
/* | |
********************************************* | |
* If we didn't have a new element to create | |
********************************************* | |
*/ | |
else | |
{ | |
pNew = NULL; | |
} | |
} | |
} | |
} | |
break; | |
/* | |
************************************************************* | |
* If we found an end tag | |
************************************************************* | |
*/ | |
case eTokenTagEnd: | |
/* | |
********************************************************* | |
* If we have node text then add this to the element | |
********************************************************* | |
*/ | |
if (lpszText) | |
{ | |
cbTemp = token.pStr - lpszText; | |
MrvFindEndOfText(lpszText, &cbTemp); | |
MrvAddText(pElement, MrvStrdup(lpszText, cbTemp), | |
MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset | |
= lpszText - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
lpszText = NULL; | |
} | |
/* | |
********************************************************* | |
* Find the name of the end tag | |
********************************************************* | |
*/ | |
//token = MrvGetNextToken(pXML, &cbTemp, &type); | |
MrvGetNextToken(pXML, &cbTemp, &type , &token); | |
/* | |
********************************************************* | |
* The end tag should be text | |
********************************************************* | |
*/ | |
if (type != eTokenText) | |
{ | |
pXML->error = eXMLErrorMissingEndTagName; | |
return FALSE; | |
} | |
lpszTemp = token.pStr; | |
/* | |
********************************************************* | |
* After the end tag we should find a closing tag | |
********************************************************* | |
*/ | |
//token = MrvGetNextToken(pXML, &cbToken, &type); | |
MrvGetNextToken(pXML, &cbToken, &type , &token); | |
if (type != eTokenCloseTag) | |
{ | |
pXML->error = eXMLErrorMissingEndTagName; | |
return FALSE; | |
} | |
/* | |
********************************************************* | |
* We need to return to the previous caller. If the name | |
* of the tag cannot be found we need to keep returning to | |
* caller until we find a match | |
********************************************************* | |
*/ | |
/*modified by shoujunl*/ | |
/* | |
if (strncmp(lpszTemp, pElement->lpszName, | |
strlen(pElement->lpszName)) != 0) | |
*/ | |
mrvxml_log("%s,pElement->lpszName is %s",__FUNCTION__,pElement->lpszName); | |
mrvxml_log("%s,cbTemp = %d,strlen(pElement->lpszName) is %d",__FUNCTION__,cbTemp,strlen(pElement->lpszName)); | |
if (((unsigned int)cbTemp != strlen(pElement->lpszName))|| | |
strncmp(lpszTemp, pElement->lpszName, | |
strlen(pElement->lpszName)) != 0) | |
{ | |
pXML->lpEndTag = lpszTemp; | |
pXML->cbEndTag = cbTemp; | |
} | |
/* | |
********************************************************* | |
* Return to the caller | |
********************************************************* | |
*/ | |
return TRUE; | |
/* | |
************************************************************* | |
* If we found a clear (unformatted) token | |
************************************************************* | |
*/ | |
case eTokenClear: | |
/* | |
********************************************************* | |
* If we have node text then add this to the element | |
********************************************************* | |
*/ | |
if (lpszText) | |
{ | |
cbTemp = token.pStr - lpszText; | |
MrvFindEndOfText(lpszText, &cbTemp); | |
MrvAddText(pElement, MrvStrdup(lpszText, cbTemp), | |
MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset | |
= lpszText - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
lpszText = NULL; | |
} | |
if (!MrvParseClearTag(pXML, pElement, token.pClr)) | |
{ | |
return FALSE; | |
} | |
break; | |
/* | |
************************************************************* | |
* Errors... | |
************************************************************* | |
*/ | |
case eTokenCloseTag: /* '>' */ | |
case eTokenShortHandClose: /* '/>' */ | |
pXML->error = eXMLErrorUnexpectedToken; | |
return FALSE; | |
break; | |
default: | |
break; | |
} | |
break; | |
/* | |
***************************************************************** | |
* If we are inside a tag definition we need to search for | |
* attributes | |
***************************************************************** | |
*/ | |
case eInsideTag: | |
/* | |
************************************************************* | |
* Check what part of the attribute (name, equals, value) we | |
* are looking for. | |
************************************************************* | |
*/ | |
switch(attrib) | |
{ | |
/* | |
************************************************************ | |
* If we are looking for a new attribute | |
************************************************************ | |
*/ | |
case eAttribName: | |
/* | |
********************************************************* | |
* Check what the current token type is | |
********************************************************* | |
*/ | |
switch(type) | |
{ | |
/* | |
********************************************************* | |
* If the current type is text... | |
* Eg. 'attribute' | |
********************************************************* | |
*/ | |
case eTokenText: | |
/* | |
***************************************************** | |
* Cache the token then indicate that we are next to | |
* look for the equals | |
***************************************************** | |
*/ | |
lpszTemp = token.pStr; | |
cbTemp = cbToken; | |
attrib = eAttribEquals; | |
break; | |
/* | |
********************************************************* | |
* If we found a closing tag... | |
* Eg. '>' | |
********************************************************* | |
*/ | |
case eTokenCloseTag: | |
/* | |
***************************************************** | |
* We are now outside the tag | |
***************************************************** | |
*/ | |
status = eOutsideTag; | |
break; | |
/* | |
********************************************************* | |
* If we found a short hand '/>' closing tag then we can | |
* return to the caller | |
********************************************************* | |
*/ | |
case eTokenShortHandClose: | |
return TRUE; | |
/* | |
********************************************************* | |
* Errors... | |
********************************************************* | |
*/ | |
case eTokenQuotedText: /* '"SomeText"' */ | |
case eTokenTagStart: /* '<' */ | |
case eTokenTagEnd: /* '</' */ | |
case eTokenEquals: /* '=' */ | |
case eTokenDeclaration: /* '<?' */ | |
case eTokenClear: | |
pXML->error = eXMLErrorUnexpectedToken; | |
return FALSE; | |
default: | |
break; | |
} | |
break; | |
/* | |
************************************************************ | |
* If we are looking for an equals | |
************************************************************ | |
*/ | |
case eAttribEquals: | |
/* | |
********************************************************* | |
* Check what the current token type is | |
********************************************************* | |
*/ | |
switch(type) | |
{ | |
/* | |
********************************************************* | |
* If the current type is text... | |
* Eg. 'Attribute AnotherAttribute' | |
********************************************************* | |
*/ | |
case eTokenText: | |
/* | |
***************************************************** | |
* Add the unvalued attribute to the list | |
***************************************************** | |
*/ | |
MrvAddAttribute(pElement, MrvStrdup(lpszTemp, | |
cbTemp), NULL, MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset = | |
lpszTemp - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
/* | |
***************************************************** | |
* Cache the token then indicate. We are next to | |
* look for the equals attribute | |
***************************************************** | |
*/ | |
lpszTemp = token.pStr; | |
cbTemp = cbToken; | |
break; | |
/* | |
********************************************************* | |
* If we found a closing tag 'Attribute >' or a short hand | |
* closing tag 'Attribute />' | |
********************************************************* | |
*/ | |
case eTokenShortHandClose: | |
case eTokenCloseTag: | |
/* | |
***************************************************** | |
* If we are a declaration element '<?' then we need | |
* to remove extra closing '?' if it exists | |
***************************************************** | |
*/ | |
if (pElement->nIsDeclaration && | |
(lpszTemp[cbTemp-1]) == '?') | |
{ | |
cbTemp--; | |
} | |
if (cbTemp) | |
{ | |
/* | |
************************************************* | |
* Add the unvalued attribute to the list | |
************************************************* | |
*/ | |
MrvAddAttribute(pElement, MrvStrdup(lpszTemp, | |
cbTemp), NULL, MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1]. | |
nStringOffset = lpszTemp - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
} | |
/* | |
***************************************************** | |
* If this is the end of the tag then return to the | |
* caller | |
***************************************************** | |
*/ | |
if (type == eTokenShortHandClose) | |
{ | |
return TRUE; | |
} | |
/* | |
***************************************************** | |
* We are now outside the tag | |
***************************************************** | |
*/ | |
status = eOutsideTag; | |
break; | |
/* | |
********************************************************* | |
* If we found the equals token... | |
* Eg. 'Attribute =' | |
********************************************************* | |
*/ | |
case eTokenEquals: | |
/* | |
***************************************************** | |
* Indicate that we next need to search for the value | |
* for the attribute | |
***************************************************** | |
*/ | |
attrib = eAttribValue; | |
break; | |
/* | |
********************************************************* | |
* Errors... | |
********************************************************* | |
*/ | |
case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/ | |
case eTokenTagStart: /* 'Attribute <' */ | |
case eTokenTagEnd: /* 'Attribute </' */ | |
case eTokenDeclaration: /* 'Attribute <?' */ | |
case eTokenClear: | |
pXML->error = eXMLErrorUnexpectedToken; | |
return FALSE; | |
default: | |
break; | |
} | |
break; | |
/* | |
************************************************************ | |
* If we are looking for an attribute value | |
************************************************************ | |
*/ | |
case eAttribValue: | |
/* | |
********************************************************* | |
* Check what the current token type is | |
********************************************************* | |
*/ | |
switch(type) | |
{ | |
/* | |
********************************************************* | |
* If the current type is text or quoted text... | |
* Eg. 'Attribute = "Value"' or 'Attribute = Value' or | |
* 'Attribute = 'Value''. | |
********************************************************* | |
*/ | |
case eTokenText: | |
case eTokenQuotedText: | |
/* | |
***************************************************** | |
* If we are a declaration element '<?' then we need | |
* to remove extra closing '?' if it exists | |
***************************************************** | |
*/ | |
if (pElement->nIsDeclaration && | |
(token.pStr[cbToken-1]) == '?') | |
{ | |
cbToken--; | |
} | |
if (cbTemp) | |
{ | |
lpszToken = MrvStrdup(token.pStr, cbToken); | |
} | |
else | |
{ | |
lpszToken = NULL; | |
} | |
/* | |
***************************************************** | |
* Add the valued attribute to the list | |
***************************************************** | |
*/ | |
MrvAddAttribute(pElement, MrvStrdup(lpszTemp, | |
cbTemp), (MRV_LPTSTR)lpszToken, MrvGROWBY); | |
#ifdef MrvSTOREOFFSETS | |
pElement->pEntries[pElement->nSize-1].nStringOffset | |
= lpszTemp - pXML->lpXML; | |
#endif /* MrvSTOREOFFSETS */ | |
/* | |
***************************************************** | |
* Indicate we are searching for a new attribute | |
***************************************************** | |
*/ | |
attrib = eAttribName; | |
break; | |
/* | |
********************************************************* | |
* Errors... | |
********************************************************* | |
*/ | |
case eTokenTagStart: /* 'Attr = <' */ | |
case eTokenTagEnd: /* 'Attr = </' */ | |
case eTokenCloseTag: /* 'Attr = >' */ | |
case eTokenShortHandClose: /* "Attr = />" */ | |
case eTokenEquals: /* 'Attr = =' */ | |
case eTokenDeclaration: /* 'Attr = <?' */ | |
case eTokenClear: | |
pXML->error = eXMLErrorUnexpectedToken; | |
return FALSE; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
/* | |
********************************************************************* | |
* If we failed to obtain the next token | |
********************************************************************* | |
*/ | |
else | |
{ | |
return FALSE; | |
} | |
} | |
//mrvxml_log("leave %s"); | |
}/* MrvParseXMLElement */ | |
/** | |
**************************************************************************** | |
* <P> Count the number of lines and columns in an XML string. </P> | |
* | |
* @methodName MrvCountLinesAndColumns | |
* | |
* @param lpXML | |
* @param nUpto | |
* @param *pResults | |
* | |
* @return void | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static void MrvCountLinesAndColumns(MRV_LPCTSTR lpXML, int nUpto, MrvXMLResults *pResults) | |
{ | |
MRV_TCHAR ch; | |
int n; | |
ASSERT(lpXML); | |
ASSERT(pResults); | |
pResults->nLine = 1; | |
pResults->nColumn = 1; | |
for(n=0; n<nUpto; n++) | |
{ | |
ch = lpXML[n]; | |
ASSERT(ch); | |
if (ch == '\n') | |
{ | |
pResults->nLine++; | |
pResults->nColumn = 1; | |
} | |
else | |
{ | |
pResults->nColumn++; | |
} | |
} | |
}/* MrvCountLinesAndColumns */ | |
/** | |
**************************************************************************** | |
* <P> Obtain tags used for unformatted text within elements. </P> | |
* | |
* @methodName * MrvGetClearTags | |
* | |
* @param none | |
* | |
* @return MrvClearTag | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
static MrvClearTag * MrvGetClearTags() | |
{ | |
static struct MrvClearTag tags[] = | |
{ | |
{ "<![CDATA[", "]]>" }, | |
{ "<PRE>", "</PRE>" }, | |
{ "<Script>", "</Script>" }, | |
{ "<!--", "-->" }, | |
{ "<!DOCTYPE", ">" }, | |
{ NULL, NULL } | |
}; | |
return tags; | |
}/* MrvGetClearTags */ | |
#define MrvGETCDATA() &MrvGetClearTags()[0] | |
/** | |
**************************************************************************** | |
* <P> Parse XML and return the root element. </P> | |
* | |
* @methodName * MrvParseXML | |
* | |
* @param lpszXML | |
* @param *pResults | |
* | |
* @return MrvXMLElement | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvParseXML(MRV_LPCTSTR lpszXML, MrvXMLResults *pResults) | |
{ | |
//mrvxml_log("enter %s",__FUNCTION__); | |
enum MrvXMLError error; | |
struct MrvXMLElement * pElement = NULL; | |
struct MrvXML xml = | |
{ NULL, 0, 0, NULL, 0, NULL, 0, TRUE , NULL }; | |
xml.lpXML = lpszXML; | |
xml.pClrTags = MrvGetClearTags(); | |
/* | |
************************************************************************* | |
* Create header element | |
************************************************************************* | |
*/ | |
pElement = MrvCreateRoot(); | |
MrvParseXMLElement(&xml, pElement); | |
error = xml.error; | |
mrvxml_log("%s, error is %d",__FUNCTION__,error); | |
/* | |
************************************************************************* | |
* If an error occurred | |
************************************************************************* | |
*/ | |
if (error != eXMLErrorNone) | |
{ | |
/* | |
********************************************************************* | |
* Cleanup | |
********************************************************************* | |
*/ | |
MrvDeleteRoot(pElement); | |
pElement = NULL; | |
} | |
/* | |
************************************************************************* | |
* If we hae been given somewhere to place results | |
************************************************************************* | |
*/ | |
if (pResults) | |
{ | |
pResults->error = error; | |
/* | |
********************************************************************* | |
* If we have an error | |
********************************************************************* | |
*/ | |
if (error != eXMLErrorNone) | |
{ | |
/* | |
***************************************************************** | |
* Find which line and column it starts on. | |
***************************************************************** | |
*/ | |
MrvCountLinesAndColumns(xml.lpXML, xml.nIndex, pResults); | |
mrvxml_log("%s error starts on Line %d, column %d", __FUNCTION__, pResults->nLine, pResults->nColumn); | |
} | |
} | |
//duster_free(lpszXML); | |
//lpszXML = NULL; | |
xml.lpXML = NULL; | |
//mrvxml_log("leave %s",__FUNCTION__); | |
return pElement; | |
}/* MrvParseXML */ | |
/** | |
**************************************************************************** | |
* <P> Search for an element in the list. </P> | |
* | |
* @methodName * MrvFindElement | |
* | |
* @param lpszName | |
* | |
* @return MrvXMLElement | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvFindElement(MrvXMLElement *pEntry, MRV_LPCTSTR lpszPath) | |
{ | |
MrvXMLElement * pChild; | |
MRV_LPCTSTR lpszName; | |
MRV_LPCTSTR lpszNext; | |
int cbName, nlpszNameLen = 0; | |
int nIndex; | |
ASSERT(lpszPath); | |
ASSERT(pEntry); | |
/* | |
************************************************************************* | |
* Find the next name in the path in case we need to recurse | |
************************************************************************* | |
*/ | |
lpszNext = strchr(lpszPath, '/'); | |
/* | |
************************************************************************* | |
* Calculate the length of the current name we are searching for | |
************************************************************************* | |
*/ | |
if (!lpszNext) | |
{ | |
cbName = strlen(lpszPath); | |
} | |
else | |
{ | |
cbName = lpszNext - lpszPath; | |
if (lpszNext[1]) | |
{ | |
lpszNext++; | |
} | |
else | |
{ | |
lpszNext = NULL; | |
} | |
} | |
if (cbName) | |
{ | |
/* | |
********************************************************************* | |
* Enumerate child elements | |
********************************************************************* | |
*/ | |
nIndex = 0; | |
while((pChild = MrvEnumElements(pEntry, &nIndex))) | |
{ | |
/* | |
***************************************************************** | |
* Obtain the name of the child element | |
***************************************************************** | |
*/ | |
lpszName = pChild->lpszName; | |
if (lpszName) | |
{ | |
/* | |
************************************************************* | |
* If the name of the element is what we are looking for | |
************************************************************* | |
*/ | |
/*modified by shoujunl*/ | |
nlpszNameLen = strlen(lpszName); | |
if ((nlpszNameLen == cbName)&& strncmp(lpszPath, lpszName, cbName) == 0) | |
{ | |
/* | |
********************************************************* | |
* Check if this is the last element to search for | |
********************************************************* | |
*/ | |
if (!lpszNext) | |
{ | |
return pChild; | |
} | |
/* | |
********************************************************* | |
* If we have further nodes to find then recurse. | |
********************************************************* | |
*/ | |
else | |
{ | |
return MrvFindElement(pChild, lpszNext); | |
} | |
} | |
} | |
} | |
} | |
return NULL; | |
}/* MrvFindElement */ | |
/** | |
**************************************************************************** | |
* <P> Find an attribute on an element. </P> | |
* | |
* @methodName * MrvFindAttribute | |
* | |
* @param *pEntry | |
* @param lpszAttrib | |
* | |
* @return MrvXMLAttribute | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLAttribute * MrvFindAttribute(MrvXMLElement *pEntry, MRV_LPCTSTR lpszAttrib) | |
{ | |
MrvXMLAttribute * pAttr; | |
int cbAttrib; | |
int nIndex; | |
ASSERT(pEntry); | |
ASSERT(lpszAttrib); | |
cbAttrib = strlen(lpszAttrib); | |
nIndex = 0; | |
while((pAttr = MrvEnumAttributes(pEntry, &nIndex))) | |
{ | |
if (strncmp(pAttr->lpszName, lpszAttrib, cbAttrib) == 0) | |
{ | |
return pAttr; | |
} | |
} | |
return NULL; | |
}/* MrvFindAttribute */ | |
/** | |
**************************************************************************** | |
* <P> Add CData to the element. </P> | |
* | |
* @methodName * MrvAddCData | |
* | |
* @param *pEntry | |
* @param lpszValue | |
* @param nGrowBy | |
* | |
* @return MrvXMLClear | |
* | |
* @exception none | |
* **************************************************************************** | |
*/ | |
MrvXMLClear * MrvAddCData(MrvXMLElement *pEntry, MRV_LPTSTR lpszValue, int nGrowBy) | |
{ | |
return MrvAddClear(pEntry, lpszValue, MrvGETCDATA(), nGrowBy); | |
}/* MrvAddCData */ | |
/** | |
**************************************************************************** | |
* <P> Add elements to the list if they do not exist, return the final node. | |
* </P> | |
* | |
* @methodName * MrvCreateElements | |
* | |
* @param *pEntry | |
* @param lpszPath | |
* | |
* @return MrvXMLElement | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MrvXMLElement * MrvCreateElements(MrvXMLElement *pEntry, MRV_LPCTSTR lpszPath) | |
{ | |
MrvXMLElement * pChild; | |
MrvXMLElement * pNew; | |
MRV_LPCTSTR lpszName; | |
MRV_LPCTSTR lpszNext; | |
int cbName; | |
int nIndex; | |
ASSERT(lpszPath); | |
ASSERT(pEntry); | |
/* | |
************************************************************************* | |
* Find the next name in the path in case we need to recurse | |
************************************************************************* | |
*/ | |
lpszNext = strchr(lpszPath, '/'); | |
/* | |
************************************************************************* | |
* Calculate the length of the current name we are searching for | |
************************************************************************* | |
*/ | |
if (!lpszNext) | |
{ | |
cbName = strlen(lpszPath); | |
} | |
else | |
{ | |
cbName = lpszNext - lpszPath; | |
if (lpszNext[1]) | |
{ | |
lpszNext++; | |
} | |
else | |
{ | |
lpszNext = NULL; | |
} | |
} | |
if (cbName) | |
{ | |
/* | |
********************************************************************* | |
* Enumerate child elements | |
********************************************************************* | |
*/ | |
nIndex = 0; | |
while((pChild = MrvEnumElements(pEntry, &nIndex))) | |
{ | |
/* | |
***************************************************************** | |
* Obtain the name of the child element | |
***************************************************************** | |
*/ | |
lpszName = pChild->lpszName; | |
if (lpszName) | |
{ | |
/* | |
************************************************************* | |
* If the name of the element is what we are looking for | |
************************************************************* | |
*/ | |
if (strncmp(lpszPath, lpszName, cbName) == 0) | |
{ | |
/* | |
********************************************************* | |
* Check if this is the last element to search for | |
********************************************************* | |
*/ | |
if (!lpszNext) | |
{ | |
return pChild; | |
} | |
/* | |
********************************************************* | |
* If we have further nodes to find then recurse. | |
********************************************************* | |
*/ | |
else | |
{ | |
return MrvCreateElements(pChild, lpszNext); | |
} | |
} | |
} | |
} | |
/* | |
********************************************************************* | |
* If we got this far then we couldn't find the required element so we | |
* need to add a new element to the current element | |
********************************************************************* | |
*/ | |
pNew = MrvAddElement(pEntry, MrvStrdup(lpszPath, cbName), FALSE, | |
MrvGROWBY); | |
/* | |
********************************************************************* | |
* If there are no more entries then return the node we just created. | |
********************************************************************* | |
*/ | |
if (!lpszNext) | |
{ | |
return pNew; | |
} | |
/* | |
********************************************************************* | |
* If there are more elements to search | |
********************************************************************* | |
*/ | |
else | |
{ | |
/* | |
***************************************************************** | |
* Recurse to add the remaining nodes | |
***************************************************************** | |
*/ | |
return MrvCreateElements(pNew, lpszNext); | |
} | |
} | |
return NULL; | |
}/* MrvCreateElements */ | |
/** | |
**************************************************************************** | |
* <P> Creates an user friendly XML string from a given element with | |
* appropriate white space and carriage returns. | |
* | |
* This recurses through all subnodes then adds contents of the nodes to the | |
* string. | |
* </P> | |
* | |
* @methodName MrvCreateXMLStringR | |
* | |
* @param MrvXMLElement * pEntry - XML Element | |
* @param MRV_LPTSTR lpszMarker - String to create results into, this | |
* can be zero if you want to calculate | |
* the size of the returned string. | |
* @param int nFormat - Specify -1 for no formatting or the | |
* indent level (0 initially). | |
* @return int - Size of the returned string, not | |
* including the NULL terminator. | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
int MrvCreateXMLStringR(MrvXMLElement * pEntry, MRV_LPTSTR lpszMarker, int nFormat) | |
{ | |
int nResult = 0; | |
int cb; | |
int cbElement; | |
int nIndex; | |
int nChildFormat; | |
int bHasChildren = FALSE; | |
MrvXMLNode * pChild; | |
MrvXMLAttribute * pAttr; | |
ASSERT(pEntry); | |
#define McbLENSTR(lpsz) (lpsz ? strlen(lpsz) : 0) | |
/* | |
************************************************************************* | |
* If the element has no name then assume this is the head node. | |
************************************************************************* | |
*/ | |
cbElement = McbLENSTR(pEntry->lpszName); | |
if (cbElement) | |
{ | |
/* | |
********************************************************************* | |
* "<elementname " | |
********************************************************************* | |
*/ | |
cb = nFormat == -1 ? 0 : nFormat; | |
if (lpszMarker) | |
{ | |
if (cb) | |
{ | |
memset(lpszMarker, MrvINDENTCHAR, sizeof(MRV_TCHAR)*cb); | |
} | |
nResult = cb; | |
lpszMarker[nResult++] = '<'; | |
strcpy(&lpszMarker[nResult], pEntry->lpszName); | |
nResult += cbElement; | |
lpszMarker[nResult++] = ' '; | |
} | |
else | |
{ | |
nResult += cbElement + 2 + cb; | |
} | |
/* | |
********************************************************************* | |
* Enumerate attributes and add them to the string | |
********************************************************************* | |
*/ | |
nIndex = 0; | |
while ((pChild = MrvEnumNodes(pEntry, &nIndex))) | |
{ | |
switch(pChild->type) | |
{ | |
/* | |
***************************************************************** | |
* Add attributes | |
***************************************************************** | |
*/ | |
case eNodeAttribute: | |
{ | |
pAttr = pChild->node.pAttrib; | |
/* | |
********************************************************* | |
* "Attrib | |
********************************************************* | |
*/ | |
cb = McbLENSTR(pAttr->lpszName); | |
if (cb) | |
{ | |
if (lpszMarker) | |
{ | |
strcpy(&lpszMarker[nResult], pAttr->lpszName); | |
} | |
nResult += cb; | |
/* | |
***************************************************** | |
* "Attrib=Value " | |
***************************************************** | |
*/ | |
/*modified bu shoujun */ | |
cb = McbLENSTR(pAttr->lpszValue); | |
if (cb) | |
{ | |
if (lpszMarker) | |
{ | |
lpszMarker[nResult] = '='; | |
lpszMarker[++nResult] = '"'; //added by shoujun | |
strcpy(&lpszMarker[nResult+1], | |
pAttr->lpszValue); | |
nResult += cb + 1; | |
lpszMarker[nResult++] = '"'; | |
} | |
else if(!lpszMarker) | |
nResult += cb + 3; | |
// nResult += cb + 1; | |
} | |
if (lpszMarker) | |
{ | |
lpszMarker[nResult] = ' '; | |
} | |
nResult++; | |
} | |
} | |
break; | |
case eNodeEmpty: | |
continue; | |
default: | |
/* | |
************************************************************* | |
* If this node isn't an attribute then flag that this element | |
* has children. | |
************************************************************* | |
*/ | |
bHasChildren = TRUE; | |
} | |
} | |
/* | |
********************************************************************* | |
* If there are child nodes we need to terminate the start tag | |
********************************************************************* | |
*/ | |
if (bHasChildren) | |
{ | |
if (lpszMarker) | |
{ | |
lpszMarker[nResult-1] = '>'; | |
} | |
if (nFormat != -1) | |
{ | |
if (lpszMarker) | |
{ | |
lpszMarker[nResult] = '\n'; | |
} | |
nResult++; | |
} | |
} | |
else | |
{ | |
nResult--; | |
} | |
} | |
/* | |
************************************************************************* | |
* Calculate the child format for when we recurse. This is used to | |
* determine the number of spaces used for prefixes. | |
************************************************************************* | |
*/ | |
if (nFormat == -1) | |
{ | |
nChildFormat = -1; | |
} | |
else | |
{ | |
if (cbElement) | |
{ | |
nChildFormat = nFormat + 1; | |
} | |
else | |
{ | |
nChildFormat = nFormat; | |
} | |
} | |
/* | |
************************************************************************* | |
* Enumerate through remaining children | |
************************************************************************* | |
*/ | |
nIndex = 0; | |
while ((pChild = MrvEnumNodes(pEntry, &nIndex))) | |
{ | |
switch(pChild->type) | |
{ | |
/* | |
********************************************************************* | |
* Text nodes | |
********************************************************************* | |
*/ | |
case eNodeText: | |
{ | |
/* | |
************************************************************* | |
* "Text" | |
************************************************************* | |
*/ | |
cb = McbLENSTR(pChild->node.pText->lpszValue); | |
if (cb) | |
{ | |
if (nFormat != -1) | |
{ | |
if (lpszMarker) | |
{ | |
memset(&lpszMarker[nResult], MrvINDENTCHAR, | |
sizeof(MRV_TCHAR)*(nFormat + 1)); | |
strcpy(&lpszMarker[nResult + nFormat + 1], | |
pChild->node.pText->lpszValue); | |
lpszMarker[nResult + nFormat + 1 + cb] = | |
'\n'; | |
} | |
nResult += cb + nFormat + 2; | |
} | |
else | |
{ | |
if (lpszMarker) | |
{ | |
strcpy(&lpszMarker[nResult], | |
pChild->node.pText->lpszValue); | |
} | |
nResult += cb; | |
} | |
} | |
} | |
break; | |
/* | |
********************************************************************* | |
* Clear type nodes | |
********************************************************************* | |
*/ | |
case eNodeClear: | |
{ | |
/* | |
************************************************************* | |
* "OpenTag" | |
************************************************************* | |
*/ | |
cb = McbLENSTR(pChild->node.pClear->lpszOpenTag); | |
if (cb) | |
{ | |
if (nFormat != -1) | |
{ | |
if (lpszMarker) | |
{ | |
memset(&lpszMarker[nResult], MrvINDENTCHAR, | |
sizeof(MRV_TCHAR)*(nFormat + 1)); | |
strcpy(&lpszMarker[nResult + nFormat + 1], | |
pChild->node.pClear->lpszOpenTag); | |
/* lpszMarker[nResult + nFormat + 1 + cb] = | |
_T('\n'); */ | |
} | |
/* nResult += cb + nFormat + 2; */ | |
nResult += cb + nFormat + 1; | |
} | |
else | |
{ | |
if (lpszMarker) | |
{ | |
strcpy(&lpszMarker[nResult], | |
pChild->node.pClear->lpszOpenTag); | |
} | |
nResult += cb; | |
} | |
} | |
/* | |
************************************************************* | |
* "OpenTag Value" | |
************************************************************* | |
*/ | |
cb = McbLENSTR(pChild->node.pClear->lpszValue); | |
if (cb) | |
{ | |
if (lpszMarker) | |
{ | |
strcpy(&lpszMarker[nResult], | |
pChild->node.pClear->lpszValue); | |
} | |
nResult += cb; | |
} | |
/* | |
************************************************************* | |
* "OpenTag Value CloseTag" | |
************************************************************* | |
*/ | |
cb = McbLENSTR(pChild->node.pClear->lpszCloseTag); | |
if (cb) | |
{ | |
if (lpszMarker) | |
{ | |
strcpy(&lpszMarker[nResult], | |
pChild->node.pClear->lpszCloseTag); | |
} | |
nResult += cb; | |
} | |
if (nFormat != -1) | |
{ | |
if (lpszMarker) | |
{ | |
lpszMarker[nResult] = '\n'; | |
} | |
nResult++; | |
} | |
} | |
break; | |
/* | |
********************************************************************* | |
* Element nodes | |
********************************************************************* | |
*/ | |
case eNodeElement: | |
{ | |
/* | |
************************************************************* | |
* Recursively add child nodes | |
************************************************************* | |
*/ | |
nResult += MrvCreateXMLStringR(pChild->node.pElement, | |
lpszMarker ? lpszMarker + nResult : 0, nChildFormat); | |
} | |
default: | |
break; | |
} | |
} | |
if (cbElement) | |
{ | |
/* | |
********************************************************************* | |
* If we have child entries we need to use long XML notation for | |
* closing the element - "<elementname>blah blah blah</elementname>" | |
********************************************************************* | |
*/ | |
if (bHasChildren) | |
{ | |
/* | |
***************************************************************** | |
* "</elementname>\0" | |
***************************************************************** | |
*/ | |
if (lpszMarker) | |
{ | |
if (nFormat != -1) | |
{ | |
if (nFormat) | |
{ | |
memset(&lpszMarker[nResult], MrvINDENTCHAR, | |
sizeof(MRV_TCHAR)*nFormat); | |
nResult+=nFormat; | |
} | |
} | |
strcpy(&lpszMarker[nResult], "</"); | |
nResult += 2; | |
strcpy(&lpszMarker[nResult], pEntry->lpszName); | |
nResult += cbElement; | |
if (nFormat == -1) | |
{ | |
strcpy(&lpszMarker[nResult], ">"); | |
nResult++; | |
} | |
else | |
{ | |
strcpy(&lpszMarker[nResult], ">\n"); | |
nResult += 2; | |
} | |
} | |
else | |
{ | |
if (nFormat != -1) | |
{ | |
nResult += cbElement + 4 + nFormat; | |
} | |
else | |
{ | |
nResult += cbElement + 3; | |
} | |
} | |
} | |
/* | |
********************************************************************* | |
* If there are no children we can use shorthand XML notation - | |
* "<elementname/>" | |
********************************************************************* | |
*/ | |
else | |
{ | |
/* | |
***************************************************************** | |
* "/>\0" | |
***************************************************************** | |
*/ | |
if (lpszMarker) | |
{ | |
if (nFormat == -1) | |
{ | |
strcpy(&lpszMarker[nResult], "/>"); | |
nResult += 2; | |
} | |
else | |
{ | |
strcpy(&lpszMarker[nResult], "/>\n"); | |
nResult += 3; | |
} | |
} | |
else | |
{ | |
nResult += nFormat == -1 ? 2 : 3; | |
} | |
} | |
} | |
return nResult; | |
}/* MrvCreateXMLStringR */ | |
/** | |
**************************************************************************** | |
* <P> Create an XML string from the head element. </P> | |
* | |
* @methodName MrvCreateXMLString | |
* | |
* @param MrvXMLElement * pHead - head element | |
* @param int nFormat - 0 if no formatting is required | |
* otherwise nonzero for formatted text | |
* with carriage returns and indentation. | |
* @param int *pnSize - [out] pointer to the size of the | |
* returned string not including the | |
* NULL terminator. | |
* | |
* @return MRV_LPTSTR - Allocated XML string, you must duster_free | |
* this with duster_free(). | |
* | |
* @exception none | |
* | |
**************************************************************************** | |
*/ | |
MRV_LPTSTR MrvCreateXMLString(MrvXMLElement * pHead, int nFormat, int *pnSize) | |
{ | |
MRV_LPTSTR lpszResult = NULL; | |
int cbStr; | |
if (pHead) | |
{ | |
/* | |
********************************************************************* | |
* Recursively Calculate the size of the XML string | |
********************************************************************* | |
*/ | |
nFormat = nFormat ? 0 : -1; | |
cbStr = MrvCreateXMLStringR(pHead, 0, nFormat); | |
ASSERT(cbStr); | |
/* | |
********************************************************************* | |
* Alllocate memory for the XML string + the NULL terminator and | |
* create the recursively XML string. | |
********************************************************************* | |
*/ | |
lpszResult = (MRV_LPTSTR)duster_malloc(cbStr+1); | |
if (lpszResult) | |
{ | |
MrvCreateXMLStringR(pHead, lpszResult, nFormat); | |
if (pnSize) *pnSize = cbStr; | |
} | |
} | |
return lpszResult; | |
}/* MrvCreateXMLString */ | |
/* | |
********************************************************************* | |
* Add New Node To the List; | |
* Toby add 2006/7/3/9:40 | |
********************************************************************* | |
*/ | |
MrvXMLElement* AddElementToXML(MrvXMLElement *pParent,MRV_LPTSTR lpszText) | |
{ | |
//NodeText_st *pTemp = NULL; | |
MrvXMLElement *pElement; | |
//pTemp = (NodeText_st *)duster_malloc(sizeof(NodeText_st)); | |
//got the mem? | |
//if(!pTemp) | |
// return NULL; | |
#ifdef DYNAMIC_MEM | |
// pTemp->lpszText = (MRV_LPTSTR)duster_malloc((char)*strlen(lpszText)); | |
#endif | |
//strcpy(pTemp->lpszText,lpszText); | |
//AddToList(&g_StrList,&(pTemp->List)); | |
//pElement = MrvCreateElements(pParent,pTemp->lpszText); | |
pElement = MrvCreateElements(pParent,lpszText); | |
return pElement; | |
} | |
/* | |
********************************************************************* | |
* Add New Attr Node To the List; | |
* Toby add 2006/7/3/9:40 | |
********************************************************************* | |
*/ | |
MrvXMLAttribute* AddAttrToXML(MrvXMLElement *pEntry,MRV_LPTSTR lpszName,MRV_LPTSTR lpszValue) | |
{ | |
MrvXMLAttribute *pAttr = NULL; | |
if(!pEntry) | |
return NULL; | |
pAttr = MrvAddAttribute(pEntry, | |
MrvStrdup(lpszName,0), | |
MrvStrdup(lpszValue,0), | |
1); | |
return pAttr; | |
} | |
/* | |
********************************************************************* | |
* Delete Node From the List; | |
* Toby add 2006/7/3/9:53 | |
********************************************************************* | |
*/ | |
void DeleteElementFromXML(MrvXMLElement *pEntry) | |
{ | |
NodeText_st *pTemp = NULL; | |
if(!pEntry) | |
return; | |
pTemp = ListEntry(pEntry->lpszName,NodeText_st,lpszText); | |
DeleteFromList(&(pTemp->List)); | |
duster_free(pTemp->lpszText); | |
duster_free(pTemp); | |
//delete from the list...add Later | |
//coverity[double_free:SUPPRESS] | |
MrvDeleteElement(pEntry); | |
} | |
/* | |
********************************************************************* | |
* Delete Node From the List; | |
* Toby add 2006/7/3/10:01 | |
********************************************************************* | |
*/ | |
void DeleteAttrFromXML(MrvXMLAttribute *pEntry) | |
{ | |
NodeText_st *pTemp = NULL; | |
pTemp = ListEntry(pEntry->lpszName,NodeText_st,lpszText); | |
DeleteFromList(&(pTemp->List)); | |
duster_free(pTemp->lpszText); | |
duster_free(pTemp); | |
pTemp = ListEntry(pEntry->lpszValue,NodeText_st,lpszText); | |
DeleteFromList(&(pTemp->List)); | |
duster_free(pTemp->lpszText); | |
duster_free(pTemp); | |
//coverity[check_after_deref:SUPPRESS] | |
if(!pEntry) | |
return; | |
//delete from the list... add later | |
//coverity[double_free:SUPPRESS] | |
MrvDeleteAttribute(pEntry); | |
} | |
MrvXMLNode * MrvEnumPrintf(MrvXMLElement *pEntry,int *nrow) | |
{ | |
int nIndex = 0,prenRow; | |
MrvXMLElement * pResult = NULL; | |
//MrvXMLElement * pParent = NULL; | |
//char *ver = NULL; | |
mrvxml_log("enter %s,pEntry->nSize is %d,pEntry->nIsDeclaration is %d,pEntry->lpszName is %s",__FUNCTION__,pEntry->nSize,pEntry->nIsDeclaration,pEntry->lpszName); | |
if(pEntry->pParent) | |
{ | |
mrvxml_log("%s,this element has parents",__FUNCTION__); | |
//pParent = pEntry->pParent; | |
mrvxml_log("%s,pParent->nSize is %d,pParent->nIsDeclaration is %d,pParent->lpszName is %s", | |
__FUNCTION__,pParent->nSize,pParent->nIsDeclaration,pParent->lpszName); | |
} | |
else | |
{ | |
mrvxml_log("%s,this element has no parents",__FUNCTION__); | |
} | |
(*nrow)++; | |
prenRow = *nrow; | |
for (;nIndex < pEntry->nSize ; nIndex++) | |
{ | |
mrvxml_log("%s root->pEntries[%d].type is %d,nrow is %d", __FUNCTION__,nIndex,pEntry->pEntries[nIndex].type,*nrow); | |
switch(pEntry->pEntries[nIndex].type) | |
{ | |
case eNodeElement: | |
pResult = pEntry->pEntries[nIndex].node.pElement; | |
mrvxml_log("%s Element type root->pEntries[%d].lpszName is %s,pResult->nSize is %d",__FUNCTION__,nIndex,pResult->lpszName,pResult->nSize); | |
/*if(strcmp(pResult->lpszName,"version_num") == 0) | |
{ | |
mrvxml_log("%s,meet version_num,pResult->nSize is %d",__FUNCTION__,pResult->nSize); | |
ver = duster_malloc(10); | |
memset(ver, 0, 10); | |
memcpy(ver, "nezha", 5); | |
MrvAddText(pResult, ver, 1); | |
} | |
*/ | |
MrvEnumPrintf(pResult,nrow); | |
break; | |
case eNodeAttribute: | |
mrvxml_log("%s eNodeAttribute name is %s,value is %s",__FUNCTION__,(pEntry->pEntries[nIndex].node.pAttrib)->lpszName,(pEntry->pEntries[nIndex].node.pAttrib)->lpszValue); | |
break; | |
case eNodeText: | |
mrvxml_log("%s eNodeText,value is %s",__FUNCTION__,(pEntry->pEntries[nIndex].node.pText)->lpszValue); | |
break; | |
case eNodeClear: | |
mrvxml_log("%s eNodeClear,",__FUNCTION__,(pEntry->pEntries[nIndex].node.pClear)->lpszOpenTag,(pEntry->pEntries[nIndex].node.pClear)->lpszValue,(pEntry->pEntries[nIndex].node.pClear)->lpszCloseTag); | |
break; | |
case eNodeEmpty: | |
mrvxml_log("%s eNodeEmpty",__FUNCTION__); | |
} | |
*nrow = prenRow; | |
} | |
return NULL; | |
} | |
/* | |
********************************************************************* | |
*McbCountElementl | |
*2013-05-31 --added by shoujunl | |
********************************************************************* | |
*/ | |
int MrvCountElement(MrvXMLElement *pEntry) | |
{ | |
int num = 0, nIndex = 0; | |
for(;nIndex < pEntry->nSize;nIndex++) | |
{ | |
if(pEntry->pEntries[nIndex].type == eNodeElement) | |
num++; | |
} | |
return num; | |
} | |
/* | |
********************************************************************* | |
*McbCountElementl | |
*2013-05-31 --added by shoujunl | |
********************************************************************* | |
*/ | |
MrvXMLText *MrvFindOnlyText(MrvXMLElement * pEntry) | |
{ | |
int textNodeNum = 0,nIndex = 0; | |
MrvXMLText *pText = NULL; | |
for(;nIndex < pEntry->nSize;nIndex++) | |
{ | |
if(pEntry->pEntries[nIndex].type == eNodeText) | |
{ | |
pText = pEntry->pEntries[nIndex].node.pText; | |
textNodeNum++; | |
if(textNodeNum>1) | |
return NULL; | |
} | |
} | |
return pText; | |
} | |