blob: 7580312d9e8ff78b3b9c89ad92f2055f82c985ab [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <xml/xml_tinyParser.h>
int32_t xml_errno;
#ifdef XML_DOM_PARSER
#define XML_IS_WHITESPACE(x) ((x) == '\t' || (x) == '\n' || (x) == ' ' || (x) == '\r')
#define XML_IS_NAMECHAR(ch) (isalpha(ch) || isdigit(ch) || ch ==':' || \
ch == '_' || ch == '-' || ch =='.')
static uint8_t *xml_ignore_blank(uint8_t *buffer)
{
if (NULL == buffer)
return NULL;
while (XML_IS_WHITESPACE(*buffer))
buffer++;
return buffer;
}
static uint8_t *xml_goto_tagend(uint8_t *buffer)
{
int32_t nameLen, valueLen;
uint8_t *name, *value;
if (NULL == buffer)
return NULL;
/* Ignore the start-tag */
if (*buffer == '<') {
buffer++;
while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
buffer++;
if (NULL == buffer)
return NULL;
}
do {
if (NULL == (buffer = xml_ignore_blank(buffer)))
return NULL;
if (*buffer == '>' || (*buffer == '/' && *(buffer + 1) == '>'))
return buffer;
if (NULL ==
XML_DOM_getAttr(buffer, &name, &nameLen, &value, &valueLen))
return NULL;
buffer = value + valueLen + 1;
} while (*buffer != '\0');
return NULL;
}
static uint8_t *xml_match_tag(uint8_t *buffer)
{
int32_t tagLen, tagType, bal;
if (NULL == buffer)
return NULL;
bal = 0;
do {
if (NULL == (buffer = XML_DOM_getTag(buffer, &tagLen, &tagType)))
return NULL;
switch (tagType) {
case XML_TAG_SELF:
case XML_TAG_START:
if (NULL == (buffer = xml_goto_tagend(buffer + tagLen + 1)))
return NULL;
if (strncmp((char *)buffer, "/>", 2) == 0) {
buffer += 2;
} else {
bal++;
}
break;
case XML_TAG_END:
if (bal <= 0)
return NULL;
buffer = buffer + tagLen + 2;
bal--;
break;
}
} while (bal != 0);
return buffer;
}
uint8_t *XML_DOM_getAttr(uint8_t *buffer, uint8_t **pName, int32_t *nameLen,
uint8_t **pValue, int32_t *valueLen)
{
uint8_t charQuoted;
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
/* Ignore the tag */
if (*buffer == '<') {
buffer++;
/* Ignore the STag */
while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
buffer++;
if (NULL == buffer)
return NULL;
}
if (NULL == (buffer = xml_ignore_blank(buffer))) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
/* Name */
*pName = buffer;
while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
buffer++;
if (NULL == buffer) {
XML_ERROR(XML_ERROR_ATTR_NAME);
return NULL;
}
*nameLen = buffer - *pName;
if (*nameLen <= 0) {
XML_ERROR(XML_ERROR_ATTR_NAME);
return NULL;
}
/* '=' */
buffer = xml_ignore_blank(buffer);
if (NULL == buffer || *buffer != '=') {
XML_ERROR(XML_ERROR_ATTR_MISSED_EQUAL);
return NULL;
}
/* Value */
buffer++;
buffer = xml_ignore_blank(buffer);
if (NULL == buffer || (*buffer != '"' && *buffer != '\'')) {
XML_ERROR(XML_ERROR_ATTR_VALUE);
return NULL;
}
charQuoted = *buffer++;
*pValue = buffer;
while (*buffer != '\0' && *buffer != charQuoted)
buffer++;
if (*buffer != charQuoted) {
XML_ERROR(XML_ERROR_ATTR_VALUE);
return NULL;
}
*valueLen = buffer - *pValue;
XML_ERROR(XML_ERROR_OK);
return buffer + 1;
}
uint8_t *XML_DOM_getValue(uint8_t *buffer, uint8_t **pValue, int32_t *valueLen)
{
uint8_t *pEnd;
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
/* Ignore the STag */
if (*buffer == '<') {
buffer++;
/* If it's an end_tag, no value should be returned */
if (*buffer == '/') {
*valueLen = 0;
XML_ERROR(XML_ERROR_NOVALUE);
return NULL;
}
while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
buffer++;
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
if (NULL == (buffer = xml_goto_tagend(buffer))) {
XML_ERROR(XML_ERROR_PROPERTY_END);
return NULL;
}
}
/* <test/> node found */
if (*buffer == '/') {
if (*(buffer + 1) != '>') {
XML_ERROR(XML_ERROR_PROPERTY_END);
return NULL;
}
XML_ERROR(XML_ERROR_OK);
*valueLen = 0;
return buffer;
}
if (*buffer == '>')
buffer++;
if (NULL == (buffer = xml_ignore_blank(buffer))) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
/* the following is a tag instead of the value */
if (*buffer == '<') { /* nono value, such as <test></test> */
buffer++;
if (*buffer != '/') {
XML_ERROR(XML_ERROR_ENDTAG);
return NULL;
}
*valueLen = 0;
XML_ERROR(XML_ERROR_OK);
return NULL;
}
*pValue = buffer;
pEnd = NULL;
while (*buffer != '\0' && *buffer != '<') {
if (!XML_IS_WHITESPACE(*buffer))
pEnd = buffer;
buffer++;
}
if (*buffer != '<' || pEnd == NULL) {
XML_ERROR(XML_ERROR_VALUE);
return NULL;
}
*valueLen = pEnd - *pValue + 1;
buffer++;
if (*buffer != '/') {
XML_ERROR(XML_ERROR_ENDTAG);
return NULL;
}
XML_ERROR(XML_ERROR_OK);
return buffer - 1;
}
uint8_t *XML_DOM_getTag(uint8_t *buffer, int32_t *tagLen, int32_t *tagType)
{
uint8_t *pStart;
/* WARNING: <!-- --> comment is not supported in this verison */
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
do {
while (*buffer != '<') {
if (*buffer == '\0') {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
if (*buffer == '\"' || *buffer == '\'') {
uint8_t charQuoted = *buffer;
buffer++;
while (*buffer != '\0' && *buffer != charQuoted)
buffer++;
if (*buffer == '\0') {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
}
buffer++;
}
buffer++;
} while (*buffer == '!' || *buffer == '?');
pStart = buffer - 1;
if (*buffer == '/') {
buffer++;
*tagType = XML_TAG_END;
} else {
/* check here if it is self-end-tag */
uint8_t *pCheck = xml_goto_tagend(pStart);
if (pCheck == NULL) {
XML_ERROR(XML_ERROR_PROPERTY_END);
return NULL;
}
if (*pCheck == '>')
*tagType = XML_TAG_START;
else if (strncmp((char *)pCheck, "/>", 2) == 0)
*tagType = XML_TAG_SELF;
else {
XML_ERROR(XML_ERROR_PROPERTY_END);
return NULL;
}
}
while (buffer != NULL && XML_IS_NAMECHAR(*buffer))
buffer++;
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
if (*tagType == XML_TAG_END)
*tagLen = buffer - pStart - 2;
else
*tagLen = buffer - pStart - 1;
XML_ERROR(XML_ERROR_OK);
return pStart;
}
uint8_t *XML_DOM_getNode(uint8_t *buffer, const uint8_t *const node)
{
uint8_t *pStart;
uint8_t buf[XML_MAX_PROPERTY_LEN + 2];
uint8_t *nodeStr = buf;
uint8_t *retPtr = NULL;
int32_t tagLen, tagType;
uint8_t *lastNode = (uint8_t *)"";
if (NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
strncpy((char *)nodeStr, (char *)node, XML_MAX_PROPERTY_LEN);
strcat((char *)nodeStr, "\\");
pStart = (uint8_t *)strchr((char *)nodeStr, '\\');
while (pStart != NULL) {
*pStart = '\0';
/* get the first start_tag from buffer */
if (NULL == (buffer = XML_DOM_getTag(buffer, &tagLen, &tagType))) {
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
if (tagType == XML_TAG_END) {
if (0 ==
strncmp((char *)lastNode, (char *)(buffer + 2), strlen((char *)lastNode)))
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
else
XML_ERROR(XML_ERROR_NO_START_TAG);
return NULL;
}
/* wrong node, contiue to fetch the next node */
if ((int32_t) strlen((char *)nodeStr) != tagLen
|| strncmp((char *)nodeStr, (char *)(buffer + 1), tagLen) != 0) {
/* we should ignore all the middle code */
buffer = xml_match_tag(buffer);
continue;
}
retPtr = buffer; /* retPtr starts with '<xxx>' */
buffer += (tagLen + 1);
if (tagType == XML_TAG_SELF) {
nodeStr = pStart + 1;
break;
}
lastNode = nodeStr;
nodeStr = pStart + 1;
pStart = (uint8_t *)strchr((char *)nodeStr, '\\');
}
/* Check 5: nodeStr should be empty here */
if (*nodeStr != '\0') {
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
XML_ERROR(XML_ERROR_OK);
return retPtr;
}
uint8_t *XML_DOM_getNodeValue(uint8_t *buffer, uint8_t *node,
uint8_t **value, int32_t *valueLen)
{
uint8_t *pStart;
uint8_t *lastTag;
if (NULL == node || NULL == buffer) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
lastTag = node + strlen((char *)node) - 1;
while (lastTag >= node && *lastTag != '\\')
lastTag--;
lastTag++;
if (NULL == (pStart = XML_DOM_getNode(buffer, node)))
return NULL;
pStart += (strlen((char *)lastTag) + 1);
if (NULL == (pStart = xml_goto_tagend(pStart))) {
XML_ERROR(XML_ERROR_PROPERTY_END);
return NULL;
}
if (NULL == (pStart = XML_DOM_getValue(pStart, value, valueLen)))
return NULL;
/* Check the end tag */
#ifdef XML_DOM_CHECK_ENDTAG
if (strncmp((char *)pStart, "/>", 2) == 0) {
} else if (strncmp((char *)lastTag, (char *)(pStart + 2), strlen((char *)lastTag)) !=
0) {
XML_ERROR(XML_ERROR_ENDTAG);
return NULL;
}
#endif
XML_ERROR(XML_ERROR_OK);
return *value;
}
uint8_t *XML_DOM_getNextNode(uint8_t *buffer, uint8_t **pNodeName, int32_t *nodenameLen)
{
int32_t tagType;
if (NULL == buffer)
return NULL;
do {
if (NULL ==
(buffer = XML_DOM_getTag(buffer + 1, nodenameLen, &tagType))) {
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
} while (tagType == XML_TAG_END);
*pNodeName = buffer + 1;
XML_ERROR(XML_ERROR_OK);
return buffer;
}
#endif /* XML_DOM_PARSER */
#ifdef WBXML_DOM_PARSER
#ifdef WBXML_OLD_VERSION
uint8_t *WBXML_DOM_getNode(uint8_t *buffer, int32_t bufferLen,
uint8_t *node)
{
int32_t i = 0, j = 0;
if (NULL == buffer || node == NULL) {
XML_ERROR(XML_ERROR_BUFFER_NULL);
return NULL;
}
while (i < bufferLen) {
if (WBXML_GET_TAG(buffer[i]) == WBXML_GET_TAG(node[j])) {
j++;
if (node[j] == '\0')
break;
/* Check if there is the content(it should have content) */
if (!WBXML_HAS_CONTENT(buffer[i])) {
/*XML_ERROR(WBXML_ERROR_MISSED_CONTENT); */
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
/* Ignore the attrib filed */
if (WBXML_HAS_ATTR(buffer[i])) {
while (i < bufferLen && buffer[i] != WBXML_ATTR_END)
i++;
if (i >= bufferLen)
break;
}
}
i++;
/* Ignore the content filed */
if (buffer[i] == WBXML_STR_I) {
while (i < bufferLen && buffer[i] != WBXML_END)
i++;
if (i >= bufferLen)
break;
i++;
}
}
if (i >= bufferLen) {
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
XML_ERROR(XML_ERROR_OK);
return buffer + i + 1;
}
uint8_t *WBXML_DOM_getNodeValue(uint8_t *buffer, int32_t bufferLen,
uint8_t *node,
uint8_t **value, int32_t *valueLen)
{
int32_t i;
uint8_t *pEnd;
*value = NULL;
*valueLen = 0;
pEnd = buffer + bufferLen;
buffer = WBXML_DOM_getNode(buffer, bufferLen, node);
if (NULL == buffer) {
XML_ERROR(XML_ERROR_NO_SUCH_NODE);
return NULL;
}
if (*buffer == WBXML_OPAUE) {
buffer++;
*valueLen = WBXML_GetUintVar(buffer, &i);
if (*valueLen < 0) {
XML_ERROR(WBXML_ERROR_MBUINT32);
return NULL;
}
buffer += i;
*value = buffer;
return *value;
}
if (*buffer != WBXML_STR_I) {
XML_ERROR(WBXML_ERROR_MISSED_STARTTAG);
return NULL;
}
buffer++;
i = 0;
while ((buffer + i) < pEnd && buffer[i] != WBXML_END)
i++;
if (buffer[i] != WBXML_END) {
XML_ERROR(WBXML_ERROR_MISSED_ENDTAG);
return NULL;
}
*value = buffer;
*valueLen = i;
XML_ERROR(XML_ERROR_OK);
return *value;
}
#endif /* WBXML_OLD_VERSION */
#define MAX_UINT_VAR_BYTE 4
#define UINTVAR_INVALID -1
int32_t WBXML_GetUintVar(const uint8_t *const buffer, int32_t *len)
{
int32_t i, byteLen;
int32_t sum;
byteLen = 0;
while ((buffer[byteLen] & 0x80) > 0 && byteLen < MAX_UINT_VAR_BYTE)
byteLen++;
if (byteLen > MAX_UINT_VAR_BYTE)
return UINTVAR_INVALID;
*len = byteLen + 1;
sum = buffer[byteLen];
for (i = byteLen - 1; i >= 0; i--)
sum += ((buffer[i] & 0x7F) << 7 * (byteLen - i));
return sum;
}
XML_BOOL WBXML_DOM_Init(WBXML * pWbxml, uint8_t *buffer,
int32_t bufferLen)
{
int32_t num, len;
pWbxml->End = buffer + bufferLen;
pWbxml->version = *buffer++;
if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
return XML_FALSE;
buffer += len;
pWbxml->publicid = num;
if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
return XML_FALSE;
buffer += len;
pWbxml->charset = num;
if (UINTVAR_INVALID == (num = WBXML_GetUintVar(buffer, &len)))
return XML_FALSE;
buffer += len;
pWbxml->strTable = buffer;
pWbxml->strTableLen = num;
buffer += num;
pWbxml->curPtr = pWbxml->Content = buffer;
pWbxml->depth = 0;
return XML_TRUE;
}
void WBXML_DOM_Rewind(WBXML * pWbxml)
{
pWbxml->curPtr = pWbxml->Content;
}
XML_BOOL WBXML_DOM_Eof(WBXML * pWbxml)
{
if (pWbxml->curPtr > pWbxml->End)
return XML_TRUE;
return XML_FALSE;
}
uint8_t WBXML_DOM_GetTag(WBXML * pWbxml)
{
uint8_t tagChar;
if (pWbxml->curPtr > pWbxml->End)
return XML_EOF;
tagChar = *pWbxml->curPtr;
pWbxml->curPtr++;
if (WBXML_GET_TAG(tagChar) == WBXML_CONTENT_END)
pWbxml->depth--;
else
pWbxml->depth++;
return tagChar;
}
uint8_t WBXML_DOM_GetChar(WBXML * pWbxml)
{
return *pWbxml->curPtr++;
}
void WBXML_DOM_Seek(WBXML * pWbxml, int32_t offset)
{
pWbxml->curPtr += offset;
}
uint8_t WBXML_DOM_GetUIntVar(WBXML * pWbxml)
{
int32_t num, len;
num = WBXML_GetUintVar(pWbxml->curPtr, &len);
pWbxml->curPtr += len;
return (uint8_t)num;
}
#ifdef XML_TREE_STRUCTURE
#ifdef DEBUG_MODE
static int32_t malloc_times = 0;
static int32_t free_times = 0;
void XML_PrintMallocInfo()
{
printf("====XML_PrintMallocInfo====\n");
printf(" Total malloc times:%d\n", malloc_times);
printf(" Total free times:%d\n", free_times);
printf("===========================\n");
}
#endif
void *xml_malloc(int32_t size)
{
#ifdef DEBUG_MODE
malloc_times++;
#endif
return malloc(size);
}
void xml_free(void *buffer)
{
#ifdef DEBUG_MODE
free_times++;
#endif
free(buffer);
}
XML_TREE *xml_tree_fillnode(uint8_t **buf, int32_t tagLen)
{
XML_TREE *Tree;
uint8_t *pAttr, *pName, *pValue;
int32_t nameLen, valueLen;
uint8_t *buffer = *buf;
if (NULL == (Tree = (XML_TREE *) xml_malloc(sizeof(XML_TREE))))
return NULL;
memset(Tree, 0, sizeof(XML_TREE));
strncpy((char *)Tree->tag, (char *)++buffer, tagLen);
buffer += tagLen;
pAttr = buffer;
/* attribute */
while (NULL !=
(pAttr =
XML_DOM_getAttr(pAttr, &pName, &nameLen, &pValue,
&valueLen))) {
XML_TREE_ATTR *attr;
if (NULL ==
(attr = (XML_TREE_ATTR *) xml_malloc(sizeof(XML_TREE_ATTR))))
return NULL;
memset(attr, 0, sizeof(XML_TREE_ATTR));
strncpy((char *)attr->name, (char *)pName, nameLen);
strncpy((char *)attr->value, (char *)pValue, valueLen);
buffer = pValue + valueLen + 1;
if (NULL != Tree->attr) // no attribute now
Tree->last_attr->next = attr;
else
Tree->attr = attr;
Tree->last_attr = attr;
}
/* value */
pAttr = XML_DOM_getValue(buffer, &pValue, &valueLen);
if (pAttr != NULL && valueLen > 0) {
strncpy((char *)Tree->value, (char *)pValue, valueLen);
buffer = pValue + valueLen;
}
*buf = buffer;
return Tree;
}
XML_TREE *XML_makeTree(uint8_t **buf)
{
uint8_t *pBuf;
int32_t valueLen, tagType;
uint8_t *buffer = *buf;
XML_TREE *TreeHead = NULL;
if (NULL == (buffer = XML_DOM_getTag(buffer, &valueLen, &tagType)))
return NULL;
if (XML_TAG_END == tagType)
return NULL;
if (NULL == (TreeHead = xml_tree_fillnode(&buffer, valueLen)))
return NULL;
if (XML_TAG_SELF == tagType) {
*buf = buffer;
return TreeHead;
}
do {
if (NULL == (pBuf = XML_DOM_getTag(buffer, &valueLen, &tagType)))
return NULL;
switch (tagType) {
case XML_TAG_SELF:
case XML_TAG_START:
if (NULL == TreeHead->child)
TreeHead->child = XML_makeTree(&buffer);
else if (NULL == TreeHead->child->last_brother) {
TreeHead->child->brother = XML_makeTree(&buffer);
TreeHead->child->last_brother = TreeHead->child->brother;
} else {
TreeHead->child->last_brother->brother =
XML_makeTree(&buffer);
TreeHead->child->last_brother =
TreeHead->child->last_brother->brother;
}
break;
case XML_TAG_END:
*buf = pBuf;
return TreeHead;
}
buffer++;
} while (1);
}
void XML_freeTree(XML_TREE * pTree)
{
XML_TREE *p, *pNext;
XML_TREE_ATTR *pa, *lastpa;
if (NULL == pTree)
return;
p = pTree->brother;
while (NULL != p) {
pNext = p->brother;
p->brother = NULL;
XML_freeTree(p);
p = pNext;
}
if (NULL != pTree->child)
XML_freeTree(pTree->child);
pa = pTree->attr;
while (NULL != pa) {
lastpa = pa;
pa = pa->next;
xml_free(lastpa);
}
xml_free(pTree);
}
#endif /* XML_TREE_STRUCTURE */
#endif /* WBXML_DOM_PARSER */