/*******************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation
 * All rights reserved.
 * Copyright (c) 2012 France Telecom All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * - Neither name of Intel Corporation nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/*!
 * \file
 */

#include "autoconfig.h"

#include "ixmlparser.h"

#include "ixmldebug.h"

#include <assert.h>
#include <stddef.h> /* for ptrdiff_t */
#include <stdio.h>
#include <stdlib.h> /* for free(), malloc() */
#include <string.h>

#include "posix_overwrites.h"

static char g_error_char = '\0';
#ifdef IXML_HAVE_SCRIPTSUPPORT
static IXML_BeforeFreeNode_t Before_Free_callback;
#endif

static const char LESSTHAN = '<';
static const char GREATERTHAN = '>';
static const char SLASH = '/';
static const char EQUALS = '=';
static const char QUOTE = '\"';
static const char SINGLEQUOTE = '\'';

static const char *WHITESPACE = "\n\t\r ";
static const char *COMPLETETAG = "/>";
static const char *ENDTAG = "</";
static const char *XMLDECL = "<?xml ";
static const char *XMLDECL2 = "<?xml?";
static const char *BEGIN_COMMENT = "<!--";
static const char *END_COMMENT = "-->";
static const char *BEGIN_PI = "<?";
static const char *END_PI = "?>";
static const char *BEGIN_DOCTYPE = "<!DOCTYPE";
static const char *CDSTART = "<![CDATA[";
static const char *CDEND = "]]>";
static const char *DEC_NUMBERS = "0123456789";
static const char *HEX_NUMBERS = "0123456789ABCDEFabcdef";
static const char *UTF8_BOM = "\xef\xbb\xbf";

typedef struct char_info
{
	unsigned short l;
	unsigned short h;
} char_info_t;

typedef char utf8char[8];

/*!
 * \brief The letter table contains all characters in XML 1.0 plus ":", "_" and
 * ideographic.
 *
 * This table contains all the characters that an element name can start with.
 * See XML 1.0 (2nd Edition) for more details.
 */
static char_info_t Letter[] = {{0x003A, 0x003A}, /* character ":" */
	{0x0041, 0x005A},
	{0x005F, 0x005F}, /* character "_" */
	{0x0061, 0x007A},
	{0x00C0, 0x00D6},
	{0x00D8, 0x00F6},
	{0x00F8, 0x00FF},
	{0x0100, 0x0131},
	{0x0134, 0x013E},
	{0x0141, 0x0148},
	{0x014A, 0x017E},
	{0x0180, 0x01C3},
	{0x01CD, 0x01F0},
	{0x01F4, 0x01F5},
	{0x01FA, 0x0217},
	{0x0250, 0x02A8},
	{0x02BB, 0x02C1},
	{0x0386, 0x0386},
	{0x0388, 0x038A},
	{0x038C, 0x038C},
	{0x038E, 0x03A1},
	{0x03A3, 0x03CE},
	{0x03D0, 0x03D6},
	{0x03DA, 0x03DA},
	{0x03DC, 0x03DC},
	{0x03DE, 0x03DE},
	{0x03E0, 0x03E0},
	{0x03E2, 0x03F3},
	{0x0401, 0x040C},
	{0x040E, 0x044F},
	{0x0451, 0x045C},
	{0x045E, 0x0481},
	{0x0490, 0x04C4},
	{0x04C7, 0x04C8},
	{0x04CB, 0x04CC},
	{0x04D0, 0x04EB},
	{0x04EE, 0x04F5},
	{0x04F8, 0x04F9},
	{0x0531, 0x0556},
	{0x0559, 0x0559},
	{0x0561, 0x0586},
	{0x05D0, 0x05EA},
	{0x05F0, 0x05F2},
	{0x0621, 0x063A},
	{0x0641, 0x064A},
	{0x0671, 0x06B7},
	{0x06BA, 0x06BE},
	{0x06C0, 0x06CE},
	{0x06D0, 0x06D3},
	{0x06D5, 0x06D5},
	{0x06E5, 0x06E6},
	{0x0905, 0x0939},
	{0x093D, 0x093D},
	{0x0958, 0x0961},
	{0x0985, 0x098C},
	{0x098F, 0x0990},
	{0x0993, 0x09A8},
	{0x09AA, 0x09B0},
	{0x09B2, 0x09B2},
	{0x09B6, 0x09B9},
	{0x09DC, 0x09DD},
	{0x09DF, 0x09E1},
	{0x09F0, 0x09F1},
	{0x0A05, 0x0A0A},
	{0x0A0F, 0x0A10},
	{0x0A13, 0x0A28},
	{0x0A2A, 0x0A30},
	{0x0A32, 0x0A33},
	{0x0A35, 0x0A36},
	{0x0A38, 0x0A39},
	{0x0A59, 0x0A5C},
	{0x0A5E, 0x0A5E},
	{0x0A72, 0x0A74},
	{0x0A85, 0x0A8B},
	{0x0A8D, 0x0A8D},
	{0x0A8F, 0x0A91},
	{0x0A93, 0x0AA8},
	{0x0AAA, 0x0AB0},
	{0x0AB2, 0x0AB3},
	{0x0AB5, 0x0AB9},
	{0x0ABD, 0x0ABD},
	{0x0AE0, 0x0AE0},
	{0x0B05, 0x0B0C},
	{0x0B0F, 0x0B10},
	{0x0B13, 0x0B28},
	{0x0B2A, 0x0B30},
	{0x0B32, 0x0B33},
	{0x0B36, 0x0B39},
	{0x0B3D, 0x0B3D},
	{0x0B5C, 0x0B5D},
	{0x0B5F, 0x0B61},
	{0x0B85, 0x0B8A},
	{0x0B8E, 0x0B90},
	{0x0B92, 0x0B95},
	{0x0B99, 0x0B9A},
	{0x0B9C, 0x0B9C},
	{0x0B9E, 0x0B9F},
	{0x0BA3, 0x0BA4},
	{0x0BA8, 0x0BAA},
	{0x0BAE, 0x0BB5},
	{0x0BB7, 0x0BB9},
	{0x0C05, 0x0C0C},
	{0x0C0E, 0x0C10},
	{0x0C12, 0x0C28},
	{0x0C2A, 0x0C33},
	{0x0C35, 0x0C39},
	{0x0C60, 0x0C61},
	{0x0C85, 0x0C8C},
	{0x0C8E, 0x0C90},
	{0x0C92, 0x0CA8},
	{0x0CAA, 0x0CB3},
	{0x0CB5, 0x0CB9},
	{0x0CDE, 0x0CDE},
	{0x0CE0, 0x0CE1},
	{0x0D05, 0x0D0C},
	{0x0D0E, 0x0D10},
	{0x0D12, 0x0D28},
	{0x0D2A, 0x0D39},
	{0x0D60, 0x0D61},
	{0x0E01, 0x0E2E},
	{0x0E30, 0x0E30},
	{0x0E32, 0x0E33},
	{0x0E40, 0x0E45},
	{0x0E81, 0x0E82},
	{0x0E84, 0x0E84},
	{0x0E87, 0x0E88},
	{0x0E8A, 0x0E8A},
	{0x0E8D, 0x0E8D},
	{0x0E94, 0x0E97},
	{0x0E99, 0x0E9F},
	{0x0EA1, 0x0EA3},
	{0x0EA5, 0x0EA5},
	{0x0EA7, 0x0EA7},
	{0x0EAA, 0x0EAB},
	{0x0EAD, 0x0EAE},
	{0x0EB0, 0x0EB0},
	{0x0EB2, 0x0EB3},
	{0x0EBD, 0x0EBD},
	{0x0EC0, 0x0EC4},
	{0x0F40, 0x0F47},
	{0x0F49, 0x0F69},
	{0x10A0, 0x10C5},
	{0x10D0, 0x10F6},
	{0x1100, 0x1100},
	{0x1102, 0x1103},
	{0x1105, 0x1107},
	{0x1109, 0x1109},
	{0x110B, 0x110C},
	{0x110E, 0x1112},
	{0x113C, 0x113C},
	{0x113E, 0x113E},
	{0x1140, 0x1140},
	{0x114C, 0x114C},
	{0x114E, 0x114E},
	{0x1150, 0x1150},
	{0x1154, 0x1155},
	{0x1159, 0x1159},
	{0x115F, 0x1161},
	{0x1163, 0x1163},
	{0x1165, 0x1165},
	{0x1167, 0x1167},
	{0x1169, 0x1169},
	{0x116D, 0x116E},
	{0x1172, 0x1173},
	{0x1175, 0x1175},
	{0x119E, 0x119E},
	{0x11A8, 0x11A8},
	{0x11AB, 0x11AB},
	{0x11AE, 0x11AF},
	{0x11B7, 0x11B8},
	{0x11BA, 0x11BA},
	{0x11BC, 0x11C2},
	{0x11EB, 0x11EB},
	{0x11F0, 0x11F0},
	{0x11F9, 0x11F9},
	{0x1E00, 0x1E9B},
	{0x1EA0, 0x1EF9},
	{0x1F00, 0x1F15},
	{0x1F18, 0x1F1D},
	{0x1F20, 0x1F45},
	{0x1F48, 0x1F4D},
	{0x1F50, 0x1F57},
	{0x1F59, 0x1F59},
	{0x1F5B, 0x1F5B},
	{0x1F5D, 0x1F5D},
	{0x1F5F, 0x1F7D},
	{0x1F80, 0x1FB4},
	{0x1FB6, 0x1FBC},
	{0x1FBE, 0x1FBE},
	{0x1FC2, 0x1FC4},
	{0x1FC6, 0x1FCC},
	{0x1FD0, 0x1FD3},
	{0x1FD6, 0x1FDB},
	{0x1FE0, 0x1FEC},
	{0x1FF2, 0x1FF4},
	{0x1FF6, 0x1FFC},
	{0x2126, 0x2126},
	{0x212A, 0x212B},
	{0x212E, 0x212E},
	{0x2180, 0x2182},
	{0x3007, 0x3007},
	{0x3021, 0x3029}, /* these two are ideographic */
	{0x3041, 0x3094},
	{0x30A1, 0x30FA},
	{0x3105, 0x312C},
	{0x4E00, 0x9FA5}, /* ideographic */
	{0xAC00, 0xD7A3}};

/*!
 * \brief The size of the letter table array.
 */
#define LETTERTABLESIZE (sizeof(Letter) / sizeof(Letter[0]))

/*!
 * \brief The NameChar table contains CombiningChar, Extender, Digit,
 * '-', '.', less '_', ':'
 *
 * NameChar ::= Digit | '-' | '.' | CombiningChar | Extender
 * See XML 1.0 2nd Edition
 */
static char_info_t NameChar[] = {
	{0x002D, 0x002D}, /* character "-" */
	{0x002E, 0x002E}, /* character "." */
	{0x0030, 0x0039}, /* digit */
	{0x00B7, 0x00B7},
	{0x02D0, 0x02D0},
	{0x02D1, 0x02D1}, /* extended */
	{0x0300, 0x0345},
	{0x0360, 0x0361},
	{0x0387, 0x0387}, /* extended */
	{0x0483, 0x0486},
	{0x0591, 0x05A1},
	{0x05A3, 0x05B9},
	{0x05BB, 0x05BD},
	{0x05BF, 0x05BF},
	{0x05C1, 0x05C2},
	{0x05C4, 0x05C4},
	{0x0640, 0x0640}, /* extended */
	{0x064B, 0x0652},
	{0x0660, 0x0669}, /* digit */
	{0x0670, 0x0670},
	{0x06D6, 0x06DC},
	{0x06DD, 0x06DF},
	{0x06E0, 0x06E4},
	{0x06E7, 0x06E8},
	{0x06EA, 0x06ED},
	{0x06F0, 0x06F9}, /* digit */
	{0x0901, 0x0903},
	{0x093C, 0x093C},
	{0x093E, 0x094C},
	{0x094D, 0x094D},
	{0x0951, 0x0954},
	{0x0962, 0x0963},
	{0x0966, 0x096F}, /* digit */
	{0x0981, 0x0983},
	{0x09BC, 0x09BC},
	{0x09BE, 0x09BE},
	{0x09BF, 0x09BF},
	{0x09C0, 0x09C4},
	{0x09C7, 0x09C8},
	{0x09CB, 0x09CD},
	{0x09D7, 0x09D7},
	{0x09E2, 0x09E3},
	{0x09E6, 0x09EF}, /* digit */
	{0x0A02, 0x0A02},
	{0x0A3C, 0x0A3C},
	{0x0A3E, 0x0A3E},
	{0x0A3F, 0x0A3F},
	{0x0A40, 0x0A42},
	{0x0A47, 0x0A48},
	{0x0A4B, 0x0A4D},
	{0x0A66, 0x0A6F}, /* digit */
	{0x0A70, 0x0A71},
	{0x0A81, 0x0A83},
	{0x0ABC, 0x0ABC},
	{0x0ABE, 0x0AC5},
	{0x0AC7, 0x0AC9},
	{0x0ACB, 0x0ACD},
	{0x0AE6, 0x0AEF}, /* digit */
	{0x0B01, 0x0B03},
	{0x0B3C, 0x0B3C},
	{0x0B3E, 0x0B43},
	{0x0B47, 0x0B48},
	{0x0B4B, 0x0B4D},
	{0x0B56, 0x0B57},
	{0x0B66, 0x0B6F}, /* digit */
	{0x0B82, 0x0B83},
	{0x0BBE, 0x0BC2},
	{0x0BC6, 0x0BC8},
	{0x0BCA, 0x0BCD},
	{0x0BD7, 0x0BD7},
	{0x0BE7, 0x0BEF}, /* digit */
	{0x0C01, 0x0C03},
	{0x0C3E, 0x0C44},
	{0x0C46, 0x0C48},
	{0x0C4A, 0x0C4D},
	{0x0C55, 0x0C56},
	{0x0C66, 0x0C6F}, /* digit */
	{0x0C82, 0x0C83},
	{0x0CBE, 0x0CC4},
	{0x0CC6, 0x0CC8},
	{0x0CCA, 0x0CCD},
	{0x0CD5, 0x0CD6},
	{0x0CE6, 0x0CEF}, /* digit */
	{0x0D02, 0x0D03},
	{0x0D3E, 0x0D43},
	{0x0D46, 0x0D48},
	{0x0D4A, 0x0D4D},
	{0x0D57, 0x0D57},
	{0x0D66, 0x0D6F}, /* digit */
	{0x0E31, 0x0E31},
	{0x0E34, 0x0E3A},
	{0x0E46, 0x0E46}, /* extended */
	{0x0E47, 0x0E4E},
	{0x0E50, 0x0E59}, /* digit */
	{0x0EB1, 0x0EB1},
	{0x0EB4, 0x0EB9},
	{0x0EBB, 0x0EBC},
	{0x0EC6, 0x0EC6}, /* extended */
	{0x0EC8, 0x0ECD},
	{0x0ED0, 0x0ED9}, /* digit */
	{0x0F18, 0x0F19},
	{0x0F20, 0x0F29}, /* digit */
	{0x0F35, 0x0F35},
	{0x0F37, 0x0F37},
	{0x0F39, 0x0F39},
	{0x0F3E, 0x0F3E},
	{0x0F3F, 0x0F3F},
	{0x0F71, 0x0F84},
	{0x0F86, 0x0F8B},
	{0x0F90, 0x0F95},
	{0x0F97, 0x0F97},
	{0x0F99, 0x0FAD},
	{0x0FB1, 0x0FB7},
	{0x0FB9, 0x0FB9},
	{0x20D0, 0x20DC},
	{0x20E1, 0x20E1},
	{0x3005, 0x3005}, /* extended */
	{0x302A, 0x302F},
	{0x3031, 0x3035}, /* extended */
	{0x3099, 0x3099},
	{0x309A, 0x309A}, /* combining char */
	{0x309D, 0x309E},
	{0x30FC, 0x30FE} /* extended */
};

/*!
 * \brief The name char table array size.
 */
#define NAMECHARTABLESIZE (sizeof(NameChar) / sizeof(NameChar[0]))

/*!
 * \brief Frees one ElementStack item.
 */
static void Parser_freeElementStackItem(
	/*! [in] The element stack item to free. */
	IXML_ElementStack *pItem)
{
	assert(pItem != NULL);
	if (pItem->element != NULL) {
		free(pItem->element);
		pItem->element = NULL;
	}
	if (pItem->namespaceUri != NULL) {
		free(pItem->namespaceUri);
		pItem->namespaceUri = NULL;
	}
	if (pItem->prefix != NULL) {
		free(pItem->prefix);
		pItem->prefix = NULL;
	}
}

/*!
 * \brief Frees namespaceURI item.
 */
static void Parser_freeNsURI(
	/*! [in] The name space URI item to free. */
	IXML_NamespaceURI *pNsURI)
{
	assert(pNsURI != NULL);
	if (pNsURI->nsURI != NULL) {
		free(pNsURI->nsURI);
	}
	if (pNsURI->prefix != NULL) {
		free(pNsURI->prefix);
	}
}

/*!
 * \brief Frees all temporary memory allocated by xmlparser.
 */
static void Parser_free(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	IXML_ElementStack *pElement;
	IXML_ElementStack *pNextElement;
	IXML_NamespaceURI *pNsURI;
	IXML_NamespaceURI *pNextNsURI;

	if (xmlParser == NULL) {
		return;
	}

	if (xmlParser->dataBuffer != NULL) {
		free(xmlParser->dataBuffer);
	}

	ixml_membuf_destroy(&(xmlParser->tokenBuf));
	ixml_membuf_destroy(&(xmlParser->lastElem));

	pElement = xmlParser->pCurElement;
	while (pElement != NULL) {
		Parser_freeElementStackItem(pElement);

		pNsURI = pElement->pNsURI;
		while (pNsURI != NULL) {
			pNextNsURI = pNsURI->nextNsURI;
			Parser_freeNsURI(pNsURI);
			free(pNsURI);
			pNsURI = pNextNsURI;
		}

		pNextElement = pElement->nextElement;
		free(pElement);
		pElement = pNextElement;
	}

	free(xmlParser);
}

/*!
 * \brief Skips document type declaration
 */
static int Parser_skipDocType(
	/*! [in,out] The pointer to the skipped point. */
	char **pstr)
{
	char *pCur = *pstr;
	/* default there is no nested < */
	char *pNext = NULL;
	int num = 1;

	assert((*pstr) != NULL);
	if (*pstr == NULL) {
		return IXML_FAILED;
	}

	while ((pCur != NULL) && (num != 0) && (*pCur != 0)) {
		if (*pCur == '<') {
			num++;
		} else if (*pCur == '>') {
			num--;
		} else if (*pCur == '"') {
			pNext = strchr(pCur + 1, '"');
			if (pNext == NULL) {
				return IXML_SYNTAX_ERR;
			}

			pCur = pNext;
		}

		pCur++;
	}

	if (num == 0) {
		*pstr = pCur;
		return IXML_SUCCESS;
	} else {
		return IXML_SYNTAX_ERR;
	}
}

/*!
 * \brief Skips all characters in the string until it finds the skip key.
 * Then it skips the skip key and returns.
 */
static int Parser_skipString(
	/*! [in,out] The pointer to the skipped point. */
	char **pstrSrc,
	/*! [in] The skip key. */
	const char *strSkipKey)
{
	if (!(*pstrSrc) || !strSkipKey) {
		return IXML_FAILED;
	}

	while ((**pstrSrc) &&
		strncmp(*pstrSrc, strSkipKey, strlen(strSkipKey)) != 0) {
		(*pstrSrc)++;
	}

	if (**pstrSrc == '\0') {
		return IXML_SYNTAX_ERR;
	}
	*pstrSrc = *pstrSrc + strlen(strSkipKey);

	return IXML_SUCCESS;
}

/*!
 * \brief Skip UTF-8 byte order mark
 */
static void Parser_skipBom(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	size_t bom_len = strlen(UTF8_BOM);

	if (strncmp(xmlParser->curPtr, UTF8_BOM, bom_len) == 0)
		xmlParser->curPtr += bom_len;
}

/*!
 * \brief Skip white spaces.
 */
static void Parser_skipWhiteSpaces(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	while ((*(xmlParser->curPtr) != 0) &&
		(strchr(WHITESPACE, (int)*(xmlParser->curPtr)) != NULL)) {
		xmlParser->curPtr++;
	}
}

/*!
 * \brief Skips XML declarations.
 */
static int Parser_skipXMLDecl(
	/*! [in,out] The XML parser. */
	Parser *xmlParser)
{
	int rc = IXML_FAILED;

	assert(xmlParser);
	if (xmlParser == NULL) {
		return rc;
	}

	rc = Parser_skipString(&(xmlParser->curPtr), END_PI);
	Parser_skipWhiteSpaces(xmlParser);
	return rc;
}

/*!
 * \brief Skips all characters in the string until it finds the skip key.
 * Then it skips the skip key and returns.
 */
static int Parser_skipComment(
	/*! [in,out] The pointer to the skipped point. */
	char **pstrSrc)
{
	char *pStrFound = NULL;

	assert((*pstrSrc) != NULL);
	if (*pstrSrc == NULL) {
		return IXML_FAILED;
	}

	pStrFound = strstr(*pstrSrc, END_COMMENT);
	if ((pStrFound != NULL) && (pStrFound != *pstrSrc) &&
		(*(pStrFound - 1) != '-')) {
		*pstrSrc = pStrFound + strlen(END_COMMENT);
	} else {
		return IXML_SYNTAX_ERR;
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Skip comment, PI and white space.
 */
static int Parser_skipMisc(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	int rc = IXML_SUCCESS;
	int done = 0;

	while ((done == 0) && (rc == IXML_SUCCESS)) {
		if (strncasecmp(xmlParser->curPtr,
			    (char *)BEGIN_COMMENT,
			    strlen(BEGIN_COMMENT)) == 0) {
			/* <!-- */
			rc = Parser_skipComment(&(xmlParser->curPtr));

		} else if (strncasecmp(xmlParser->curPtr,
				   (char *)XMLDECL,
				   strlen(XMLDECL)) == 0 ||
			   strncasecmp(xmlParser->curPtr,
				   (char *)XMLDECL2,
				   strlen(XMLDECL2)) == 0) {
			/* <?xml or <?xml? */
			rc = IXML_SYNTAX_ERR;
		} else if (strncasecmp(xmlParser->curPtr,
				   (char *)BEGIN_PI,
				   strlen(BEGIN_PI)) == 0) {
			/* <? */
			rc = Parser_skipString(&xmlParser->curPtr, END_PI);
		} else {
			done = 1;
		}
		Parser_skipWhiteSpaces(xmlParser);
	}

	return rc;
}

/*!
 * \brief Skip prolog.
 */
static int Parser_skipProlog(
	/*! [in,out] The XML parser. */
	Parser *xmlParser)
{
	int rc = IXML_SUCCESS;

	assert(xmlParser != NULL);
	if (xmlParser == NULL) {
		return IXML_FAILED;
	}

	Parser_skipBom(xmlParser);
	Parser_skipWhiteSpaces(xmlParser);

	if (strncmp(xmlParser->curPtr, (char *)XMLDECL, strlen(XMLDECL)) == 0) {
		/* <?xml */
		rc = Parser_skipXMLDecl(xmlParser);
		if (rc != IXML_SUCCESS) {
			return rc;
		}
	}

	rc = Parser_skipMisc(xmlParser);
	if ((rc == IXML_SUCCESS) && strncmp(xmlParser->curPtr,
					    (char *)BEGIN_DOCTYPE,
					    strlen(BEGIN_DOCTYPE)) == 0) {
		/* <! DOCTYPE */
		xmlParser->curPtr++;
		rc = Parser_skipDocType(&(xmlParser->curPtr));
	}

	if (rc == IXML_SUCCESS) {
		rc = Parser_skipMisc(xmlParser);
	}

	return rc;
}

/*!
 * \brief Set the last element to be the given string.
 */
static int Parser_setLastElem(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The string to copy from. */
	const char *s)
{
	int rc;

	if ((xmlParser == NULL) || (s == NULL)) {
		return IXML_FAILED;
	}

	rc = ixml_membuf_assign_str(&(xmlParser->lastElem), s);
	return rc;
}

/*!
 * \brief Clear token buffer.
 */
static void Parser_clearTokenBuf(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	ixml_membuf_destroy(&(xmlParser->tokenBuf));
}

/*!
 * \brief In UTF-8, characters are encoded using sequences of 1 to 6 octets.
 * This functions will return a UTF-8 character value and its octets number.
 *
 * \return The UTF-8 character converted to an int (32 bits).
 */
static int Parser_UTF8ToInt(
	/*! [in] The pointer to the character to encode. */
	const char *ss,
	/*! [out] The number of octets of the UTF-8 encoding of this character.
	 */
	ptrdiff_t *len)
{
	const unsigned char *s = (const unsigned char *)ss;
	int c = *s;

	if (c <= 127) {
		/* if c<=127, c is just the character. */
		*len = 1;
		return c;
	} else if ((c & 0xE0) == 0xC0 && (s[1] & 0xc0) == 0x80) {
		/* a sequence of 110xxxxx and 10xxxxxx? */
		*len = 2;
		return ((c & 0x1f) << 6) | (s[1] & 0x3f);
	} else if ((c & 0xF0) == 0xE0 && (s[1] & 0xc0) == 0x80 &&
		   (s[2] & 0xc0) == 0x80) {
		/* a sequence of 1110xxxx,10xxxxxx and 10xxxxxx ? */
		*len = 3;
		return ((c & 0x0f) << 12) | ((s[1] & 0x3f) << 6) |
		       (s[2] & 0x3f);
	} else if ((c & 0xf8) == 0xf0 && (s[1] & 0xc0) == 0x80 &&
		   (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) {
		/* a sequence of 11110xxx,10xxxxxx,10xxxxxx and 10xxxxxx ? */
		*len = 4;
		return ((c & 0x07) << 18) | ((s[1] & 0x3f) << 12) |
		       ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
	} else if ((c & 0xfc) == 0xf8 && (s[1] & 0xc0) == 0x80 &&
		   (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80 &&
		   (s[4] & 0xc0) == 0x80) {
		/* a sequence of 111110xx,10xxxxxx,10xxxxxx,10xxxxxx,10xxxxxx ?
		 */
		*len = 5;
		return ((c & 0x03) << 24) | ((s[1] & 0x3f) << 18) |
		       ((s[2] & 0x3f) << 12) | ((s[3] & 0x3f) << 6) |
		       (s[4] & 0x3f);
	} else if ((c & 0xfe) == 0xfc && (s[1] & 0xc0) == 0x80 &&
		   (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80 &&
		   (s[4] & 0xc0) == 0x80 && (s[5] & 0xc0) == 0x80) {
		/* a sequence of 1111110x,10xxxxxx,10xxxxxx,10xxxxxx,10xxxxxx
		 * and 10xxxxxx ? */
		*len = 6;
		return ((c & 0x01) << 30) | ((s[1] & 0x3f) << 24) |
		       ((s[2] & 0x3f) << 18) | ((s[3] & 0x3f) << 12) |
		       ((s[4] & 0x3f) << 6) | (s[5] & 0x3f);
	} else {
		/* none of above, error */
		int ret = 0;
		int line = __LINE__;
		if (g_error_char) {
			*len = 1;
			ret = g_error_char;
		} else {
			*len = 0;
			ret = -1;
		}
		IxmlPrintf(
			__FILE__, line, "Parser_UTF8ToInt", "Error %d\n", ret);
		return ret;
	}
}

/*!
 * \brief Will determine whether character c is in the table of tbl (either
 * Letter table or NameChar table).
 *
 * \return 1 or 0.
 */
static int Parser_isCharInTable(
	/*! [in] Character to check. */
	int c,
	/*! [in] Table to use. */
	char_info_t *tbl,
	/*! [in] Size of the table. */
	int sz)
{
	int t = 0;
	int b = sz - 1;
	int m;

	while (t <= b) {
		m = (t + b) / 2;
		if (c < tbl[m].l) {
			b = m - 1;
		} else if (c > tbl[m].h) {
			t = m + 1;
		} else {
			return 1;
		}
	}

	return 0;
}

/*!
 * \brief Check whether c (int) is in LetterTable or NameCharTable.
 */
static int Parser_isNameChar(
	/*! [in] The character to check. */
	int c,
	/*! [in] 1 if you also want to check in the NameChar table. */
	int bNameChar)
{
	if (Parser_isCharInTable(c, Letter, (int)LETTERTABLESIZE)) {
		return 1;
	}

	if (bNameChar &&
		Parser_isCharInTable(c, NameChar, (int)NAMECHARTABLESIZE)) {
		return 1;
	}

	return 0;
}

/*!
 * \brief see XML 1.0 (2nd Edition) 2.2.
 */
static int Parser_isXmlChar(
	/*! [in] The character to check. */
	int c)
{
	return c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xD7FF) ||
	       (c >= 0xE000 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x10FFFF);
}

/*!
 * \brief Returns next char value and its length.
 */
static int Parser_getChar(
	/*! [in] . */
	const char *src,
	/*! [in,out] . */
	ptrdiff_t *cLen)
{
	int ret = -1;
	int line = 0;
	const char *pnum;
	int sum;
	char c;
	int i;

	if (src == NULL || cLen == NULL) {
		line = __LINE__;
		ret = -1;
		goto ExitFunction;
	}

	*cLen = 0;
	if (*src != '&') {
		if (*src > 0 && Parser_isXmlChar((int)*src)) {
			*cLen = 1;
			ret = *src;
			goto ExitFunction;
		}

		i = Parser_UTF8ToInt(src, cLen);
		if (!Parser_isXmlChar(i)) {
			line = __LINE__;
			ret = g_error_char ? g_error_char : -1;
			goto ExitFunction;
		}

		line = __LINE__;
		ret = i;
		goto ExitFunction;
	} else if (strncasecmp(src, QUOT, strlen(QUOT)) == 0) {
		*cLen = (int)strlen(QUOT);
		ret = '"';
		goto ExitFunction;
	} else if (strncasecmp(src, LT, strlen(LT)) == 0) {
		*cLen = (int)strlen(LT);
		ret = '<';
		goto ExitFunction;
	} else if (strncasecmp(src, GT, strlen(GT)) == 0) {
		*cLen = (int)strlen(GT);
		ret = '>';
		goto ExitFunction;
	} else if (strncasecmp(src, APOS, strlen(APOS)) == 0) {
		*cLen = (int)strlen(APOS);
		ret = '\'';
		goto ExitFunction;
	} else if (strncasecmp(src, AMP, strlen(AMP)) == 0) {
		*cLen = (int)strlen(AMP);
		ret = '&';
		goto ExitFunction;
	} else if (strncasecmp(src, ESC_HEX, strlen(ESC_HEX)) == 0) {
		/* Read in escape characters of type &#xnn where nn is a
		 * hexadecimal value */
		pnum = src + strlen(ESC_HEX);
		sum = 0;
		while (strchr(HEX_NUMBERS, (int)*pnum) != 0) {
			c = *pnum;
			if (c <= '9') {
				sum = sum * 16 + (c - '0');
			} else if (c <= 'F') {
				sum = sum * 16 + (c - 'A' + 10);
			} else {
				sum = sum * 16 + (c - 'a' + 10);
			}
			pnum++;
		}
		if (pnum == src || *pnum != ';' || !Parser_isXmlChar(sum)) {
			line = __LINE__;
			goto fail_entity;
		}
		*cLen = pnum - src + 1;
		ret = sum;
		goto ExitFunction;
	} else if (strncasecmp(src, ESC_DEC, strlen(ESC_DEC)) == 0) {
		/* Read in escape characters of type &#nn where nn is a decimal
		 * value */
		pnum = src + strlen(ESC_DEC);
		sum = 0;
		while (strchr(DEC_NUMBERS, (int)*pnum) != 0) {
			sum = sum * 10 + (*pnum - '0');
			pnum++;
		}
		if ((pnum == src) || *pnum != ';' || !Parser_isXmlChar(sum)) {
			line = __LINE__;
			goto fail_entity;
		}
		*cLen = pnum - src + 1;
		ret = sum;
		goto ExitFunction;
	}

fail_entity:
	if (g_error_char) {
		*cLen = 1;
		ret = '&';
		goto ExitFunction;
	}
	ret = -1;

ExitFunction:
	if (ret == -1 || (g_error_char && ret == g_error_char)) {
		IxmlPrintf(__FILE__, line, "Parser_getChar", "Error %d\n", ret);
	}

	return ret;
}

/*!
 * \brief Appends c to token buffer.
 */
static int Parser_appendTokBufChar(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The character to append. */
	char c)
{
	int rc;

	rc = ixml_membuf_append(&(xmlParser->tokenBuf), &c);
	return rc;
}

/*!
 * \brief Encodes a character to its UTF-8 character string, and return its
 * length.
 *
 * \return The length of the encoded string in bytes.
 */
static int Parser_intToUTF8(
	/*! [in] The character to encode. */
	int c,
	/*! [out] The resultant UTF-8 encoded string. */
	utf8char s)
{
	if (c < 0)
		return 0;

	if (c <= 127) {
		s[0] = (char)c;
		s[1] = (char)0;
		return 1;
	} else if (c <= 0x07FF) {
		/* 0x0080 < c <= 0x07FF */
		s[0] = (char)(0xC0 | (c >> 6));
		s[1] = (char)(0x80 | (c & 0x3f));
		s[2] = (char)0;
		return 2;
	} else if (c <= 0xFFFF) {
		/* 0x0800 < c <= 0xFFFF */
		s[0] = (char)(0xE0 | (c >> 12));
		s[1] = (char)(0x80 | ((c >> 6) & 0x3f));
		s[2] = (char)(0x80 | (c & 0x3f));
		s[3] = (char)0;
		return 3;
	} else if (c <= 0x1FFFFF) {
		/* 0x10000 < c <= 0x1FFFFF */
		s[0] = (char)(0xF0 | (c >> 18));
		s[1] = (char)(0x80 | ((c >> 12) & 0x3f));
		s[2] = (char)(0x80 | ((c >> 6) & 0x3f));
		s[3] = (char)(0x80 | (c & 0x3f));
		s[4] = (char)0;
		return 4;
	} else if (c <= 0x3FFFFFF) {
		/* 0x200000 < c <= 3FFFFFF */
		s[0] = (char)(0xF8 | (c >> 24));
		s[1] = (char)(0x80 | ((c >> 18) & 0x3f));
		s[2] = (char)(0x80 | ((c >> 12) & 0x3f));
		s[3] = (char)(0x80 | ((c >> 6) & 0x3f));
		s[4] = (char)(0x80 | (c & 0x3f));
		s[5] = (char)0;
		return 5;
	} else if (c <= 0x7FFFFFFF) {
		/* 0x4000000 < c <= 7FFFFFFF */
		s[0] = (char)(0xFC | (c >> 30));
		s[1] = (char)(0x80 | ((c >> 24) & 0x3f));
		s[2] = (char)(0x80 | ((c >> 18) & 0x3f));
		s[3] = (char)(0x80 | ((c >> 12) & 0x3f));
		s[4] = (char)(0x80 | ((c >> 6) & 0x3f));
		s[5] = (char)(0x80 | (c & 0x3f));
		s[6] = (char)0;
		return 6;
	} else {
		/* illegal */
		return 0;
	}
}

/*!
 * \brief Appends string s to token buffer.
 */
static int Parser_appendTokBufStr(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The string to append. */
	const char *s)
{
	int rc = IXML_SUCCESS;

	if (s != NULL) {
		rc = ixml_membuf_append_str(&(xmlParser->tokenBuf), s);
	}

	return rc;
}

/*!
 * \brief Copy string in src into xml parser token buffer
 */
static int Parser_copyToken(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The string to copy from. */
	const char *src,
	/*! [in] The lenght to copy. */
	ptrdiff_t len)
{
	int ret = IXML_SUCCESS;
	int line = 0;
	int i;
	int c;
	ptrdiff_t cl;
	const char *psrc;
	const char *pend;
	utf8char uch;

	if (!src || len <= 0) {
		line = __LINE__;
		ret = IXML_FAILED;
		goto ExitFunction;
	}

	psrc = src;
	pend = src + len;

	while (psrc < pend) {
		c = Parser_getChar(psrc, &cl);
		if (c <= 0) {
			line = __LINE__;
			ret = IXML_FAILED;
			goto ExitFunction;
		}

		if (cl == 1) {
			Parser_appendTokBufChar(xmlParser, (char)c);
			psrc++;
		} else {
			i = Parser_intToUTF8(c, uch);
			if (i == 0) {
				line = __LINE__;
				ret = IXML_FAILED;
				goto ExitFunction;
			}
			Parser_appendTokBufStr(xmlParser, uch);
			psrc += cl;
		}
	}

	if (psrc > pend) {
		line = __LINE__;
		ret = IXML_FAILED;
		goto ExitFunction;
	}

ExitFunction:
	if (ret != IXML_SUCCESS) {
		IxmlPrintf(
			__FILE__, line, "Parser_copyToken", "Error %d\n", ret);
	}

	return ret;
}

/*!
 * \brief Return the length of next token in tokenBuff.
 */
static ptrdiff_t Parser_getNextToken(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	ptrdiff_t tokenLength = 0;
	int temp;
	ptrdiff_t tlen;
	int rc;

	Parser_clearTokenBuf(xmlParser);

	if (*(xmlParser->curPtr) == '\0') {
		return 0;
	}
	/* skip XML instructions */
	rc = Parser_skipMisc(xmlParser);
	if (rc != IXML_SUCCESS) {
		return 0;
	}
	/* Attribute value logic must come first, since all text untokenized
	 * until end-quote */
	if (*(xmlParser->curPtr) == QUOTE) {
		tokenLength = 1;
	} else if (*(xmlParser->curPtr) == SINGLEQUOTE) {
		tokenLength = 1;
	} else if (*(xmlParser->curPtr) == LESSTHAN) {
		/* Check for start tags */
		temp = Parser_UTF8ToInt(xmlParser->curPtr + 1, &tlen);
		if (temp == '/') {
			/* token is '</' end tag */
			tokenLength = 2;
		} else if (Parser_isNameChar(temp, 0)) {
			/* '<' found, so return '<' token */
			tokenLength = 1;
		} else {
			/* error */
			return 0;
		}
	} else if (*(xmlParser->curPtr) == EQUALS) {
		/* Check for '=' token, return it as a token */
		tokenLength = 1;
	} else if (*(xmlParser->curPtr) == SLASH) {
		if (*(xmlParser->curPtr + 1) == GREATERTHAN) {
			/* token '/>' found */
			tokenLength = 2;
			/* fix */
			xmlParser->savePtr = xmlParser->curPtr;
		}
	} else if (*(xmlParser->curPtr) == GREATERTHAN) {
		/* > found, so return it as a token */
		tokenLength = 1;
	} else if (Parser_isNameChar(
			   Parser_UTF8ToInt(xmlParser->curPtr, &tlen), 0)) {
		/* Check for name tokens, name found, so find out how long it is
		 */
		ptrdiff_t iIndex = tlen;

		while (Parser_isNameChar(
			Parser_UTF8ToInt(xmlParser->curPtr + iIndex, &tlen),
			1)) {
			iIndex += tlen;
		}
		tokenLength = iIndex;
	} else {
		return 0;
	}

	/* Copy the token to the return string */
	if (Parser_copyToken(xmlParser, xmlParser->curPtr, tokenLength) !=
		IXML_SUCCESS) {
		return 0;
	}

	xmlParser->curPtr += tokenLength;
	return tokenLength;
}

/*!
 * \brief Version of strdup() that handles NULL input.
 *
 * \return The same as strdup().
 */
static char *safe_strdup(
	/*! [in] String to be duplicated. */
	const char *s)
{
	assert(s != NULL);

	if (s == NULL) {
		return strdup((const char *)"");
	}
	return strdup(s);
}

/*!
 * \brief Processes the STag as defined by XML spec.
 */
static int Parser_processSTag(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *node)
{
	char *pCurToken = NULL;
	int rc;

	if (Parser_getNextToken(xmlParser) == 0) {
		return IXML_SYNTAX_ERR;
	}

	pCurToken = (xmlParser->tokenBuf).buf;
	if (pCurToken != NULL) {
		node->nodeName = safe_strdup(pCurToken);
		if (node->nodeName == NULL) {
			return IXML_INSUFFICIENT_MEMORY;
		}
	} else {
		return IXML_SYNTAX_ERR;
	}

	rc = Parser_setLastElem(xmlParser, node->nodeName);
	if (rc != IXML_SUCCESS) {
		/* no need to free node->nodeName, main loop will free it */
		return IXML_FAILED;
	}

	rc = Parser_setNodePrefixAndLocalName(node);
	if (rc != IXML_SUCCESS) {
		/* no need to free node->nodeName, main loop will free it */
		return IXML_FAILED;
	}

	node->nodeValue = NULL;
	node->nodeType = eELEMENT_NODE;

	xmlParser->savePtr = xmlParser->curPtr;
	if (Parser_getNextToken(xmlParser) == 0) {
		/* no need to free node->nodeName, main loop will free it */
		return IXML_SYNTAX_ERR;
	}

	pCurToken = (xmlParser->tokenBuf).buf;
	/* check to see what is the next token */
	if (strcmp(pCurToken, "/>") == 0) {
		/* empty element */
		xmlParser->state = eELEMENT;
		/* backup to /> */
		xmlParser->curPtr = xmlParser->savePtr;
	} else if (strcmp(pCurToken, ">") == 0) {
		/* expecting text node */
		xmlParser->state = eCONTENT;
	} else {
		xmlParser->state = eATTRIBUTE;
		xmlParser->curPtr = xmlParser->savePtr;
	}

	return IXML_SUCCESS;
}

/*!
 * \brief
 */
static int Parser_skipPI(
	/*! [in,out] The pointer to the skipped point. */
	char **pSrc)
{
	char *pEnd = NULL;

	assert(*pSrc);
	if (*pSrc == NULL) {
		return IXML_FAILED;
	}

	if ((strncasecmp(*pSrc, (char *)XMLDECL, strlen(XMLDECL)) == 0) ||
		(strncasecmp(*pSrc, (char *)XMLDECL2, strlen(XMLDECL2)) == 0)) {
		/* not allowed */
		return IXML_SYNTAX_ERR;
	}

	if (strncasecmp(*pSrc, (char *)BEGIN_PI, strlen(BEGIN_PI)) == 0) {
		pEnd = strstr(*pSrc, END_PI);
		if ((pEnd != NULL) && (pEnd != *pSrc)) {
			*pSrc = pEnd + strlen(BEGIN_PI);
		} else {
			return IXML_SYNTAX_ERR;
		}
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Processes CDSection as defined by XML spec.
 *
 * \return
 */
static int Parser_processCDSect(
	/*! [in] . */
	char **pSrc,
	/*! [in] The Node to process. */
	IXML_Node *node)
{
	char *pEnd;
	size_t tokenLength = (size_t)0;
	char *pCDataStart;

	if (*pSrc == NULL) {
		return IXML_FAILED;
	}

	pCDataStart = *pSrc + strlen(CDSTART);
	pEnd = pCDataStart;
	while ((Parser_isXmlChar((int)*pEnd)) && (*pEnd != '\0')) {
		if (strncmp(pEnd, CDEND, strlen(CDEND)) == 0) {
			break;
		} else {
			pEnd++;
		}
	}

	if ((pEnd - pCDataStart > 0) && (*pEnd != '\0')) {
		tokenLength = (size_t)pEnd - (size_t)pCDataStart;
		node->nodeValue = (char *)malloc(tokenLength + (size_t)1);
		if (node->nodeValue == NULL) {
			return IXML_INSUFFICIENT_MEMORY;
		}
		strncpy(node->nodeValue, pCDataStart, tokenLength);
		node->nodeValue[tokenLength] = '\0';

		node->nodeName = safe_strdup(CDATANODENAME);
		if (node->nodeName == NULL) {
			/* no need to free node->nodeValue at all, bacause node
			 * contents will be freed by the main loop. */
			return IXML_INSUFFICIENT_MEMORY;
		}

		node->nodeType = eCDATA_SECTION_NODE;
		*pSrc = pEnd + strlen(CDEND);
		return IXML_SUCCESS;
	} else {
		return IXML_SYNTAX_ERR;
	}
}

/*!
 * \brief Processes the CONTENT as defined in XML spec.
 */
static int Parser_processContent(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *node)
{
	int ret = IXML_SUCCESS;
	int line = 0;
	char *pEndContent;
	ptrdiff_t tokenLength;
	const char *notAllowed = "]]>";
	char *pCurToken = NULL;

	/* save pointer for backup */
	xmlParser->savePtr = xmlParser->curPtr;
	Parser_skipWhiteSpaces(xmlParser);

	if (*(xmlParser->curPtr) == '\0') {
		/* end of file is reached */
		ret = IXML_SUCCESS;
		goto ExitFunction;
	}

	pEndContent = xmlParser->curPtr;
	if (*pEndContent == LESSTHAN) {
		if (strncmp(pEndContent, (char *)CDSTART, strlen(CDSTART)) ==
			0) {
			if (Parser_processCDSect(&pEndContent, node) !=
				IXML_SUCCESS) {
				line = __LINE__;
				ret = IXML_SYNTAX_ERR;
				goto ExitFunction;
			} else {
				xmlParser->curPtr = pEndContent;
			}
		} else if (strncmp(pEndContent,
				   (char *)BEGIN_COMMENT,
				   strlen(BEGIN_COMMENT)) == 0) {
			if (Parser_skipComment(&pEndContent) != IXML_SUCCESS) {
				line = __LINE__;
				ret = IXML_SYNTAX_ERR;
				goto ExitFunction;
			} else {
				xmlParser->curPtr = pEndContent;
			}
		} else if (strncmp(pEndContent,
				   (char *)BEGIN_PI,
				   strlen(BEGIN_PI)) == 0) {
			if (Parser_skipPI(&pEndContent) != IXML_SUCCESS) {
				line = __LINE__;
				ret = IXML_SYNTAX_ERR;
				goto ExitFunction;
			} else {
				xmlParser->curPtr = pEndContent;
			}
		} else {
			/* empty content */
			xmlParser->state = eELEMENT;
		}
	} else {
		/* backup */
		xmlParser->curPtr = xmlParser->savePtr;
		pEndContent = xmlParser->curPtr;

		while ((*pEndContent != LESSTHAN) &&
			(strncmp(pEndContent,
				 (const char *)notAllowed,
				 strlen(notAllowed)) != 0) &&
			*pEndContent) {
			pEndContent++;
		}

		if (strncmp(pEndContent,
			    (const char *)notAllowed,
			    strlen(notAllowed)) == 0) {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}

		tokenLength = pEndContent - xmlParser->curPtr;
		Parser_clearTokenBuf(xmlParser);

		if (Parser_copyToken(xmlParser,
			    xmlParser->curPtr,
			    tokenLength) != IXML_SUCCESS) {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}

		pCurToken = (xmlParser->tokenBuf).buf;
		if (pCurToken != NULL) {
			node->nodeValue = safe_strdup(pCurToken);
			if (node->nodeValue == NULL) {
				line = __LINE__;
				ret = IXML_INSUFFICIENT_MEMORY;
				goto ExitFunction;
			}
		} else {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}

		node->nodeName = safe_strdup(TEXTNODENAME);
		if (node->nodeName == NULL) {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}
		node->nodeType = eTEXT_NODE;

		/* adjust curPtr */
		xmlParser->curPtr += tokenLength;
	}

ExitFunction:
	if (ret != IXML_SUCCESS) {
		IxmlPrintf(__FILE__,
			line,
			"Parser_processContent",
			"Error %d\n",
			ret);
	}

	return ret;
}

/*!
 * \brief Process ETag as defined by XML spec.
 */
static int Parser_processETag(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *node,
	/*! [out] . */
	int *bETag)
{
	int ret = IXML_SUCCESS;
	int line = 0;
	char *pCurToken = NULL;

	assert(xmlParser != NULL);
	if (Parser_getNextToken(xmlParser) == 0) {
		line = __LINE__;
		ret = IXML_SYNTAX_ERR;
		goto ExitFunction;
	}

	pCurToken = (xmlParser->tokenBuf).buf;
	if (pCurToken == NULL) {
		line = __LINE__;
		ret = IXML_SYNTAX_ERR;
		goto ExitFunction;
	}
	node->nodeName = safe_strdup(pCurToken);
	if (node->nodeName == NULL) {
		line = __LINE__;
		ret = IXML_INSUFFICIENT_MEMORY;
		goto ExitFunction;
	}

	node->nodeValue = NULL;
	node->nodeType = eELEMENT_NODE;

	Parser_skipWhiteSpaces(xmlParser);

	/* read the >  */
	if (Parser_getNextToken(xmlParser) == 0) {
		line = __LINE__;
		ret = IXML_SYNTAX_ERR;
		goto ExitFunction;
	}

	pCurToken = (xmlParser->tokenBuf).buf;
	if (pCurToken == NULL) {
		/* no need to free node->nodeName, it is freed by main loop */
		line = __LINE__;
		ret = IXML_SYNTAX_ERR;
		goto ExitFunction;
	}

	if (strcmp(pCurToken, ">") != 0) {
		line = __LINE__;
		ret = IXML_SYNTAX_ERR;
		goto ExitFunction;
	}

	*bETag = 1;

ExitFunction:
	if (ret != IXML_SUCCESS) {
		IxmlPrintf(__FILE__,
			line,
			"Parser_processETag",
			"Error %d\n",
			ret);
	}

	return ret;
}

/*!
 * \brief Unimplemented function.
 *
 * \return IXML_SUCCESS.
 */
#if 0
static int Parser_parseReference(
	/*! [in] Currently unused. */
	char *pStr)
{
	/* place holder for future implementation */
	return IXML_SUCCESS;
	pStr = pStr;
}
#endif

/*!
 * \brief Return the namespce as defined as prefix.
 */
static char *Parser_getNameSpace(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The prefix. */
	const char *prefix)
{
	IXML_ElementStack *pCur;
	IXML_NamespaceURI *pNsUri;

	pCur = xmlParser->pCurElement;
	if (strcmp(pCur->prefix, prefix) != 0) {
		pNsUri = pCur->pNsURI;
		while (pNsUri != NULL) {
			if (strcmp(pNsUri->prefix, prefix) == 0) {
				return pNsUri->nsURI;
			}
			pNsUri = pNsUri->nextNsURI;
		}
	} else {
		return pCur->namespaceUri;
	}

	return NULL;
}

/*!
 * \brief Add a namespace definition.
 */
static int Parser_addNamespace(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	IXML_Node *pNode;
	IXML_ElementStack *pCur;
	const char *namespaceUri;

	pNode = xmlParser->pNeedPrefixNode;
	pCur = xmlParser->pCurElement;
	if (!pNode->prefix) {
		/* element does not have prefix */
		if (strcmp(pNode->nodeName, pCur->element) != 0)
			return IXML_FAILED;
		if (pCur->namespaceUri) {
			/* it would be wrong that pNode->namespace != NULL. */
			assert(pNode->namespaceURI == NULL);
			pNode->namespaceURI = safe_strdup(pCur->namespaceUri);
			if (!pNode->namespaceURI)
				return IXML_INSUFFICIENT_MEMORY;
		}
		xmlParser->pNeedPrefixNode = NULL;
	} else {
		if (!pCur->prefix ||
			((strcmp(pNode->nodeName, pCur->element) != 0) &&
				(strcmp(pNode->prefix, pCur->prefix) != 0)))
			return IXML_FAILED;
		namespaceUri = Parser_getNameSpace(xmlParser, pCur->prefix);
		if (namespaceUri) {
			pNode->namespaceURI = safe_strdup(namespaceUri);
			if (!pNode->namespaceURI)
				return IXML_INSUFFICIENT_MEMORY;
			xmlParser->pNeedPrefixNode = NULL;
		}
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Add namespace definition.
 */
static int Parser_xmlNamespace(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *newNode)
{
	IXML_ElementStack *pCur = xmlParser->pCurElement;
	IXML_NamespaceURI *pNewNs = NULL;
	IXML_NamespaceURI *pNs = NULL;
	IXML_NamespaceURI *pPrevNs = NULL;
	int ret = IXML_SUCCESS;
	int line = 0;

	/* if the newNode contains a namespace definition */
	assert(newNode->nodeName != NULL);

	if (strcmp(newNode->nodeName, "xmlns") == 0) {
		/* default namespace def. */
		if (pCur->namespaceUri != NULL) {
			free(pCur->namespaceUri);
		}
		pCur->namespaceUri = safe_strdup(newNode->nodeValue);
		if (pCur->namespaceUri == NULL) {
			ret = IXML_INSUFFICIENT_MEMORY;
			line = __LINE__;
			goto ExitFunction;
		}
	} else if (strncmp(newNode->nodeName, "xmlns:", strlen("xmlns:")) ==
		   0) {
		/* namespace definition */
		ret = Parser_setNodePrefixAndLocalName(newNode);
		if (ret != IXML_SUCCESS) {
			line = __LINE__;
			goto ExitFunction;
		}

		assert(newNode->localName != NULL);

		if (pCur == NULL) {
			ret = IXML_FAILED;
			line = __LINE__;
			goto ExitFunction;
		}
		if (pCur->prefix != NULL &&
			strcmp(pCur->prefix, newNode->localName) == 0) {
			if (pCur->namespaceUri != NULL) {
				free(pCur->namespaceUri);
			}
			pCur->namespaceUri = safe_strdup(newNode->nodeValue);
			if (pCur->namespaceUri == NULL) {
				ret = IXML_INSUFFICIENT_MEMORY;
				line = __LINE__;
				goto ExitFunction;
			}
		} else {
			pPrevNs = pCur->pNsURI;
			pNs = pPrevNs;
			while (pNs != NULL) {
				if (pNs->prefix != NULL &&
					strcmp(pNs->prefix,
						newNode->localName) == 0) {
					/* replace namespace definition */
					break;
				} else {
					pPrevNs = pNs;
					pNs = pNs->nextNsURI;
				}
			}
			if (pNs == NULL) {
				/* a new definition */
				pNewNs = (IXML_NamespaceURI *)malloc(
					sizeof(IXML_NamespaceURI));
				if (pNewNs == NULL) {
					ret = IXML_INSUFFICIENT_MEMORY;
					line = __LINE__;
					goto ExitFunction;
				}
				memset(pNewNs, 0, sizeof(IXML_NamespaceURI));
				pNewNs->prefix =
					safe_strdup(newNode->localName);
				if (pNewNs->prefix == NULL) {
					free(pNewNs);
					ret = IXML_INSUFFICIENT_MEMORY;
					line = __LINE__;
					goto ExitFunction;
				}
				pNewNs->nsURI = safe_strdup(newNode->nodeValue);
				if (pNewNs->nsURI == NULL) {
					Parser_freeNsURI(pNewNs);
					free(pNewNs);
					ret = IXML_INSUFFICIENT_MEMORY;
					line = __LINE__;
					goto ExitFunction;
				}
				if (pCur->pNsURI == NULL) {
					pCur->pNsURI = pNewNs;
				} else {
					pPrevNs->nextNsURI = pNewNs;
				}
			} else {
				/* udpate the namespace */
				if (pNs->nsURI != NULL) {
					free(pNs->nsURI);
				}
				pNs->nsURI = safe_strdup(newNode->nodeValue);
				if (pNs->nsURI == NULL) {
					ret = IXML_INSUFFICIENT_MEMORY;
					line = __LINE__;
					goto ExitFunction;
				}
			}
		}
	}
	if (xmlParser->pNeedPrefixNode != NULL) {
		ret = Parser_addNamespace(xmlParser);
		line = __LINE__;
		goto ExitFunction;
	}

ExitFunction:
	if (ret != IXML_SUCCESS && ret != IXML_FILE_DONE) {
		IxmlPrintf(__FILE__,
			line,
			"Parser_xmlNamespace",
			"Error %d\n",
			ret);
	}

	return ret;
}

/*!
 * \brief Processes attribute.
 *
 * \return IXML_SUCCESS or failure code.
 */
static int Parser_processAttribute(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *node)
{
	int ret = IXML_SUCCESS;
	int line = 0;
	ptrdiff_t tlen = 0;
	char *strEndQuote = NULL;
	char *pCur = NULL;
	char *pCurToken = NULL;

	assert(xmlParser);

	if (xmlParser == NULL) {
		ret = IXML_FAILED;
		line = __LINE__;
		goto ExitFunction;
	}
	pCurToken = xmlParser->tokenBuf.buf;
	if (pCurToken == NULL) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	if (Parser_isNameChar(Parser_UTF8ToInt(pCurToken, &tlen), 0) == 0) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	/* copy in the attribute name */
	node->nodeName = safe_strdup(pCurToken);
	if (node->nodeName == NULL) {
		ret = IXML_INSUFFICIENT_MEMORY;
		line = __LINE__;
		goto ExitFunction;
	}
	/* read in the "=" sign */
	if (Parser_getNextToken(xmlParser) == 0) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}

	pCurToken = xmlParser->tokenBuf.buf;
	if (*pCurToken != EQUALS) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	/* read in the single quote or double quote */
	if (Parser_getNextToken(xmlParser) == 0) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	/* pCurToken is either quote or single quote */
	pCurToken = (xmlParser->tokenBuf).buf;
	if (*pCurToken != QUOTE && *pCurToken != SINGLEQUOTE) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	strEndQuote = strstr(xmlParser->curPtr, pCurToken);
	if (strEndQuote == NULL) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}
	/* check between curPtr and strEndQuote,
	 * whether there are illegal chars. */
	pCur = xmlParser->curPtr;
	while (pCur < strEndQuote) {
		if (*pCur == '<') {
			ret = IXML_SYNTAX_ERR;
			line = __LINE__;
			goto ExitFunction;
		}
		/*if (*pCur == '&') {
			Parser_parseReference(++pCur);
		}*/
		pCur++;
	}
	/* clear token buffer */
	Parser_clearTokenBuf(xmlParser);
	if (strEndQuote != xmlParser->curPtr) {
		ret = Parser_copyToken(xmlParser,
			xmlParser->curPtr,
			strEndQuote - xmlParser->curPtr);
		if (ret != IXML_SUCCESS) {
			ret = IXML_SYNTAX_ERR;
			line = __LINE__;
			goto ExitFunction;
		}
	}
	/* skip the ending quote */
	xmlParser->curPtr = strEndQuote + 1;
	pCurToken = xmlParser->tokenBuf.buf;
	if (pCurToken != NULL) {
		/* attribute has value, like a="c" */
		node->nodeValue = safe_strdup(pCurToken);
		if (node->nodeValue == NULL) {
			ret = IXML_INSUFFICIENT_MEMORY;
			line = __LINE__;
			goto ExitFunction;
		}
	} else {
		/* if attribute doesn't have value, like a=""
		 * somewhere on other places is this copied */
		node->nodeValue = malloc(sizeof(char));
		*(node->nodeValue) = '\0';
	}
	node->nodeType = eATTRIBUTE_NODE;

	/* check whether this is a new namespace definition */
	ret = Parser_xmlNamespace(xmlParser, node);
	if (ret != IXML_SUCCESS) {
		line = __LINE__;
		goto ExitFunction;
	}
	/* read ahead to see whether we have more attributes */
	xmlParser->savePtr = xmlParser->curPtr;
	if (Parser_getNextToken(xmlParser) == 0) {
		ret = IXML_SYNTAX_ERR;
		line = __LINE__;
		goto ExitFunction;
	}

	pCurToken = xmlParser->tokenBuf.buf;
	if (strcmp(pCurToken, "<") == 0) {
		ret = IXML_FAILED;
		line = __LINE__;
		goto ExitFunction;
	} else if (strcmp(pCurToken, ">") != 0) {
		/* more attribute? */
		/* backup */
		xmlParser->curPtr = xmlParser->savePtr;
	} else {
		xmlParser->state = eCONTENT;
	}

ExitFunction:
	if (ret != IXML_SUCCESS && ret != IXML_FILE_DONE) {
		IxmlPrintf(__FILE__,
			line,
			"Parser_processAttribute",
			"Error %d\n",
			ret);
	}

	return ret;
}

/*!
 * \brief Get the next node.
 *
 * \return IXML_SUCCESS and the next node or IXML_FILE_DONE or an error.
 */
static int Parser_getNextNode(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [out] The XML parser. */
	IXML_Node *node,
	/*! [out] The XML parser. */
	int *bETag)
{
	char *pCurToken = NULL;
	char *lastElement = NULL;
	int ret = IXML_SUCCESS;
	int line = 0;
	ptrdiff_t tokenLen = 0;

	/* endof file reached? */
	if (*(xmlParser->curPtr) == '\0') {
		*bETag = 1;
		line = __LINE__;
		ret = IXML_FILE_DONE;
		goto ExitFunction;
	}

	switch (xmlParser->state) {
	case eCONTENT:
		line = __LINE__;
		ret = Parser_processContent(xmlParser, node);
		goto ExitFunction;
	default:
		Parser_skipWhiteSpaces(xmlParser);
		tokenLen = Parser_getNextToken(xmlParser);
		if (tokenLen == 0 && xmlParser->pCurElement == NULL &&
			*(xmlParser->curPtr) == '\0') {
			/* comments after the xml doc */
			line = __LINE__;
			ret = IXML_SUCCESS;
			goto ExitFunction;
		} else if ((xmlParser->tokenBuf).length == (size_t)0) {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}

		pCurToken = (xmlParser->tokenBuf).buf;
		if (*pCurToken == GREATERTHAN) {
			line = __LINE__;
			ret = IXML_SUCCESS;
			goto ExitFunction;
		} else if (strcmp(pCurToken, ENDTAG) == 0) {
			/* we got </, read next element */
			line = __LINE__;
			ret = Parser_processETag(xmlParser, node, bETag);
			goto ExitFunction;
		} else if (*pCurToken == LESSTHAN) {
			line = __LINE__;
			ret = Parser_processSTag(xmlParser, node);
			goto ExitFunction;
		} else if (strcmp(pCurToken, COMPLETETAG) == 0) {
			lastElement = (xmlParser->lastElem).buf;
			if (lastElement == NULL) {
				line = __LINE__;
				ret = IXML_SYNTAX_ERR;
				goto ExitFunction;
			}

			node->nodeName = safe_strdup(lastElement);
			if (node->nodeName == NULL) {
				line = __LINE__;
				ret = IXML_INSUFFICIENT_MEMORY;
				goto ExitFunction;
			}
			node->nodeType = eELEMENT_NODE;
			*bETag = 1;

			line = __LINE__;
			ret = IXML_SUCCESS;
			goto ExitFunction;
		} else if (xmlParser->pCurElement != NULL) {
			switch (xmlParser->state) {
			case eATTRIBUTE:
				if (Parser_processAttribute(xmlParser, node) !=
					IXML_SUCCESS) {
					line = __LINE__;
					ret = IXML_SYNTAX_ERR;
					goto ExitFunction;
				}
				break;
			default:
				line = __LINE__;
				ret = IXML_SYNTAX_ERR;
				goto ExitFunction;
			}
		} else {
			line = __LINE__;
			ret = IXML_SYNTAX_ERR;
			goto ExitFunction;
		}
	}

ExitFunction:
	if (ret != IXML_SUCCESS && ret != IXML_FILE_DONE) {
		IxmlPrintf(__FILE__,
			line,
			"Parser_getNextNode",
			"Error %d\n",
			ret);
	}

	return ret;
}

/*!
 * \brief Decides whether element's prefix is already defined.
 */
static int Parser_ElementPrefixDefined(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *newNode,
	/*! [in,out] The name space URI. */
	char **nsURI)
{
	IXML_ElementStack *pCur = xmlParser->pCurElement;
	IXML_NamespaceURI *pNsUri;

	while (pCur != NULL) {
		if ((pCur->prefix != NULL) &&
			(strcmp(pCur->prefix, newNode->prefix) == 0)) {
			*nsURI = pCur->namespaceUri;
			return 1;
		} else {
			pNsUri = pCur->pNsURI;

			while (pNsUri != NULL) {
				if (strcmp(pNsUri->prefix, newNode->prefix) ==
					0) {
					*nsURI = pNsUri->nsURI;
					return 1;
				} else {
					pNsUri = pNsUri->nextNsURI;
				}
			}
		}

		pCur = pCur->nextElement;
	}

	return 0;
}

/*!
 * \brief Set element's namespace.
 */
static int Parser_setElementNamespace(
	/*! [in] The Element Node to process. */
	IXML_Element *newElement,
	/*! [in] The name space string. */
	const char *nsURI)
{
	if (newElement != NULL) {
		if (newElement->n.namespaceURI != NULL) {
			return IXML_SYNTAX_ERR;
		} else {
			(newElement->n).namespaceURI = safe_strdup(nsURI);
			if ((newElement->n).namespaceURI == NULL) {
				return IXML_INSUFFICIENT_MEMORY;
			}
		}
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Reports whether the new attribute is the same as an existing one.
 *
 * \return 1 if the new attribute is the same as an existing one.
 */
static int isDuplicateAttribute(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The node attribute to compare. */
	IXML_Node *newAttrNode)
{
	IXML_Node *elementNode = NULL;
	IXML_Node *attrNode = NULL;

	elementNode = xmlParser->currentNodePtr;
	attrNode = elementNode->firstAttr;
	while (attrNode != NULL) {
		if (strcmp(attrNode->nodeName, newAttrNode->nodeName) == 0) {
			return 1;
		}

		attrNode = attrNode->nextSibling;
	}

	return 0;
}

/*!
 * \brief Processes the attribute name.
 *
 * \return IXML_SUCCESS if successful, otherwise or an error code.
 */
static int Parser_processAttributeName(
	/*! [in] The XML document. */
	IXML_Document *rootDoc,
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *newNode)
{
	IXML_Attr *attr = NULL;
	int rc = IXML_SUCCESS;

	if (isDuplicateAttribute(xmlParser, newNode)) {
		return IXML_SYNTAX_ERR;
	}

	rc = ixmlDocument_createAttributeEx(rootDoc, newNode->nodeName, &attr);
	if (rc != IXML_SUCCESS) {
		return rc;
	}

	rc = ixmlNode_setNodeProperties((IXML_Node *)attr, newNode);
	if (rc != IXML_SUCCESS) {
		ixmlAttr_free(attr);
		return rc;
	}

	rc = ixmlElement_setAttributeNode(
		(IXML_Element *)xmlParser->currentNodePtr, attr, NULL);
	if (rc != IXML_SUCCESS) {
		ixmlAttr_free(attr);
	}
	return rc;
}

/*!
 * \brief Push a new element onto element stack.
 *
 * \return
 */
static int Parser_pushElement(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The element node to push. */
	IXML_Node *newElement)
{
	IXML_ElementStack *pCurElement = NULL;
	IXML_ElementStack *pNewStackElement = NULL;

	assert(newElement);
	if (newElement != NULL) {
		/* push new element */
		pNewStackElement =
			(IXML_ElementStack *)malloc(sizeof(IXML_ElementStack));
		if (pNewStackElement == NULL) {
			return IXML_INSUFFICIENT_MEMORY;
		}

		memset(pNewStackElement, 0, sizeof(IXML_ElementStack));
		/* the element member includes both prefix and name */

		pNewStackElement->element = safe_strdup(newElement->nodeName);
		if (pNewStackElement->element == NULL) {
			free(pNewStackElement);
			return IXML_INSUFFICIENT_MEMORY;
		}

		if (newElement->prefix != 0) {
			pNewStackElement->prefix =
				safe_strdup(newElement->prefix);
			if (pNewStackElement->prefix == NULL) {
				Parser_freeElementStackItem(pNewStackElement);
				free(pNewStackElement);
				return IXML_INSUFFICIENT_MEMORY;
			}
		}

		if (newElement->namespaceURI != 0) {
			pNewStackElement->namespaceUri =
				safe_strdup(newElement->namespaceURI);
			if (pNewStackElement->namespaceUri == NULL) {
				Parser_freeElementStackItem(pNewStackElement);
				free(pNewStackElement);
				return IXML_INSUFFICIENT_MEMORY;
			}
		}

		pCurElement = xmlParser->pCurElement;

		/* insert the new element into the top of the stack */
		pNewStackElement->nextElement = pCurElement;
		xmlParser->pCurElement = pNewStackElement;
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Reports whether there is a top level element in the parser.
 *
 * \return 1 if there is a top level element in the parser.
 */
static int isTopLevelElement(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	assert(xmlParser);
	return xmlParser->pCurElement == NULL;
}

/*!
 * \brief Decide whether the current element has default namespace
 */
static int Parser_hasDefaultNamespace(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in,out] The name space URI. */
	char **nsURI)
{
	IXML_ElementStack *pCur = xmlParser->pCurElement;

	while (pCur != NULL) {
		if ((pCur->prefix == NULL) && (pCur->namespaceUri != NULL)) {
			*nsURI = pCur->namespaceUri;
			return 1;
		} else {
			pCur = pCur->nextElement;
		}
	}

	return 0;
}

/*!
 * \brief Processes element name.
 *
 * \return IXML_SUCCESS if successful, otherwise or an error code.
 */
static int Parser_processElementName(
	/*! [in] The XML document. */
	IXML_Document *rootDoc,
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *newNode)
{
	IXML_Element *newElement = NULL;
	char *nsURI = NULL;
	int rc = IXML_SUCCESS;

	if (xmlParser->bHasTopLevel) {
		if (isTopLevelElement(xmlParser)) {
			return IXML_SYNTAX_ERR;
		}
	} else {
		xmlParser->bHasTopLevel = 1;
	}

	xmlParser->savePtr = xmlParser->curPtr;
	rc = ixmlDocument_createElementEx(
		rootDoc, newNode->nodeName, &newElement);
	if (rc != IXML_SUCCESS) {
		return rc;
	}

	rc = ixmlNode_setNodeProperties((IXML_Node *)newElement, newNode);
	if (rc != IXML_SUCCESS) {
		ixmlElement_free(newElement);
		return rc;
	}

	if (newNode->prefix) {
		/* element has namespace prefix */
		if (!Parser_ElementPrefixDefined(xmlParser, newNode, &nsURI)) {
			/* read next node to see whether it includes namespace
			 * definition */
			xmlParser->pNeedPrefixNode = (IXML_Node *)newElement;
		} else {
			/* fill in the namespace */
			Parser_setElementNamespace(newElement, nsURI);
		}
	} else {
		/* does element has default namespace */
		/* the node may have default namespace definition */
		if (Parser_hasDefaultNamespace(xmlParser, &nsURI)) {
			Parser_setElementNamespace(newElement, nsURI);
		} else {
			switch (xmlParser->state) {
			case eATTRIBUTE:
				/* the default namespace maybe defined later */
				xmlParser->pNeedPrefixNode =
					(IXML_Node *)newElement;
				break;
			default:
				break;
			}
		}
	}

	rc = ixmlNode_appendChild(
		xmlParser->currentNodePtr, (IXML_Node *)newElement);
	if (rc != IXML_SUCCESS) {
		ixmlElement_free(newElement);
		return rc;
	}

	xmlParser->currentNodePtr = (IXML_Node *)newElement;

	/* push element to stack */
	rc = Parser_pushElement(xmlParser, (IXML_Node *)newElement);
	return rc;
}

/*!
 * \brief Check if a new node->nodeName matches top of element stack.
 *
 * \return 1 if matches.
 */
static int Parser_isValidEndElement(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The node. */
	IXML_Node *newNode)
{
	assert(xmlParser);

	if (xmlParser->pCurElement == NULL) {
		return 0;
	}

	assert(xmlParser->pCurElement->element);
	assert(newNode);
	assert(newNode->nodeName);
	return strcmp(xmlParser->pCurElement->element, newNode->nodeName) == 0;
}

/*!
 * \brief Remove element from element stack.
 */
static void Parser_popElement(
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	IXML_ElementStack *pCur = NULL;
	IXML_NamespaceURI *pnsUri = NULL;
	IXML_NamespaceURI *pNextNS = NULL;

	pCur = xmlParser->pCurElement;
	if (pCur != NULL) {
		xmlParser->pCurElement = pCur->nextElement;
		Parser_freeElementStackItem(pCur);
		pnsUri = pCur->pNsURI;
		while (pnsUri != NULL) {
			pNextNS = pnsUri->nextNsURI;
			Parser_freeNsURI(pnsUri);
			free(pnsUri);
			pnsUri = pNextNS;
		}
		free(pCur);
	}
}

/*!
 * \brief Verifies endof element tag is the same as the openning element tag.
 */
static int Parser_eTagVerification(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The Node to process. */
	IXML_Node *newNode)
{
	assert(newNode->nodeName);
	assert(xmlParser->currentNodePtr);

	switch (newNode->nodeType) {
	case eELEMENT_NODE:
		if (Parser_isValidEndElement(xmlParser, newNode)) {
			Parser_popElement(xmlParser);
		} else {
			/* syntax error */
			return IXML_SYNTAX_ERR;
		}
		break;
	default:
		break;
	}

	if (strcmp(newNode->nodeName, xmlParser->currentNodePtr->nodeName) ==
		0) {
		xmlParser->currentNodePtr =
			xmlParser->currentNodePtr->parentNode;
	} else {
		return IXML_SYNTAX_ERR;
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Parses the xml file and returns the DOM document tree.
 *
 * \return
 */
static int Parser_parseDocument(
	/*! [out] The XML document. */
	IXML_Document **retDoc,
	/*! [in] The XML parser. */
	Parser *xmlParser)
{
	IXML_Document *gRootDoc = NULL;
	IXML_Node newNode;
	int bETag = 0;
	IXML_Node *tempNode = NULL;
	int rc = IXML_SUCCESS;
	IXML_CDATASection *cdataSecNode = NULL;

	/* It is important that the node gets initialized here, otherwise things
	 * can go wrong on the error handler. */
	ixmlNode_init(&newNode);

	rc = ixmlDocument_createDocumentEx(&gRootDoc);
	if (rc != IXML_SUCCESS) {
		goto ErrorHandler;
	}

	xmlParser->currentNodePtr = (IXML_Node *)gRootDoc;

	rc = Parser_skipProlog(xmlParser);
	if (rc != IXML_SUCCESS) {
		goto ErrorHandler;
	}

	while (bETag == 0) {
		/* clear the newNode contents. Redundant on the first iteration,
		 * but nonetheless, necessary due to the possible calls to
		 * ErrorHandler above. Currently, this is just a memset to zero.
		 */
		ixmlNode_init(&newNode);

		if (Parser_getNextNode(xmlParser, &newNode, &bETag) ==
			IXML_SUCCESS) {
			if (bETag == 0) {
				switch (newNode.nodeType) {
				case eELEMENT_NODE:
					rc = Parser_processElementName(
						gRootDoc, xmlParser, &newNode);
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}
					break;

				case eTEXT_NODE:
					rc = ixmlDocument_createTextNodeEx(
						gRootDoc,
						newNode.nodeValue,
						&tempNode);
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}

					rc = ixmlNode_appendChild(
						xmlParser->currentNodePtr,
						tempNode);
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}

					break;

				case eCDATA_SECTION_NODE:
					rc = ixmlDocument_createCDATASectionEx(
						gRootDoc,
						newNode.nodeValue,
						&cdataSecNode);
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}

					rc = ixmlNode_appendChild(
						xmlParser->currentNodePtr,
						&(cdataSecNode->n));
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}
					break;

				case eATTRIBUTE_NODE:
					rc = Parser_processAttributeName(
						gRootDoc, xmlParser, &newNode);
					if (rc != IXML_SUCCESS) {
						goto ErrorHandler;
					}
					break;

				default:
					break;
				}
			} else {
				/* ETag==1, endof element tag. */
				rc = Parser_eTagVerification(
					xmlParser, &newNode);
				if (rc != IXML_SUCCESS) {
					goto ErrorHandler;
				}
				xmlParser->state = eCONTENT;
			}

			/* reset bETag flag */
			bETag = 0;

		} else if (bETag) {
			/* file is done */
			break;
		} else {
			rc = IXML_FAILED;
			goto ErrorHandler;
		}
		Parser_freeNodeContent(&newNode);
	}

	if (xmlParser->pCurElement != NULL) {
		rc = IXML_SYNTAX_ERR;
		goto ErrorHandler;
	}

	*retDoc = (IXML_Document *)gRootDoc;
	Parser_free(xmlParser);
	return rc;

ErrorHandler:
	Parser_freeNodeContent(&newNode);
	ixmlDocument_free(gRootDoc);
	Parser_free(xmlParser);
	return rc;
}

int Parser_isValidXmlName(const DOMString name)
{
	const char *pstr = NULL;
	size_t i = (size_t)0;
	size_t nameLen = (size_t)0;

	assert(name != NULL);

	nameLen = strlen(name);
	pstr = name;
	if (Parser_isNameChar((int)*pstr, 0)) {
		for (i = (size_t)1; i < nameLen; ++i) {
			if (Parser_isNameChar((int)*(pstr + i), 1) == 0) {
				/* illegal char */
				return 0;
			}
		}
	}

	return 1;
}

void Parser_setErrorChar(char c) { g_error_char = c; }

#ifdef IXML_HAVE_SCRIPTSUPPORT
void Parser_setBeforeFree(IXML_BeforeFreeNode_t hndlr)
{
	Before_Free_callback = hndlr;
}

IXML_BeforeFreeNode_t Parser_getBeforeFree(void)
{
	return Before_Free_callback;
}
#endif

/*!
 * \brief Initializes a xml parser.
 *
 * \return The parser object or \b NULL if there is not enough memory.
 */
static Parser *Parser_init(void)
{
	Parser *newParser = NULL;

	newParser = (Parser *)malloc(sizeof(Parser));
	if (newParser == NULL) {
		return NULL;
	}

	memset(newParser, 0, sizeof(Parser));
	ixml_membuf_init(&(newParser->tokenBuf));
	ixml_membuf_init(&(newParser->lastElem));

	return newParser;
}

/*!
 * \brief Read a xml file or buffer contents into xml parser.
 */
static int Parser_readFileOrBuffer(
	/*! [in] The XML parser. */
	Parser *xmlParser,
	/*! [in] The file name or the buffer to copy, according to the
	 * parameter "file". */
	const char *xmlFileName,
	/*! [in] 1 if you want to read from a file, 0 if xmlFileName is
	 * the buffer to copy to the parser. */
	int file)
{
	long fileSize = 0;
	size_t bytesRead = (size_t)0;
	FILE *xmlFilePtr = NULL;

	if (file) {
#ifdef _WIN32
		fopen_s(&xmlFilePtr, xmlFileName, "rb");
#else
		xmlFilePtr = fopen(xmlFileName, "rb");
#endif
		if (xmlFilePtr == NULL) {
			return IXML_NO_SUCH_FILE;
		} else {
			fseek(xmlFilePtr, 0, SEEK_END);
			fileSize = ftell(xmlFilePtr);
			if (fileSize <= 0) {
				fclose(xmlFilePtr);
				return IXML_SYNTAX_ERR;
			}

			xmlParser->dataBuffer =
				(char *)malloc((size_t)fileSize + (size_t)1);
			if (xmlParser->dataBuffer == NULL) {
				fclose(xmlFilePtr);
				return IXML_INSUFFICIENT_MEMORY;
			}

			fseek(xmlFilePtr, 0, SEEK_SET);
			bytesRead = fread(xmlParser->dataBuffer,
				(size_t)1,
				(size_t)fileSize,
				xmlFilePtr);
			/* append null */
			xmlParser->dataBuffer[bytesRead] = '\0';
			fclose(xmlFilePtr);
		}
	} else {
		xmlParser->dataBuffer = safe_strdup(xmlFileName);
		if (xmlParser->dataBuffer == NULL) {
			return IXML_INSUFFICIENT_MEMORY;
		}
	}

	return IXML_SUCCESS;
}

/*!
 * \brief Parses a xml file and return the DOM tree.
 */
int Parser_LoadDocument(
	/*! [out] The output document tree. */
	IXML_Document **retDoc,
	/*! [in] The file name or the buffer to copy, according to the
	 * parameter "file". */
	const char *xmlFileName,
	/*! [in] 1 if you want to read from a file, 0 if xmlFileName is
	 * the buffer to copy to the parser. */
	int file)
{
	int rc = IXML_SUCCESS;
	Parser *xmlParser = NULL;

	xmlParser = Parser_init();
	if (xmlParser == NULL) {
		return IXML_INSUFFICIENT_MEMORY;
	}

	rc = Parser_readFileOrBuffer(xmlParser, xmlFileName, file);
	if (rc != IXML_SUCCESS) {
		Parser_free(xmlParser);
		return rc;
	}

	xmlParser->curPtr = xmlParser->dataBuffer;
	rc = Parser_parseDocument(retDoc, xmlParser);
	return rc;
}

void Parser_freeNodeContent(IXML_Node *nodeptr)
{
	if (nodeptr == NULL) {
		return;
	}

	if (nodeptr->nodeName != NULL) {
		free(nodeptr->nodeName);
	}

	if (nodeptr->nodeValue != NULL) {
		free(nodeptr->nodeValue);
	}

	if (nodeptr->namespaceURI != NULL) {
		free(nodeptr->namespaceURI);
	}

	if (nodeptr->prefix != NULL) {
		free(nodeptr->prefix);
	}

	if (nodeptr->localName != NULL) {
		free(nodeptr->localName);
	}
}

/*!
 * \brief Set the node prefix and localName as defined by the nodeName in the
 * form of ns:name.
 */
int Parser_setNodePrefixAndLocalName(
	/*! [in,out] The Node to process. */
	IXML_Node *node)
{
	char *pStrPrefix = NULL;
	char *pLocalName;
	ptrdiff_t nPrefix;

	assert(node != NULL);
	if (node == NULL) {
		return IXML_FAILED;
	}

	pStrPrefix = strchr(node->nodeName, ':');
	if (pStrPrefix == NULL) {
		node->prefix = NULL;
		node->localName = safe_strdup(node->nodeName);
		if (node->localName == NULL) {
			return IXML_INSUFFICIENT_MEMORY;
		}

	} else {
		/* fill in the local name and prefix */
		pLocalName = (char *)pStrPrefix + 1;
		nPrefix = pStrPrefix - node->nodeName;
		node->prefix = malloc((size_t)nPrefix + (size_t)1);
		if (!node->prefix) {
			return IXML_INSUFFICIENT_MEMORY;
		}

		memset(node->prefix, 0, (size_t)nPrefix + (size_t)1);
		strncpy(node->prefix, node->nodeName, (size_t)nPrefix);

		node->localName = safe_strdup(pLocalName);
		if (node->localName == NULL) {
			free(node->prefix);
			/* no need to free really, main loop will frees it
			 * when return code is not success */
			node->prefix = NULL;
			return IXML_INSUFFICIENT_MEMORY;
		}
	}

	return IXML_SUCCESS;
}
