/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbXmlItf
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbXmlItf.h"
#include "xbVariant.h"

#pragma warning(push)
#pragma warning(disable: 4311) //troncation de pointeur de 'wchar *' à 'xbNptr'
xbEimpl(xbExmlParser,xbEadv);

/********************************************************************************
         C/C++ escape/unescape (\\xx ...)
 ********************************************************************************/
bool xbEscape(const wchar *pStr,int lg,xbBSTR *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,ulong cp,bool bUnicodeExt)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;

	while(pos < lg)
	{
		if ((*ptr >= 32 && /**ptr <= 0x7F &&*/ *ptr != L'"' && *ptr!=L'\''
			&& *ptr != L'\\'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);
		wchar ch[6];
		int chlg = 2;
		ch[0] = L'\\';
		switch(*ptr)
		{
		case L'\\':
			ch[1] = L'\\';
			break;
		case L'"':
			ch[1] = L'"';
			break;
		case L'\'':
			ch[1] = L'\'';
			break;
		case L'\n':
			ch[1] = L'n';
			break;
		case L'\r':
			ch[1] = L'r';
			break;
		case L'\t':
			ch[1] = L't';
			break;
		case 0:
			ch[1] = L'0';
			break;
		default:
			if (*ptr <= 0x7F || (!bUnicodeExt && *ptr < 256))
			{
				chlg = 4;
				ch[1] = L'x';
				ch[2] = ((*ptr)>>4)&0xF;
				if (ch[2] <= 9)
					ch[2] += L'0';
				else
					ch[2] += L'A' - 10;
				ch[3] = ((*ptr)&0xF);
				if (ch[3] <= 9)
					ch[3] += L'0';
				else
					ch[3] += L'A' - 10;
				break;
			}
			chlg = 6;
			ch[1] = L'u';
			ch[2] = ((*ptr)>>12)&0xF;
			ch[3] = ((*ptr)>>8)&0xF;
			ch[4] = ((*ptr)>>4)&0xF;
			ch[5] = ((*ptr)&0xF);
			for(int i = 2; i < 6; i++)
			{
				if (ch[i] <= 9)
					ch[i] += L'0';
				else
					ch[i] += L'A' - 10;
			}
			break;
		}
		pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbEscape(const wchar *pStr,int lg,xbStringW *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,ulong cp,bool bUnicodeExt)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;

	while(pos < lg)
	{
		if ((*ptr >= 32 && /**ptr <= 0x7F &&*/ *ptr != L'"' && *ptr!=L'\''
			&& *ptr != L'\\'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);
		wchar ch[6];
		int chlg = 2;
		ch[0] = L'\\';
		switch(*ptr)
		{
		case L'\\':
			ch[1] = L'\\';
			break;
		case L'"':
			ch[1] = L'"';
			break;
		case L'\'':
			ch[1] = L'\'';
			break;
		case L'\n':
			ch[1] = L'n';
			break;
		case L'\r':
			ch[1] = L'r';
			break;
		case L'\t':
			ch[1] = L't';
			break;
		case 0:
			ch[1] = L'0';
			break;
		default:
			if (*ptr <= 0x7F || (!bUnicodeExt && *ptr < 256))
			{
				chlg = 4;
				ch[1] = L'x';
				ch[2] = ((*ptr)>>4)&0xF;
				if (ch[2] <= 9)
					ch[2] += L'0';
				else
					ch[2] += L'A' - 10;
				ch[3] = ((*ptr)&0xF);
				if (ch[3] <= 9)
					ch[3] += L'0';
				else
					ch[3] += L'A' - 10;
				break;
			}
			chlg = 6;
			ch[1] = L'u';
			ch[2] = ((*ptr)>>12)&0xF;
			ch[3] = ((*ptr)>>8)&0xF;
			ch[4] = ((*ptr)>>4)&0xF;
			ch[5] = ((*ptr)&0xF);
			for(int i = 2; i < 6; i++)
			{
				if (ch[i] <= 9)
					ch[i] += L'0';
				else
					ch[i] += L'A' - 10;
			}
			break;
		}
		pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbEscape(const char *pStr,int lg,xbStringA *pBuffer,LPCSTR pEncodeInclude,LPCSTR pEncodeExclude,ulong cp,bool bUnicodeExt)
{
	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;

	while(pos < lg)
	{
		if ((*ptr >= 32 && /**ptr <= 0x7F &&*/ *ptr != '"' && *ptr!='\''
			&& *ptr != '\\'
			&& (!pEncodeInclude || strchr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && strchr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pBuffer->Add(pStr+lastpos,pos - lastpos);
		uchar ch[6];
		int chlg = 2;
		ch[0] = '\\';
		switch(*ptr)
		{
		case '\\':
			ch[1] = '\\';
			break;
		case '"':
			ch[1] = '"';
			break;
		case '\'':
			ch[1] = '\'';
			break;
		case '\n':
			ch[1] = 'n';
			break;
		case '\r':
			ch[1] = 'r';
			break;
		case '\t':
			ch[1] = 't';
			break;
		case 0:
			ch[1] = '0';
			break;
		default:
			if (*ptr > 0x7F && bUnicodeExt)
			{
				wchar uch;
				int n = ::MultiByteToWideChar(cp, 0, (LPCSTR)ptr, 1, &uch, 1);
				if (n>0)
				{
					chlg = 6;
					ch[1] = 'u';
					ch[2] = (uch>>12)&0xF;
					ch[3] = (uch>>8)&0xF;
					ch[4] = (uch>>4)&0xF;
					ch[5] = (uch&0xF);
					for(int i = 2; i < 6; i++)
					{
						if (ch[i] <= 9)
							ch[i] += '0';
						else
							ch[i] += 'A' - 10;
					}
					break;
				}
			}
			chlg = 4;
			ch[1] = 'x';
			ch[2] = ((*ptr)>>4)&0xF;
			if (ch[2] <= 9)
				ch[2] += '0';
			else
				ch[2] += 'A' - 10;
			ch[3] = ((*ptr)&0xF);
			if (ch[3] <= 9)
				ch[3] += '0';
			else
				ch[3] += 'A' - 10;
			break;
		}
		pBuffer->Add((LPCSTR)ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUnescape(LPCWSTR pStr,int lg,xbBSTR *pbstr,ulong cp,xbOpMode opm)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		ch;
//	lg--;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch != L'\\') 
			continue;
		if (pos >= lg)
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \\ not terminated at end of string \""+xbStringA(pStr,lg)+"\"");
			continue;
		}
		if (lastpos<0) 
			lastpos = 0;
		if (lastpos < pos - 1)
			pbstr->Add(pStr + lastpos,pos - lastpos - 1);
		ch = *(ptr++);
		pos++;
		switch(ch)
		{
		case L'n':
			ch = L'\n';
			break;
		case L'r':
			ch = L'\r';
			break;
		case L't':
			ch = L'\t';
			break;
		case L'\'':
		case L'\"':
		case L'\\':
			break;
		case L'a':
			ch = L'\a';
			break;
		case L'b':
			ch = L'\b';
			break;
		case L'f':
			ch = L'\f';
			break;
		case L'v':
			ch = L'\v';
			break;
		case L'u' :
			{
				for(int i = 0; i < 4 && pos < lg; i++)
				{
					if ((*ptr < L'0' || *ptr > L'9')
						&& (*ptr < L'a' || *ptr > L'f')
						&& (*ptr < L'A' || *ptr > L'F')) break;

					if (i == 0)
						ch = 0;
					else
						ch <<= 4;
					if (*ptr >= L'0' && *ptr <= L'9') ch += wchar(uint(*ptr) - uint(L'0'));
					else if (*ptr >= L'a' && *ptr <= L'f') ch += wchar(uint(*ptr) - uint(L'a') + 10);
					else ch += wchar(uint(*ptr) - uint(L'A') + 10);
					ptr++;
					pos++;
				}
			}
			break;
		case L'x' :
			if ((*ptr < L'0' || *ptr > L'9')
				&& (*ptr < L'a' || *ptr > L'f')
				&& (*ptr < L'A' || *ptr > L'F')) break;
			if (*ptr >= L'0' && *ptr <= L'9') ch = wchar(uint(*ptr) - uint(L'0'));
			else if (*ptr >= L'a' && *ptr <= L'f') ch = wchar(uint(*ptr) - uint(L'a') + 10);
			else ch = wchar(uint(*ptr) - uint(L'A') + 10);
			ptr++;
			pos++;
			if (pos >= lg)
				break;
			if ((*ptr < L'0' || *ptr > L'9')
				&& (*ptr < L'a' || *ptr > L'f')
				&& (*ptr < L'A' || *ptr > L'F')) break;
			ch <<= 4;
			if (*ptr >= L'0' && *ptr <= L'9') ch += wchar(uint(*ptr) - uint(L'0'));
			else if (*ptr >= L'a' && *ptr <= L'f') ch += wchar(uint(*ptr) - uint(L'a') + 10);
			else ch += wchar(uint(*ptr) - uint(L'A') + 10);
			ptr++;
			pos++;
			break;
		case '0' :
			ch = 0;
			{
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < L'0' || *ptr > L'7') break;

					ch <<= 3;
					ch += wchar(uint(*ptr) - uint(L'0'));
					ptr++;
					pos++;
				}
			}
			break;
		default:
			if (ch >= '1' && ch <= '9')
			{
				ch = wchar(uint(ch) - uint(L'0'));
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < L'0' || *ptr > L'9') break;

					ch *= 10;
					ch += wchar(uint(*ptr) - uint(L'0'));
					ptr++;
					pos++;
				}

				break;
			}
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \""+xbStringA(&ch,1)+", in string \""+xbStringA(pStr,lg)+"\"");
			break;
		}
		pbstr->Add(&ch,1);
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUnescape(LPCWSTR pStr,int lg,xbStringW *pbstr,ulong cp,xbOpMode opm)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		ch;
//	lg--;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch != L'\\') 
			continue;
		if (pos >= lg)
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \\ not terminated at end of string \""+xbStringA(pStr,lg)+"\"");
			continue;
		}
		if (lastpos<0) 
			lastpos = 0;
		if (lastpos < pos - 1)
			pbstr->Add(pStr + lastpos,pos - lastpos - 1);
		ch = *(ptr++);
		pos++;
		switch(ch)
		{
		case L'n':
			ch = L'\n';
			break;
		case L'r':
			ch = L'\r';
			break;
		case L't':
			ch = L'\t';
			break;
		case L'\'':
		case L'\"':
		case L'\\':
			break;
		case L'a':
			ch = L'\a';
			break;
		case L'b':
			ch = L'\b';
			break;
		case L'f':
			ch = L'\f';
			break;
		case L'v':
			ch = L'\v';
			break;
		case L'u' :
			{
				for(int i = 0; i < 4 && pos < lg; i++)
				{
					if ((*ptr < L'0' || *ptr > L'9')
						&& (*ptr < L'a' || *ptr > L'f')
						&& (*ptr < L'A' || *ptr > L'F')) break;

					if (i == 0)
						ch = 0;
					else
						ch <<= 4;
					if (*ptr >= L'0' && *ptr <= L'9') ch += wchar(uint(*ptr) - uint(L'0'));
					else if (*ptr >= L'a' && *ptr <= L'f') ch += wchar(uint(*ptr) - uint(L'a') + 10);
					else ch += wchar(uint(*ptr) - uint(L'A') + 10);
					ptr++;
					pos++;
				}
			}
			break;
		case L'x' :
			if ((*ptr < L'0' || *ptr > L'9')
				&& (*ptr < L'a' || *ptr > L'f')
				&& (*ptr < L'A' || *ptr > L'F')) break;
			if (*ptr >= L'0' && *ptr <= L'9') ch = wchar(uint(*ptr) - uint(L'0'));
			else if (*ptr >= L'a' && *ptr <= L'f') ch = wchar(uint(*ptr) - uint(L'a') + 10);
			else ch = wchar(uint(*ptr) - uint(L'A') + 10);
			ptr++;
			pos++;
			if (pos >= lg)
				break;
			if ((*ptr < L'0' || *ptr > L'9')
				&& (*ptr < L'a' || *ptr > L'f')
				&& (*ptr < L'A' || *ptr > L'F')) break;
			ch <<= 4;
			if (*ptr >= L'0' && *ptr <= L'9') ch += wchar(uint(*ptr) - uint(L'0'));
			else if (*ptr >= L'a' && *ptr <= L'f') ch += wchar(uint(*ptr) - uint(L'a') + 10);
			else ch += wchar(uint(*ptr) - uint(L'A') + 10);
			ptr++;
			pos++;
			break;
		case '0' :
			ch = 0;
			{
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < L'0' || *ptr > L'7') break;

					ch <<= 3;
					ch += wchar(uint(*ptr) - uint(L'0'));
					ptr++;
					pos++;
				}
			}
			break;
		default:
			if (ch >= '1' && ch <= '9')
			{
				ch = wchar(uint(ch) - uint(L'0'));
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < L'0' || *ptr > L'9') break;

					ch *= 10;
					ch += wchar(uint(*ptr) - uint(L'0'));
					ptr++;
					pos++;
				}

				break;
			}
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \""+xbStringA(&ch,1)+", in string \""+xbStringA(pStr,lg)+"\"");
			break;
		}
		pbstr->Add(&ch,1);
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUnescape(const char *pStr,int lg,xbStringA *pBuffer,ulong cp,xbOpMode opm)
{
//	xbRdbgAssert(memchr(pStr,'§',lg) == NULL);

	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;
	uchar		ch;
//	lg--;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch != '\\') 
			continue;
		if (pos >= lg)
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \\ not terminated at end of string \""+xbStringA(pStr,lg)+"\"");
			continue;
		}
		if (lastpos<0) 
			lastpos = 0;
		if (lastpos < pos - 1)
			pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
		ch = *(ptr++);
		pos++;
		switch(ch)
		{
		case 'n':
			ch = '\n';
			break;
		case 'r':
			ch = '\r';
			break;
		case 't':
			ch = '\t';
			break;
		case 'a':
			ch = '\a';
			break;
		case 'b':
			ch = '\b';
			break;
		case 'f':
			ch = '\f';
			break;
		case 'v':
			ch = '\v';
			break;
		case '\'':
		case '\"':
		case '\\':
			break;
		case 'u' :
			{
				wchar uch = 0;
				int i;
				for(i = 0; i < 4 && pos < lg; i++)
				{
					if ((*ptr < '0' || *ptr > '9')
						&& (*ptr < 'a' || *ptr > 'f')
						&& (*ptr < 'A' || *ptr > 'F')) break;

					uch <<= 4;
					if (*ptr >= '0' && *ptr <= '9') uch += uchar(uint(*ptr) - uint('0'));
					else if (*ptr >= 'a' && *ptr <= 'f') uch += uchar(uint(*ptr) - uint('a') + 10);
					else uch += uchar(uint(*ptr) - uint('A') + 10);
					ptr++;
					pos++;
				}
				if (i > 0)
				{
					char str[8];
					int n = ::WideCharToMultiByte(cp, 0, &uch, 1, str, sizeof(str),NULL,NULL);
					if (n > 0)
						pBuffer->Add(str,n);
					lastpos = pos;
					continue;
				}
			}
			break;
		case 'x' :
			if ((*ptr < '0' || *ptr > '9')
				&& (*ptr < 'a' || *ptr > 'f')
				&& (*ptr < 'A' || *ptr > 'F')) break;
			if (*ptr >= '0' && *ptr <= '9') ch = uchar(uint(*ptr) - uint('0'));
			else if (*ptr >= 'a' && *ptr <= 'f') ch = uchar(uint(*ptr) - uint('a') + 10);
			else ch = uchar(uint(*ptr) - uint('A') + 10);
			ptr++;
			pos++;
			if (pos >= lg)
				break;
			if ((*ptr < '0' || *ptr > '9')
				&& (*ptr < 'a' || *ptr > 'f')
				&& (*ptr < 'A' || *ptr > 'F')) break;
			ch <<= 4;
			if (*ptr >= '0' && *ptr <= '9') ch += uchar(uint(*ptr) - uint('0'));
			else if (*ptr >= 'a' && *ptr <= 'f') ch += uchar(uint(*ptr) - uint('a') + 10);
			else ch += uchar(uint(*ptr) - uint('A') + 10);
			ptr++;
			pos++;
			break;
		case '0' :
			ch = 0;
			{
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < '0' || *ptr > '7') break;

					ch <<= 3;
					ch += uchar(uint(*ptr) - uint('0'));
					ptr++;
					pos++;
				}
			}
			break;
		default:
			if (ch >= '1' && ch <= '9')
			{
				ch = uchar(uint(ch) - uint('0'));
				for(int i = 0; i < 3 && pos < lg; i++)
				{
					if (*ptr < '0' || *ptr > '9') break;

					ch *= 10;
					ch += uchar(uint(*ptr) - uint('0'));
					ptr++;
					pos++;
				}

				break;
			}
			if (opm == xbOp_Required)
				xbThrow xbEapp("Invalid escape sequence: \""+xbStringA((char*)&ch,1)+", in string \""+xbStringA(pStr,lg)+"\"");
			break;
		}
		pBuffer->Add((LPCSTR)&ch,1);
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos);
	return true;
}


/********************************************************************************
         Url escape/unescape (%xx ...)
 ********************************************************************************/
bool xbUrlEscape(LPCWSTR pStr,int lg,xbBSTR *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,ulong cp,bool bUnicodeExt,bool bPlusEncoded,wchar escapeChar)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar ch[6];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != L'"' && *ptr!=L'\''
			&& (!bPlusEncoded || *ptr != 0x20)
			&& *ptr != L'\\'
			&& *ptr != L'>'  && *ptr != L'<'
			&& *ptr != L'#' && *ptr != L'&' && *ptr != L'+' && *ptr != L':'
			&& *ptr != escapeChar && *ptr != L'{' && *ptr != L'}' && *ptr != L'['
			&& *ptr != L']' && *ptr != L'|' && *ptr != L'^' && *ptr != L'~'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);

		if (bPlusEncoded && *ptr == 0x20)
		{
			pbstr->Add(L"+",1);
			lastpos = ++pos;
			ptr++;
			continue;
		}

		int chlg;
		ch[0] = escapeChar;
		for(;;)
		{
			if (*ptr <= 0x7F)
			{
				chlg = 3;
				ch[1] = ((*ptr)>>4)&0xF;
				if (ch[1] <= 9)
					ch[1] += L'0';
				else
					ch[1] += L'A' - 10;
				ch[2] = ((*ptr)&0xF);
				if (ch[2] <= 9)
					ch[2] += L'0';
				else
					ch[2] += L'A' - 10;
				break;
			}
			if (bUnicodeExt)
			{
				chlg = 6;
				ch[1] = 'u';
				ch[2] = ((*ptr)>>12)&0xF;
				ch[3] = ((*ptr)>>8)&0xF;
				ch[4] = ((*ptr)>>4)&0xF;
				ch[5] = ((*ptr)&0xF);
				for(int i = 2; i < 6; i++)
				{
					if (ch[i] <= 9)
						ch[i] += L'0';
					else
						ch[i] += L'A' - 10;
				}
				break;
			}
			chlg = 0;

			xbStringA dst,dst2;
			xbCPconvert(ptr,1,cp,&dst);

			if (!xbUrlEscape(dst,dst.Length,&dst2,NULL,NULL,CP_ACP,false,bPlusEncoded,char(escapeChar)))
				pbstr->Add(dst);
			else
				pbstr->Add(dst2);
			break;
		}
		if (chlg)
			pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUrlEscape(LPCWSTR pStr,int lg,xbStringW *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,ulong cp,bool bUnicodeExt,bool bPlusEncoded,wchar escapeChar)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar ch[6];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != L'"' && *ptr!=L'\''
			&& (!bPlusEncoded || *ptr != 0x20)
			&& *ptr != L'\\'
			&& *ptr != L'>'  && *ptr != L'<'
			&& *ptr != L'#' && *ptr != L'&' && *ptr != L'+' && *ptr != L':'
			&& *ptr != escapeChar && *ptr != L'{' && *ptr != L'}' && *ptr != L'['
			&& *ptr != L']' && *ptr != L'|' && *ptr != L'^' && *ptr != L'~'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);

		if (bPlusEncoded && *ptr == 0x20)
		{
			pbstr->Add(L"+",1);
			lastpos = ++pos;
			ptr++;
			continue;
		}

		int chlg;
		ch[0] = escapeChar;
		for(;;)
		{
			if (*ptr <= 0x7F)
			{
				chlg = 3;
				ch[1] = ((*ptr)>>4)&0xF;
				if (ch[1] <= 9)
					ch[1] += L'0';
				else
					ch[1] += L'A' - 10;
				ch[2] = ((*ptr)&0xF);
				if (ch[2] <= 9)
					ch[2] += L'0';
				else
					ch[2] += L'A' - 10;
				break;
			}
			if (bUnicodeExt)
			{
				chlg = 6;
				ch[1] = 'u';
				ch[2] = ((*ptr)>>12)&0xF;
				ch[3] = ((*ptr)>>8)&0xF;
				ch[4] = ((*ptr)>>4)&0xF;
				ch[5] = ((*ptr)&0xF);
				for(int i = 2; i < 6; i++)
				{
					if (ch[i] <= 9)
						ch[i] += L'0';
					else
						ch[i] += L'A' - 10;
				}
				break;
			}
			chlg = 0;

			xbStringA dst,dst2;
			xbCPconvert(ptr,1,cp,&dst);

			if (!xbUrlEscape(dst,dst.Length,&dst2,NULL,NULL,CP_ACP,false,bPlusEncoded,char(escapeChar)))
				pbstr->Add(xbStringW(dst));
			else
				pbstr->Add(xbStringW(dst2));
			break;
		}
		if (chlg)
			pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUrlEscape(const char *pStr,int lg,xbStringA *pBuffer,LPCSTR pEncodeInclude,LPCSTR pEncodeExclude,ulong cp,bool bUnicodeExt,bool bPlusEncoded,char escapeChar)
{
	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;
	uchar ch[6];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != '"' && *ptr!='\''
			&& (!bPlusEncoded || *ptr != 0x20)
			&& *ptr != '\\'
			&& *ptr != '>'  && *ptr != '<'
			&& *ptr != '#' && *ptr != '&' && *ptr != '+' && *ptr != ':'
			&& *ptr != escapeChar && *ptr != '{' && *ptr != '}' && *ptr != '['
			&& *ptr != ']' && *ptr != '|' && *ptr != '^' && *ptr != '~'
			&& (!pEncodeInclude || strchr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && strchr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (!bUnicodeExt && cp == CP_UTF8)
		{
			xbBSTR dst(xbAllocLib),dst2(xbAllocLib),dstEncodeInclude(xbAllocLib),dstEncodeExclude(xbAllocLib);
			::xbCPconvert(CP_ACP,pStr,lg,&dst);
			if (pEncodeInclude)
				dstEncodeInclude.Set(pEncodeInclude);
			if (pEncodeExclude)
				dstEncodeExclude.Set(pEncodeExclude);

			if (!xbUrlEscape(dst,dst.GetLength(),&dst2,pEncodeInclude?(LPCWSTR)dstEncodeInclude:(LPCWSTR)NULL,
				pEncodeExclude?(LPCWSTR)dstEncodeExclude:(LPCWSTR)NULL,cp,bUnicodeExt,bPlusEncoded,(wchar)escapeChar))
				return false;

			if (pBuffer)
				::xbCPconvert(dst2,dst2.GetLength(),cp,pBuffer);

			return true;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pBuffer->Add(pStr+lastpos,pos - lastpos);
		if (bPlusEncoded && *ptr == 0x20)
		{
			pBuffer->Add("+",1);
			lastpos = ++pos;
			ptr++;
			continue;
		}

		int chlg;
		ch[0] = escapeChar;
		for(;;)
		{
			if (bUnicodeExt && *ptr > 0x7f)
			{
				wchar uch;
				int n = ::MultiByteToWideChar(cp, 0, (LPCSTR)ptr, 1, &uch, 1);
				if (n>0)
				{
					chlg = 6;
					ch[1] = 'u';
					ch[2] = (uch>>12)&0xF;
					ch[3] = (uch>>8)&0xF;
					ch[4] = (uch>>4)&0xF;
					ch[5] = (uch&0xF);
					for(int i = 2; i < 6; i++)
					{
						if (ch[i] <= 9)
							ch[i] += '0';
						else
							ch[i] += 'A' - 10;
					}
					break;
				}
			}
			chlg = 3;
			ch[1] = ((*ptr)>>4)&0xF;
			if (ch[1] <= 9)
				ch[1] += '0';
			else
				ch[1] += 'A' - 10;
			ch[2] = ((*ptr)&0xF);
			if (ch[2] <= 9)
				ch[2] += '0';
			else
				ch[2] += 'A' - 10;
			break;
		}
		pBuffer->Add((LPCSTR)ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbUrlUnescape(LPCWSTR pStr,int lg,xbBSTR *pbstr,ulong cp,bool bPlusEncoded,wchar escapeChar)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		wch;
	lg--;

	xbStringA tmp;
	while(pos < lg)
	{
		wch = *(ptr++);
		pos++;
		if (bPlusEncoded && wch == L'+')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				tmp.Add(xbStringA(cp,pStr + lastpos,pos - lastpos - 1));
			tmp.Add(" ",1);
			lastpos = pos;
			continue;
		}
		if (wch != escapeChar) 
			continue;
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos - 1)
			tmp.Add(xbStringA(cp,pStr + lastpos,pos - lastpos - 1));
		wch = *(ptr++);
		pos++;
		switch(wch)
		{
		case L'u' :
			{
				for(int i = 0; i < 4 && pos <= lg; i++)
				{
					if ((*ptr < L'0' || *ptr > L'9')
						&& (*ptr < L'a' || *ptr > L'f')
						&& (*ptr < L'A' || *ptr > L'F')) break;

					if (i == 0)
						wch = 0;
					else
						wch <<= 4;
					if (*ptr >= L'0' && *ptr <= L'9') wch += wchar(uint(*ptr) - uint(L'0'));
					else if (*ptr >= L'a' && *ptr <= L'f') wch += wchar(uint(*ptr) - uint(L'a') + 10);
					else wch += wchar(uint(*ptr) - uint(L'A') + 10);
					ptr++;
					pos++;
				}
				if (tmp.IsValid())
				{
					pbstr->Add(cp,tmp,tmp.Length);
					tmp.Empty();
				}
				pbstr->Add(&wch,1);
			}
			break;
		default:
			if (wch == escapeChar)
			{
				if (tmp.IsValid())
				{
					pbstr->Add(cp,tmp,tmp.Length);
					tmp.Empty();
				}
				pbstr->Add(&wch,1);
				break;
			}

			{
				if ((wch < L'0' || wch > L'9')
					&& (wch < L'a' || wch > L'f')
					&& (wch < L'A' || wch > L'F')) break;
				if (wch >= L'0' && wch <= L'9') wch = wchar(uint(wch) - uint(L'0'));
				else if (wch >= L'a' && wch <= L'f') wch = wchar(uint(wch) - uint(L'a') + 10);
				else wch = wchar(uint(wch) - uint(L'A') + 10);
				if (pos > lg)
					break;
				if ((*ptr < L'0' || *ptr > L'9')
					&& (*ptr < L'a' || *ptr > L'f')
					&& (*ptr < L'A' || *ptr > L'F')) break;
				wch <<= 4;
				if (*ptr >= L'0' && *ptr <= L'9') wch += wchar(uint(*ptr) - uint(L'0'));
				else if (*ptr >= L'a' && *ptr <= L'f') wch += wchar(uint(*ptr) - uint(L'a') + 10);
				else wch += wchar(uint(*ptr) - uint(L'A') + 10);
				ptr++;
				pos++;

				char ch = char(wch);
				tmp.Add(&ch,1);
				break;
			}
		}
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos <= lg)
		tmp.Add(xbStringA(cp,pStr+lastpos,lg - lastpos + 1));
	if (tmp.IsValid())
		pbstr->Add(cp,tmp,tmp.Length);
	return true;
}

bool xbUrlUnescape(LPCWSTR pStr,int lg,xbStringW *pbstr,ulong cp,bool bPlusEncoded,wchar escapeChar)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		wch;
	lg--;

	xbStringA tmp;
	while(pos < lg)
	{
		wch = *(ptr++);
		pos++;
		if (bPlusEncoded && wch == L'+')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				tmp.Add(xbStringA(cp,pStr + lastpos,pos - lastpos - 1));
			tmp.Add(" ",1);
			lastpos = pos;
			continue;
		}
		if (wch != escapeChar) 
			continue;
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos - 1)
			tmp.Add(xbStringA(cp,pStr + lastpos,pos - lastpos - 1));
		wch = *(ptr++);
		pos++;
		switch(wch)
		{
		case L'u' :
			{
				for(int i = 0; i < 4 && pos <= lg; i++)
				{
					if ((*ptr < L'0' || *ptr > L'9')
						&& (*ptr < L'a' || *ptr > L'f')
						&& (*ptr < L'A' || *ptr > L'F')) break;

					if (i == 0)
						wch = 0;
					else
						wch <<= 4;
					if (*ptr >= L'0' && *ptr <= L'9') wch += wchar(uint(*ptr) - uint(L'0'));
					else if (*ptr >= L'a' && *ptr <= L'f') wch += wchar(uint(*ptr) - uint(L'a') + 10);
					else wch += wchar(uint(*ptr) - uint(L'A') + 10);
					ptr++;
					pos++;
				}
				if (tmp.IsValid())
				{
					pbstr->Add(xbStringW(cp,tmp,tmp.Length));
					tmp.Empty();
				}
				pbstr->Add(&wch,1);
			}
			break;
		default:
			if (wch == escapeChar)
			{
				if (tmp.IsValid())
				{
					pbstr->Add(xbStringW(cp,tmp,tmp.Length));
					tmp.Empty();
				}
				pbstr->Add(&wch,1);
				break;
			}

			{
				if ((wch < L'0' || wch > L'9')
					&& (wch < L'a' || wch > L'f')
					&& (wch < L'A' || wch > L'F')) break;
				if (wch >= L'0' && wch <= L'9') wch = wchar(uint(wch) - uint(L'0'));
				else if (wch >= L'a' && wch <= L'f') wch = wchar(uint(wch) - uint(L'a') + 10);
				else wch = wchar(uint(wch) - uint(L'A') + 10);
				if (pos > lg)
					break;
				if ((*ptr < L'0' || *ptr > L'9')
					&& (*ptr < L'a' || *ptr > L'f')
					&& (*ptr < L'A' || *ptr > L'F')) break;
				wch <<= 4;
				if (*ptr >= L'0' && *ptr <= L'9') wch += wchar(uint(*ptr) - uint(L'0'));
				else if (*ptr >= L'a' && *ptr <= L'f') wch += wchar(uint(*ptr) - uint(L'a') + 10);
				else wch += wchar(uint(*ptr) - uint(L'A') + 10);
				ptr++;
				pos++;

				char ch = char(wch);
				tmp.Add(&ch,1);
				break;
			}
		}
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos <= lg)
		tmp.Add(xbStringA(cp,pStr+lastpos,lg - lastpos + 1));
	if (tmp.IsValid())
		pbstr->Add(xbStringW(cp,tmp,tmp.Length));
	return true;
}

bool xbUrlUnescape(const char *pStr,int lg,xbStringA *pBuffer,ulong cp,bool bPlusEncoded,char escapeChar)
{
	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;
	uchar		ch;
	lg--;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (bPlusEncoded && ch == '+')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
			pBuffer->Add(" ",1);
			lastpos = pos;
			continue;
		}
		if (ch != escapeChar) 
			continue;
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos - 1)
			pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
		ch = *(ptr++);
		pos++;
		switch(ch)
		{
		case 'u' :
			{
				wchar uch = 0;
				int i;
				for(i = 0; i < 4 && pos <= lg; i++)
				{
					if ((*ptr < '0' || *ptr > '9')
						&& (*ptr < 'a' || *ptr > 'f')
						&& (*ptr < 'A' || *ptr > 'F')) break;

					uch <<= 4;
					if (*ptr >= '0' && *ptr <= '9') uch += uchar(uint(*ptr) - uint('0'));
					else if (*ptr >= 'a' && *ptr <= 'f') uch += uchar(uint(*ptr) - uint('a') + 10);
					else uch += uchar(uint(*ptr) - uint('A') + 10);
					ptr++;
					pos++;
				}
				if (i > 0)
				{
					char str[8];
					int n = ::WideCharToMultiByte(cp, 0, &uch, 1, str, sizeof(str),NULL,NULL);
					if (n > 0)
						pBuffer->Add(str,n);
					lastpos = pos;
					continue;
				}
			}
			break;
		default:
			if (ch == escapeChar)
				break;

			if ((ch < '0' || ch > '9')
				&& (ch < 'a' || ch > 'f')
				&& (ch < 'A' || ch > 'F')) break;
			if (ch >= '0' && ch <= '9') ch = uchar(uint(ch) - uint('0'));
			else if (ch >= 'a' && ch <= 'f') ch = uchar(uint(ch) - uint('a') + 10);
			else ch = uchar(uint(ch) - uint('A') + 10);
			if (pos > lg)
				break;
			if ((*ptr < '0' || *ptr > '9')
				&& (*ptr < 'a' || *ptr > 'f')
				&& (*ptr < 'A' || *ptr > 'F')) break;
			ch <<= 4;
			if (*ptr >= '0' && *ptr <= '9') ch += uchar(uint(*ptr) - uint('0'));
			else if (*ptr >= 'a' && *ptr <= 'f') ch += uchar(uint(*ptr) - uint('a') + 10);
			else ch += uchar(uint(*ptr) - uint('A') + 10);
			ptr++;
			pos++;
			break;
		}
		pBuffer->Add((LPCSTR)&ch,1);
		lastpos = pos;
	}

	if (lastpos <= 0)
		return false;
	if (lastpos <= lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos + 1);
	return true;
}


/********************************************************************************
         Html escape/unescape (&xxx;)
 ********************************************************************************/
struct xbHtmlEscapeSt
{
	LPCWSTR	pWname, pWch;
	LPCSTR	pName, pCh;
	bool	bUsable;
	LPCSTR	pLabel;
} g_xbHtmlEscapeList[] =
{
	L"AElig",		L"Æ",	"AElig",	"Æ",	true, NULL,
	L"Aacute",		L"Á",	"Aacute",	"Á",	true, NULL,
	L"Acirc",		L"Â",	"Acirc",	"Â",	true, NULL,
	L"Agrave",		L"À",	"Agrave",	"À",	true, NULL,
	L"Aring",		L"Å",	"Aring",	"Å",	true, NULL,
	L"Atilde",		L"Ã",	"Atilde",	"Ã",	true, NULL,
	L"Auml",		L"Ä",	"Auml",		"Ä",	true, NULL,
	L"Ccedil",		L"Ç",	"Ccedil",	"Ç",	true, NULL, 
	L"ETH",			L"Ð",	"ETH",		"Ð",	true, NULL,
	L"Eacute",		L"É",	"Eacute",	"É",	true, NULL,
	L"Ecirc",		L"Ê",	"Ecirc",	"Ê",	true, NULL,
	L"Egrave",		L"È",	"Egrave",	"È",	true, NULL,
	L"Euml",		L"Ë",	"Euml",		"Ë",	true, NULL,
	L"Iacute",		L"Í",	"Iacute",	"Í",	true, NULL,
	L"Icirc",		L"Î",	"Icirc",	"Î",	true, NULL,
	L"Igrave",		L"Ì",	"Igrave",	"Ì",	true, NULL,
	L"Iuml",		L"Ï",	"Iuml",		"Ï",	true, NULL,
	L"Ntilde",		L"Ñ",	"Ntilde",	"Ñ",	true, NULL,
	L"Oacute",		L"Ó",	"Oacute",	"Ó",	true, NULL,
	L"Ocirc",		L"Ô",	"Ocirc",	"Ô",	true, NULL,
	L"Ograve",		L"Ò",	"Ograve",	"Ò",	true, NULL,
	L"Oslash",		L"Ø",	"Oslash",	"Ø",	true, NULL,
	L"Otilde",		L"Õ",	"Otilde",	"Õ",	true, NULL,
	L"Ouml",		L"Ö",	"Ouml",		"Ö",	true, NULL,
	L"THORN",		L"Þ",	"THORN",	"Þ",	true, NULL,
	L"Uacute",		L"Ú",	"Uacute",	"Ú",	true, NULL,
	L"Ucirc",		L"Û",	"Ucirc",	"Û",	true, NULL,
	L"Ugrave",		L"Ù",	"Ugrave",	"Ù",	true, NULL,
	L"Uuml",		L"Ü",	"Uuml",		"Ü",	true, NULL,
	L"Yacute",		L"Ý",	"Yacute",	"Ý",	true, NULL,
	L"aacute",		L"á",	"aacute",	"á",	true, NULL, 
	L"acirc",		L"â",	"acirc",	"â",	true, NULL,
	L"aelig",		L"æ",	"aelig",	"æ",	true, NULL,
	L"agrave",		L"à",	"agrave",	"à",	true, NULL,
	L"amp",			L"&",	"amp",		"&",	true, NULL,
	L"apos",		L"'",	"apos",		"'",	false, NULL,
	L"aring",		L"å",	"aring",	"å",	true, NULL,
	L"ast",			L"*",	"ast",		"*",	false, NULL,
	L"atilde",		L"ã",	"atilde",	"ã",	true, NULL,
	L"auml",		L"ä",	"auml",		"ä",	true, NULL,
	L"brvbar",		L"¦",	"brvbar",	"¦",	true, NULL,
	L"ccedil",		L"ç",	"ccedil",	"ç",	true, NULL,
	L"cent",		L"¢",	"cent",		"¢",	true, NULL,
	L"colon",		L":",	"colon",	":",	false, NULL,
	L"comma",		L",",	"comma",	",",	false, NULL,
	L"commat",		L",",	"commat",	",",	false, NULL,
	L"copy",		L"©",	"copy",		"©",	true, NULL,
	L"deg",			L"°",	"deg",		"°",	true, NULL,
	L"dollar",		L"$",	"dollar",	"$",	false, NULL,
	L"eacute",		L"é",	"eacute",	"é",	true, NULL,
	L"ecirc",		L"ê",	"ecirc",	"ê",	true, NULL,
	L"egrave",		L"è",	"egrave",	"è",	true, NULL,
	L"emsp",		NULL,	"emsp",		NULL,	false, NULL,
	L"ensp",		NULL,	"ensp",		NULL,	false, NULL,
	L"equals",		L"=",	"equals",	"=",	false, NULL,
	L"eth",			L"ð",	"eth",		"ð",	true, NULL,
	L"euml",		L"ë",	"euml",		"ë",	true, NULL,
	L"excl",		NULL,	"excl",		NULL,	false, NULL,
	L"frac12",		L"½",	"frac12",	"½",	true, NULL,
	L"frac14",		L"¼",	"frac14",	"¼",	true, NULL,
	L"frac34",		L"¾",	"frac34",	"¾",	true, NULL,
	L"frac18",		NULL,	"frac18",	NULL,	false, NULL,
	L"frac38",		NULL,	"frac38",	NULL,	false, NULL,
	L"frac58",		NULL,	"frac58",	NULL,	false, NULL,
	L"frac78",		NULL,	"frac78",	NULL,	false, NULL,
	L"gt",			L">",	"gt",		">",	true, NULL,
	L"half",		L"½",	"half",		"½",	false, NULL,
	L"hyphen",		L"-",	"hyphen",	"-",	false, NULL,
	L"iacute",		L"í",	"iacute",	"í",	true, NULL, 
	L"icirc",		L"î",	"icirc",	"î",	true, NULL,
	L"iexcl",		L"¡",	"iexcl",	"¡",	true, NULL,
	L"igrave",		L"ì",	"igrave",	"ì",	true, NULL,
	L"iquest",		L"¿",	"iquest",	"¿",	true, NULL,
	L"iuml",		L"ï",	"iuml",		"ï",	true, NULL,
	L"laquo",		L"«",	"laquo",	"«",	true, NULL,
	L"lpar",		NULL,	"lpar",		NULL,	false, NULL,
	L"lsqb",		NULL,	"lsqb",		NULL,	false, NULL,
	L"lt",			L"<",	"lt",		"<",	true, NULL,
	L"mdash",		NULL,	"mdash",	NULL,	false, NULL,
	L"micro",		L"µ",	"micro",	"µ",	true, NULL,
	L"middot",		L"·" ,	"middot",	"·" ,	true, NULL,
	L"nbsp",		L"\xA0","nbsp",		"\xA0",	true, NULL,
	L"ndash",		L"–",	"ndash",	"–",	false, NULL,
	L"not",			L"¬",	"not",		"¬",	true, NULL,
	L"ntilde",		L"ñ",	"ntilde",	"ñ",	true, NULL,
	L"oacute",		L"ó",	"oacute",	"ó",	true, NULL,
	L"ocirc",		L"ô",	"ocirc",	"ô",	true, NULL,
	L"ograve",		L"ò",	"ograve",	"ò",	true, NULL,
	L"oslash",		L"ø" ,	"oslash",	"ø" ,	true, NULL,
	L"otilde",		L"õ" ,	"otilde",	"õ" ,	true, NULL,
	L"ouml",		L"ö",	"ouml",		"ö",	true, NULL,
	L"para",		L"¶",	"para",		"¶",	true, NULL,
	L"percnt",		L"%" ,	"percnt",	"%" ,	false, NULL,
	L"period",		L"." ,	"period",	"." ,	false, NULL,
	L"plus",		L"+",	"plus",		"+",	false, NULL,
	L"plusmn",		L"±",	"plusmn",	"±",	true, NULL,
	L"pound",		L"£",	"pound",	"£",	true, NULL,
	L"quest",		L"?",	"quest",	"?",	false, NULL,
	L"quot",		L"\"",	"quot",		"\"",	true, NULL,
	L"raquo",		L"»",	"raquo",	"»",	true, NULL,
	L"reg",			L"®",	"reg",		"®",	true, NULL,
	L"rpar",		NULL,	"rpar",		NULL,	false, NULL,
	L"rsqb",		NULL,	"rsqb",		NULL,	false, NULL,
	L"sect",		L"§",	"sect",		"§",	false, NULL,
	L"semi",		L";",	"semi",		";",	false, NULL,
	L"shy",			L"\xAD","shy",		"\xAD",	true, NULL,
	L"sup1",		L"¹",	"sup1",		"¹",	true, NULL,
	L"sup2",		L"²",	"sup2",		"²",	true, NULL,
	L"sup3",		L"³",	"sup3",		"³",	true, NULL,
	L"szlig",		L"ß",	"szlig",	"ß",	true, NULL,
	L"thorn",		L"þ",	"thorn",	"þ",	true, NULL,
	L"tilde",		L"~",	"tilde",	"~",	true, NULL,
	L"trade",		L"\x2122",	"trade",	NULL,	false, NULL,
	L"uacute",		L"ú",	"uacute",	"ú",	true, NULL,
	L"ucirc",		L"û",	"ucirc",	"û",	true, NULL,
	L"ugrave",		L"ù",	"ugrave",	"ù",	true, NULL,
	L"uuml",		L"ü",	"uuml",		"ü",	true, NULL,
	L"yacute",		L"ý",	"yacute",	"ý",	true, NULL,
	L"yen",			L"¥",	"yen",		"¥",	true, NULL,
	L"yuml",		L"ÿ",	"yuml",		"ÿ",	true, NULL,
	L"verbar",		NULL,	"verbar",	NULL,	false, NULL,
	NULL,		NULL,	NULL,		NULL,	true, NULL
};

struct xbHtmlEscape
{
	xbHtmlEscape()
	{
		seqNb = 0;
		const xbHtmlEscapeSt *pSt = pSeqList = g_xbHtmlEscapeList;
		memset(seqNo,0,sizeof(seqNo));
		memset(seqNoW,0,sizeof(seqNoW));
		int no = 0;
		while(pSt->pName)
		{
			if (pSt->bUsable)
			{
				if (pSt->pCh)
				{
					int sn = int(*(uchar*)(pSt->pCh));
					seqNo[sn] = pSt;
				}
				if (pSt->pWch)
				{
					int sn = int(*(wchar*)(pSt->pWch));
					if (sn < 256)
						seqNoW[sn] = pSt;
				}
			}
			pSt++;
			no++;
		}
		seqNb = no;
	}
	bool HasSeqW(wchar ch,const wchar **ppSeqName) const
	{
		if (ch >= 256)
			return false;
		if (seqNoW[ch]==NULL)
			return false;
	
		if (ppSeqName)
			*ppSeqName = seqNoW[ch]->pWname;
		return true;
	}
	bool HasSeq(uchar ch,const char **ppSeqName) const
	{
		if (seqNo[ch]==NULL)
			return false;
	
		if (ppSeqName)
			*ppSeqName = seqNo[ch]->pName;
		return true;
	}
	const xbHtmlEscapeSt *GetSeqW(const wchar *tofind,int lgfind) const
	{
		long Cmp=1, lg;
		const xbHtmlEscapeSt *hi = pSeqList + seqNb - 1;
		const xbHtmlEscapeSt *lo = pSeqList,*ptr=pSeqList;
		while (lo <= hi)
		{
			ptr = lo + (((xbNptr(hi) - xbNptr(lo))/sizeof(xbHtmlEscapeSt)) / 2);
			lg = long(wcslen(ptr->pWname));
			Cmp = wcsncmp(ptr->pWname,tofind,lgfind>lg?lg:lgfind);
			if (Cmp == 0)
				Cmp = lg - lgfind;
			if (Cmp > 0) hi = ptr - 1;
			else if (!Cmp) break;
			else lo = ptr + 1;
		}
	
		if (Cmp) 
			return NULL;

		return ptr;
	}
	const xbHtmlEscapeSt *GetSeq(const char *tofind,int lgfind) const
	{
		long Cmp=1, lg;
		const xbHtmlEscapeSt *hi = pSeqList + seqNb - 1;
		const xbHtmlEscapeSt *lo = pSeqList,*ptr=pSeqList;
		while (lo <= hi)
		{
			ptr = lo + (((xbNptr(hi) - xbNptr(lo))/sizeof(xbHtmlEscapeSt)) / 2);
			lg = long(strlen(ptr->pName));
			Cmp = strncmp(ptr->pName,tofind,lgfind>lg?lg:lgfind);
			if (Cmp == 0)
				Cmp = lg - lgfind;
			if (Cmp > 0) hi = ptr - 1;
			else if (!Cmp) break;
			else lo = ptr + 1;
		}
	
		if (Cmp) 
			return NULL;

		return ptr;
	}
	const xbHtmlEscapeSt	*seqNo[256];
	const xbHtmlEscapeSt	*seqNoW[256];
	const xbHtmlEscapeSt	*pSeqList;
	int						seqNb;
} g_xbHtmlEscape;


bool xbHtmlEscape(LPCWSTR pStr,int lg,xbBSTR *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,bool bLFtoo)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar ch[16];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != L'"' && *ptr!=L'\''
			&& *ptr != L'>'  && *ptr != L'<'
			&& *ptr != L'&'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);
		if (*ptr == L'\n' || (pos < lg && *ptr == L'\r' && *(ptr+1) == L'\n'))
		{
			if (bLFtoo)
				pbstr->Add(L"<br>\r\n",6);
			else
				pbstr->Add(L"\r\n",2);
			if (*ptr == '\r')
			{
				pos++;
				ptr++;
			}
			lastpos = ++pos;
			ptr++;
			continue;
		}
		const wchar *pseq;
		int chlg;
		ch[0] = L'&';
		if (g_xbHtmlEscape.HasSeqW(*ptr,&pseq))
		{
			chlg = int(wcslen(pseq));
			if (chlg > sizeof(ch) - sizeof(wchar)*2)
				chlg = sizeof(ch) - sizeof(wchar)*2;
			wcsncpy(ch+1,pseq,chlg);
			chlg++;
		}
		else
		{
			ch[1] = L'#';
			wchar dg[5];
			chlg = 0;
			wchar val = *ptr;
			while(val > 0)
			{
				dg[chlg++] = (val % 10);
				val /= 10;
			}
			if (chlg == 0)
			{
				ch[2] = L'0';
				chlg = 3;
			}
			else
			{
				for(int i = 1; i <= chlg; i++)
					ch[i+1] = dg[chlg-i] + L'0';
				chlg += 2;
			}
		}
		ch[chlg++] = ';';
		pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);

	return true;
}

bool xbHtmlEscape(LPCWSTR pStr,int lg,xbStringW *pbstr,LPCWSTR pEncodeInclude,LPCWSTR pEncodeExclude,bool bLFtoo)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar ch[16];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != L'"' && *ptr!=L'\''
			&& *ptr != L'>'  && *ptr != L'<'
			&& *ptr != L'&'
			&& (!pEncodeInclude || wcschr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && wcschr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pbstr->Add(pStr+lastpos,pos - lastpos);
		if (*ptr == L'\n' || (pos < lg && *ptr == L'\r' && *(ptr+1) == L'\n'))
		{
			if (bLFtoo)
				pbstr->Add(L"<br>\r\n",6);
			else
				pbstr->Add(L"\r\n",2);
			if (*ptr == '\r')
			{
				pos++;
				ptr++;
			}
			lastpos = ++pos;
			ptr++;
			continue;
		}
		const wchar *pseq;
		int chlg;
		ch[0] = L'&';
		if (g_xbHtmlEscape.HasSeqW(*ptr,&pseq))
		{
			chlg = int(wcslen(pseq));
			if (chlg > sizeof(ch) - sizeof(wchar)*2)
				chlg = sizeof(ch) - sizeof(wchar)*2;
			wcsncpy(ch+1,pseq,chlg);
			chlg++;
		}
		else
		{
			ch[1] = L'#';
			wchar dg[5];
			chlg = 0;
			wchar val = *ptr;
			while(val > 0)
			{
				dg[chlg++] = (val % 10);
				val /= 10;
			}
			if (chlg == 0)
			{
				ch[2] = L'0';
				chlg = 3;
			}
			else
			{
				for(int i = 1; i <= chlg; i++)
					ch[i+1] = dg[chlg-i] + L'0';
				chlg += 2;
			}
		}
		ch[chlg++] = ';';
		pbstr->Add(ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);

	return true;
}

bool xbHtmlEscape(const char *pStr,int lg,xbStringA *pBuffer,LPCSTR pEncodeInclude,LPCSTR pEncodeExclude,bool bLFtoo)
{
	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;
	uchar ch[16];

	while(pos < lg)
	{
		if ((*ptr >= 0x20 && *ptr <= 0x7F && *ptr != '"' && *ptr!='\''
			&& *ptr != '>'  && *ptr != '<'
			&& *ptr != '&'
			&& (!pEncodeInclude || strchr(pEncodeInclude,*ptr) == NULL))
			|| (pEncodeExclude && strchr(pEncodeExclude,*ptr) != NULL)
			)
		{
			pos++;
			ptr++;
			continue;
		}
		if (lastpos<0) lastpos = 0;
		if (lastpos < pos)
			pBuffer->Add(pStr+lastpos,pos - lastpos);
		if (*ptr == '\n' || (pos < lg && *ptr == '\r' && *(ptr+1) == '\n'))
		{
			if (bLFtoo)
				pBuffer->Add("<br>\r\n",6);
			else
				pBuffer->Add("\r\n",2);
			if (*ptr == '\r')
			{
				pos++;
				ptr++;
			}
			lastpos = ++pos;
			ptr++;
			continue;
		}
		const char *pseq;
		int chlg;
		ch[0] = '&';
		if (g_xbHtmlEscape.HasSeq(*ptr,&pseq))
		{
			chlg = int(strlen(pseq));
			if (chlg > sizeof(ch) - sizeof(uchar)*2)
				chlg = sizeof(ch) - sizeof(uchar)*2;
			strncpy((LPSTR)ch+1,pseq,chlg);
			chlg++;
		}
		else
		{
			ch[1] = '#';
			uchar dg[5];
			chlg = 0;
			uchar val = *ptr;
			while(val > 0)
			{
				dg[chlg++] = (val % 10);
				val /= 10;
			}
			if (chlg == 0)
			{
				ch[2] = '0';
				chlg = 3;
			}
			else
			{
				for(int i = 1; i <= chlg; i++)
					ch[i+1] = dg[chlg-i] + '0';
				chlg+=2;
			}
		}
		ch[chlg++] = ';';
		pBuffer->Add((LPCSTR)ch,chlg);
		lastpos = ++pos;
		ptr++;
	}
	if (lastpos <= 0)
		return false;
	if (lastpos < lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos);

	return true;
}

bool xbHtmlUnescape(LPCWSTR pStr,int lg,xbBSTR *pbstr,bool bLFtoo)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		ch;
	bool		bInSeq = false;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch == L'&')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pbstr->Add(pStr + lastpos,pos - lastpos - 1);
			lastpos = pos - 1;
			bInSeq = true;
			continue;
		}
		if (!bInSeq)
		{
			if (!bLFtoo || (ch != L'\r' && ch != L'\n' && ch != L'<'))
				continue;
			if (ch == '<')
			{
				if (pos+3>lg)
					continue;
				if (xbWcsnicmp(ptr,L"br>",3) != 0)
					continue;
				if (lastpos<0) lastpos = 0;
				if (lastpos < pos - 1)
					pbstr->Add(pStr + lastpos,pos - lastpos - 1);
				pbstr->Add(L"\r\n",2);
				ptr+=3;
				pos+=3;
				lastpos = pos;
				continue;
			}
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pbstr->Add(pStr + lastpos,pos - lastpos - 1);
			if (ch == L'\r')
			{
				if (pos+1<=lg && *ptr == L'\n')
				{
					ptr++;
					pos++;
				}
			}
			pbstr->Add(L" ",1);
			lastpos = pos;
			continue;
		}
		if (iswalnum(ch) || (pos == (lastpos+2) && ch == L'#'))
		{
			if (pos - lastpos > 12)
				bInSeq = false;
			continue;
		}
		if (ch != L';' || lastpos >= pos - 2)
		{
/*			ptr--;
			pos--;*/
			bInSeq = false;
			continue;
		}
		if (lastpos <= pos - 3 && *(pStr+lastpos+1) == L'#')
		{
			wchar uch = 0;
			const wchar *pSeq = pStr+lastpos+2;
			const wchar *pSeqEnd = pStr+pos-1;
			while(pSeq < pSeqEnd)
			{
				if (*pSeq < L'0' || *pSeq > L'9')
					break;
				uch *= 10;
				uch += wchar(uint(*pSeq) - uint(L'0'));
				pSeq++;
			}
			if (pSeq < pSeqEnd)
			{
				bInSeq = false;
				continue;
			}
			pbstr->Add(&uch,1);
			lastpos = pos;
			bInSeq = false;
			continue;
		}
		const xbHtmlEscapeSt *pEsc = g_xbHtmlEscape.GetSeqW(pStr+lastpos+1,pos-lastpos-2);
		if (!pEsc)
		{
			bInSeq = false;
			continue;
		}

		if (pEsc->pWch)
			pbstr->Add(pEsc->pWch);
		lastpos = pos;
		bInSeq = false;
	}

	if (lastpos <= 0)
		return false;

	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbHtmlUnescape(LPCWSTR pStr,int lg,xbStringW *pbstr,bool bLFtoo)
{
	const wchar *ptr = pStr;
	int			pos = 0,lastpos = -1;
	wchar		ch;
	bool		bInSeq = false;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch == L'&')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pbstr->Add(pStr + lastpos,pos - lastpos - 1);
			lastpos = pos - 1;
			bInSeq = true;
			continue;
		}
		if (!bInSeq)
		{
			if (!bLFtoo || (ch != L'\r' && ch != L'\n' && ch != L'<'))
				continue;
			if (ch == '<')
			{
				if (pos+3>lg)
					continue;
				if (xbWcsnicmp(ptr,L"br>",3) != 0)
					continue;
				if (lastpos<0) lastpos = 0;
				if (lastpos < pos - 1)
					pbstr->Add(pStr + lastpos,pos - lastpos - 1);
				pbstr->Add(L"\r\n",2);
				ptr+=3;
				pos+=3;
				lastpos = pos;
				continue;
			}
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pbstr->Add(pStr + lastpos,pos - lastpos - 1);
			if (ch == L'\r')
			{
				if (pos+1<=lg && *ptr == L'\n')
				{
					ptr++;
					pos++;
				}
			}
			pbstr->Add(L" ",1);
			lastpos = pos;
			continue;
		}
		if (iswalnum(ch) || (pos == (lastpos+2) && ch == L'#'))
		{
			if (pos - lastpos > 12)
				bInSeq = false;
			continue;
		}
		if (ch != L';' || lastpos >= pos - 2)
		{
/*			ptr--;
			pos--;*/
			bInSeq = false;
			continue;
		}
		if (lastpos <= pos - 3 && *(pStr+lastpos+1) == L'#')
		{
			wchar uch = 0;
			const wchar *pSeq = pStr+lastpos+2;
			const wchar *pSeqEnd = pStr+pos-1;
			while(pSeq < pSeqEnd)
			{
				if (*pSeq < L'0' || *pSeq > L'9')
					break;
				uch *= 10;
				uch += wchar(uint(*pSeq) - uint(L'0'));
				pSeq++;
			}
			if (pSeq < pSeqEnd)
			{
				bInSeq = false;
				continue;
			}
			pbstr->Add(&uch,1);
			lastpos = pos;
			bInSeq = false;
			continue;
		}
		const xbHtmlEscapeSt *pEsc = g_xbHtmlEscape.GetSeqW(pStr+lastpos+1,pos-lastpos-2);
		if (!pEsc)
		{
			bInSeq = false;
			continue;
		}

		if (pEsc->pWch)
			pbstr->Add(pEsc->pWch);
		lastpos = pos;
		bInSeq = false;
	}

	if (lastpos <= 0)
		return false;

	if (lastpos < lg)
		pbstr->Add(pStr+lastpos,lg - lastpos);
	return true;
}

bool xbHtmlUnescape(const char *pStr,int lg,xbStringA *pBuffer,bool bLFtoo,ulong cp)
{
	const uchar *ptr = (const uchar*)pStr;
	int			pos = 0,lastpos = -1;
	uchar		ch;
	bool		bInSeq = false;

	while(pos < lg)
	{
		ch = *(ptr++);
		pos++;
		if (ch == '&')
		{
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
			lastpos = pos - 1;
			bInSeq = true;
			continue;
		}
		if (!bInSeq)
		{
			if (!bLFtoo || (ch != '\r' && ch != '\n' && ch != '<'))
				continue;
			if (ch == '<')
			{
				if (pos+3>lg)
					continue;
				if (xbStrnicmp((LPCSTR)ptr,"br>",3) != 0)
					continue;
				if (lastpos<0) lastpos = 0;
				if (lastpos < pos - 1)
					pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
				pBuffer->Add("\r\n",2);
				ptr+=3;
				pos+=3;
				lastpos = pos;
				continue;
			}
			if (lastpos<0) lastpos = 0;
			if (lastpos < pos - 1)
				pBuffer->Add(pStr + lastpos,pos - lastpos - 1);
			if (ch == '\r')
			{
				if (pos+1<=lg && *ptr == '\n')
				{
					ptr++;
					pos++;
				}
			}
			pBuffer->Add(" ",1);
			lastpos = pos;
			continue;
		}
		if (isalnum(ch) || (pos == (lastpos+2) && ch == '#'))
		{
			if (pos - lastpos > 12)
				bInSeq = false;
			continue;
		}
		if (ch != ';' || lastpos >= pos - 2)
		{
/*			ptr--;
			pos--;*/
			bInSeq = false;
			continue;
		}
		if (lastpos <= pos - 3 && *(pStr+lastpos+1) == '#')
		{
			wchar uch = 0;
			const char *pSeq = pStr+lastpos+2;
			const char *pSeqEnd = pStr+pos-1;
			while(pSeq < pSeqEnd)
			{
				if (*pSeq < '0' || *pSeq > '9')
					break;
				uch *= 10;
				uch += uchar(uint(*pSeq) - uint('0'));
				pSeq++;
			}
			if (pSeq < pSeqEnd)
			{
				bInSeq = false;
				continue;
			}

			char str[8];
			int n = ::WideCharToMultiByte(cp, 0, &uch, 1, str, sizeof(str),NULL,NULL);
			if (n > 0)
				pBuffer->Add(str,n);
			lastpos = pos;
			bInSeq = false;
			continue;
		}
		const xbHtmlEscapeSt *pEsc = g_xbHtmlEscape.GetSeq(pStr+lastpos+1,pos-lastpos-2);
		if (!pEsc)
		{
			bInSeq = false;
			continue;
		}

		if (pEsc->pCh)
			pBuffer->Add(pEsc->pCh,int(strlen(pEsc->pCh)));
		lastpos = pos;
		bInSeq = false;
	}

	if (lastpos <= 0)
		return false;

	if (lastpos <= lg)
		pBuffer->Add(pStr+lastpos,lg - lastpos);
	return true;
}

#pragma warning(pop)
