/*<<<***************************************************************************
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: xbString.cpp
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#ifdef xbSTRING_DEFW
#define _xbSD(name)		name ## W
#define _xbCHAR			wchar
#define _xbUCHAR		wchar
#define _xbSL(str)		L ## str
#define _xbBYTEtoCHAR(cByte) (cByte/sizeof(wchar))
#define _xbPCSTR		const wchar *
#define _xbPSTR			wchar *
#else
//xbSTRING_DEFA
#define _xbSD(name)		name ## A
#define _xbCHAR			char
#define _xbUCHAR		uchar
#define _xbSL(str)		str
#define _xbBYTEtoCHAR(cByte) (cByte)
#define _xbPCSTR		const char *
#define _xbPSTR			char *
#endif

#pragma warning(push)
#pragma warning(disable: 4200) //extension non standard utilisée : tableau de taille zéro dans un struct/union
#pragma warning(disable: 4311) //troncation de pointeur de 'wchar *' à 'xbNptr'

#define _xbSTR _xbSD(xbString)
#define _xbSTRDATA _xbSD(xbStringData)

int _xbSD(xbStrToDouble)(const _xbCHAR *str,double *pNb,const _xbCHAR **ppStop)
{
	_xbCHAR *pchStop = NULL;
	double vbl = _xbSD(::xbStrtod)(str,(_xbCHAR**)&pchStop);
	if (pNb)
		*pNb = vbl;
	if (ppStop)
		*ppStop = pchStop;
	if (pchStop == NULL)
		return -1;
	
	return (int)(((xbNptr)pchStop - (xbNptr)str)/sizeof(_xbCHAR));
}

_xbSTR _xbSD(xbStrFromDouble)(double val,xbStringNumPrec prec)
{//cf fcvt
	_xbCHAR buf[64];
	if (prec <= xbStringNumPrec_full)
		_xbSD(xbSnprintf)(buf,_xbBYTEtoCHAR(sizeof(buf)),_xbSL("%lf"),val);
	else
		_xbSD(xbSnprintf)(buf,_xbBYTEtoCHAR(sizeof(buf)),_xbSL("%.*lf"),(int)prec,val);
	return buf;
}

_xbSTR xbEXPORT _xbSD(xbStrFromGuid)(const GUID &id,bool bAccolades)
{
	_xbCHAR buf[128];

	_xbSD(xbSnprintf)(buf,sizeof(buf),
		bAccolades?_xbSL("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}")
		:_xbSL("%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"),
		id.Data1,id.Data2,id.Data3,id.Data4[0],id.Data4[1],
		id.Data4[2],id.Data4[3],id.Data4[4],id.Data4[5],id.Data4[6],id.Data4[7]);
	return buf;
}

int _xbSTR::IndexOf(_xbCHAR ch) const
{
	if (!m_pData)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pChEnd = pChBegin + m_pData->m_cChar; 
	_xbPCSTR pCh = pChBegin; 
	while(pCh < pChEnd)
	{
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
		pCh++;
	}
	return -1;
}
// find character starting at right
int _xbSTR::LastIndexOf(_xbCHAR ch) const
{
	if (!m_pData)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pCh = pChBegin + m_pData->m_cChar; 
	while(pCh > pChBegin)
	{
		pCh--;
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
	}
	return -1;
}

int _xbSTR::IndexOf(_xbCHAR ch,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::Find(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pChEnd = pChBegin + m_pData->m_cChar; 
	_xbPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
		pCh++;
	}
	return -1;
}
// find character starting at right
int _xbSTR::LastIndexOf(_xbCHAR ch,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::ReverseFind(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pCh = pChBegin + min(m_pData->m_cChar,start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (*pCh == ch)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
	}
	return -1;
}

// find first instance of any character in passed string
int _xbSTR::IndexOfChars(_xbPCSTR lpszCharSet,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::IndexOfChars(start=%d) : bad index",start);
	if (!m_pData || !lpszCharSet)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pChEnd = pChBegin + m_pData->m_cChar; 
	_xbPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		_xbPCSTR pSep = lpszCharSet; 
		while(*pSep)
		{
			if (*pCh == *pSep)
				return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
			pSep++;
		}
		pCh++;
	}
	return -1;
}
int _xbSTR::LastIndexOfChars(_xbPCSTR lpszCharSet,int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::LastIndexOfChars(start=%d) : bad index",start);
	if (!m_pData || !lpszCharSet)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pCh = pChBegin + min(m_pData->m_cChar,start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		_xbPCSTR pSep = lpszCharSet; 
		while(*pSep)
		{
			if (*pCh == *pSep)
				return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
			pSep++;
		}
	}
	return -1;
}

// find first instance of substring starting at zero-based index
int _xbSTR::IndexOf(_xbPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::IndexOf(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?_xbSD(xbStrlen)(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pChEnd = pChBegin + m_pData->m_cChar - cChSub + 1; 
	_xbPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		if (_xbSD(xbStrncmp)(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
		pCh++;
	}
	return -1;
}

int _xbSTR::LastIndexOf(_xbPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::LastIndexOf(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?_xbSD(xbStrlen)(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pCh = pChBegin + min(m_pData->m_cChar-cChSub+1,start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (_xbSD(xbStrncmp)(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
	}
	return -1;
}

int _xbSTR::IndexOfNC(_xbPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::IndexOfNC(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?_xbSD(xbStrlen)(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pChEnd = pChBegin + m_pData->m_cChar - cChSub + 1; 
	_xbPCSTR pCh = pChBegin + start; 
	while(pCh < pChEnd)
	{
		if (_xbSD(xbStrnicmp)(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
		pCh++;
	}
	return -1;
}

int _xbSTR::LastIndexOfNC(_xbPCSTR lpszSub,int cChSub, int start) const
{
	if (start < 0) 
		xbThrowEargFmt("xbString::LastIndexOfNC(start=%d) : bad index",start);
	if (!m_pData)
		return -1;
	if (cChSub < 0)
		cChSub = (int)(lpszSub?_xbSD(xbStrlen)(lpszSub):0);
	if (cChSub <= 0)
		return -1;
	_xbPCSTR pChBegin = m_pData->m_str; 
	_xbPCSTR pCh = pChBegin + min(m_pData->m_cChar-cChSub+1,start+1); 
	while(pCh > pChBegin)
	{
		pCh--;
		if (_xbSD(xbStrnicmp)(pCh,lpszSub,cChSub)==0)
			return (int)(((xbNptr)pCh - (xbNptr)pChBegin ) / sizeof(_xbCHAR));
	}
	return -1;
}


void _xbSTR::Pad(int cnt,_xbCHAR ch)
{
	int lg = GetLength();
	if (lg == cnt) 
		return;
	if (cnt < 0)
	{
		cnt = -cnt;
		if (cnt < lg)
			return;
	}

	setLength(cnt,ch);
}

void _xbSTR::PadLeft(int cnt,_xbCHAR ch)
{
	int lg = GetLength();
	if (lg == cnt) 
		return;
	if (cnt < 0)
	{
		cnt = -cnt;
		if (cnt < lg)
			return;
	}

	if (lg > cnt) 
		setLength(cnt);
	else
		Insert(0,ch,cnt - lg);
}

_xbSTR _xbSTR::RetPad(int cnt,_xbCHAR ch) const
{
	_xbSTR str(*this);
	str.Pad(cnt,ch);
	return str;
}

_xbSTR _xbSTR::RetPadLeft(int cnt,_xbCHAR ch) const
{
	_xbSTR str(*this);
	str.PadLeft(cnt,ch);
	return str;
}

int _xbSTR::Compare(_xbPCSTR lpsz) const
{
	if (IsEmpty())
	{
		if (!lpsz || !*lpsz)
			return 0;
		return -1;
	}
	if (!lpsz || !*lpsz)
		return 1;
	return _xbSD(xbStrcmp)(m_pData->m_str,lpsz);
}

int _xbSTR::Compare(const _xbSTR &str) const
{
	if (IsEmpty())
	{
		if (str.IsEmpty())
			return 0;
		return -1;
	}
	if (str.IsEmpty())
		return 1;
	return _xbSD(xbStrncmp)(m_pData->m_str,str,min(m_pData->m_cChar+1,str.Length+1));
}
// compare ignoring case
int _xbSTR::CompareNC(_xbPCSTR lpsz) const
{
	if (IsEmpty())
	{
		if (!lpsz || !*lpsz)
			return 0;
		return -1;
	}
	if (!lpsz || !*lpsz)
		return 1;
	return _xbSD(xbStricmp)(m_pData->m_str,lpsz);
}

int _xbSTR::CompareNC(const _xbSTR &str) const
{
	if (IsEmpty())
	{
		if (str.IsEmpty())
			return 0;
		return -1;
	}
	if (str.IsEmpty())
		return 1;
	return _xbSD(xbStrnicmp)(m_pData->m_str,str,min(m_pData->m_cChar+1,str.Length+1));
}
// NLS aware conversion to uppercase
void _xbSTR::Upper()
{
	if (!m_pData)
		return;
	OwnData();
	_xbSD(xbStrupr)(m_pData->m_str,m_pData->m_cChar+1);
}

_xbSTR _xbSTR::RetUpper() const
{
	_xbSTR str(*this);
	str.Upper();
	return str;
}
// NLS aware conversion to lowercase
void _xbSTR::Lower()
{
	if (!m_pData)
		return;
	OwnData();
	_xbSD(xbStrlwr)(m_pData->m_str,m_pData->m_cChar+1);
}
_xbSTR _xbSTR::RetLower() const
{
	_xbSTR str(*this);
	str.Lower();
	return str;
}

// advanced manipulation

// replace occurrences of chOld with chNew
int _xbSTR::Replace(_xbCHAR chOld, _xbCHAR chNew)
{
	int cnt = 0, pos = 0;
	while((pos = IndexOf(chOld,pos))>=0)
	{
		SetAt(pos,chNew);
		cnt++;
		pos++;
	}

	return cnt;
}

// replace occurrences of substring lpszOld with lpszNew;
// empty lpszNew removes instances of lpszOld
int _xbSTR::Replace(_xbPCSTR lpszOld, _xbPCSTR lpszNew)
{
	int oldLg = (int)(lpszOld?_xbSD(xbStrlen)(lpszOld):0);
	if (oldLg <= 0)
		return 0;
	int cnt = 0, pos = 0;
	while((pos = IndexOf(lpszOld,pos))>=0)
	{
		SetAt(pos,lpszNew,-1,oldLg);
		cnt++;
		pos++;
	}

	return cnt;
}
int _xbSTR::Replace(const _xbSTR &strOld, const _xbSTR &strNew)
{
	int oldLg = strOld.Length;
	if (oldLg <= 0)
		return 0;
	int cnt = 0, pos = 0;
	while((pos = IndexOf(strOld,pos))>=0)
	{
		SetAt(pos,strNew,strNew.Length,oldLg);
		cnt++;
		pos++;
	}

	return cnt;
}

// replace occurrences of substring lpszOld with lpszNew (NC);
int _xbSTR::ReplaceNC(_xbPCSTR lpszOld, _xbPCSTR lpszNew)
{
	int oldLg = (int)(lpszOld?_xbSD(xbStrlen)(lpszOld):0);
	if (oldLg <= 0)
		return 0;
	int cnt = 0, pos = 0;
	while((pos = IndexOfNC(lpszOld,pos))>=0)
	{
		SetAt(pos,lpszNew,-1,oldLg);
		cnt++;
		pos++;
	}

	return cnt;
}
int _xbSTR::ReplaceNC(const _xbSTR &strOld, const _xbSTR &strNew)
{
	int oldLg = strOld.Length;
	if (oldLg <= 0)
		return 0;
	int cnt = 0, pos = 0;
	while((pos = IndexOfNC(strOld,pos))>=0)
	{
		SetAt(pos,strNew,strNew.Length,oldLg);
		cnt++;
		pos++;
	}

	return cnt;
}

// remove occurrences of chRemove
// int Remove(_xbCHAR chRemove);

// simple formatting

// printf-like formatting using passed string
void _xbSTR::Format(_xbPCSTR lpszFormat, ...)
{
	va_list ap;
	va_start(ap, lpszFormat);
	Set(_xbSD(xbFormatV)(lpszFormat, ap));
	va_end(ap);
}

_xbSTR _xbSD(xbFormat)(_xbPCSTR lpszFormat, ...)
{
	va_list ap;
	va_start(ap, lpszFormat);
	_xbSTR ret(_xbSD(xbFormatV)(lpszFormat, ap));
	va_end(ap);
	return ret;
}

// printf-like formatting using variable arguments parameter
void _xbSTR::FormatV(_xbPCSTR lpszFormat, va_list ap)
{
	Set(_xbSD(xbFormatV)(lpszFormat, ap));
}

_xbSTR _xbSD(xbFormatV)(_xbPCSTR lpszFormat, va_list ap)
{
	int lg = 64,cChar;
	_xbSTR ret;
	for(;;)
	{
		cChar = _xbSD(xbVsnprintf)( ret.GetWritePtr(lg), lg, lpszFormat, ap );
		if (cChar>=0)
			break;
		lg *= 2;
	}
	ret.setLength(cChar);
	return ret;
}

bool _xbSD(xbStrToGuid)(const _xbCHAR *ptr,GUID *pId,const _xbCHAR **ppStop)
{
	if (!ptr) 
		return false;

	bool	bAccolades = false;
	while(*ptr 
		&& _xbSD(xbIsSpace)(*ptr)) 
		ptr++;
	
	if (*ptr == _xbSL('{')) 
	{
		bAccolades = true;
		ptr++;
		while(*ptr 
			&& _xbSD(xbIsSpace)(*ptr)) 
			ptr++;
	}
	
	const _xbCHAR *pEnd = NULL;
	ulong nb1;
	if (_xbSD(xbStrToUlong)(ptr,&nb1,16,&pEnd) <= 0)
		return false;
	if (!pEnd || *pEnd != _xbSL('-')) 
		return false;
	ptr = pEnd + 1;

	DWORD nb2;
	if (_xbSD(xbStrToUlong)(ptr,&nb2,16,&pEnd) <= 0)
		return false;
	if (!pEnd || *pEnd != _xbSL('-')) 
		return false;
	if (nb2 > 0xFFFF) 
		return false;
	ptr = pEnd + 1;

	DWORD nb3;
	if (_xbSD(xbStrToUlong)(ptr,&nb3,16,&pEnd) <= 0)
		return false;
	if (!pEnd || *pEnd != _xbSL('-')) 
		return false;
	if (nb3 > 0xFFFF) 
		return false;
	ptr = pEnd + 1;

	int ix = 0;
    uchar nb4[sizeof(pId->Data4)];
	while(ptr && *ptr)
	{
		int ch1 = *ptr;
		if (ch1 >= _xbSL('0') && ch1 <= _xbSL('9')) 
			ch1 -= _xbSL('0');
		else if (ch1 >= _xbSL('A') && ch1 <= _xbSL('F')) 
			ch1 -= _xbSL('A') - 10;
		else if (ch1 >= 'a' && ch1 <= 'f') 
			ch1 -= _xbSL('a') - 10;
		else 
			return false;
		
		int ch2 = *(ptr+1);
		if (ch2 >= _xbSL('0') && ch2 <= _xbSL('9')) 
			ch2 -= _xbSL('0');
		else if (ch2 >= _xbSL('A') && ch2 <= _xbSL('F')) 
			ch2 -= _xbSL('A') - 10;
		else if (ch2 >= _xbSL('a') 
			&& ch2 <= _xbSL('f')) 
			ch2 -= _xbSL('a') - 10;
		else 
			return false;

		nb4[ix++] = (ch1 << 4) + ch2;
		ptr+=2;
		if (ix >= sizeof(nb4)) 
			break;
		if (*ptr == _xbSL('-')) 
			ptr++;
	}
	if (ix != sizeof(nb4)) 
		return false;
	if (pId)
	{
		pId->Data1 = nb1;
		pId->Data2 = WORD(nb2);
		pId->Data3 = WORD(nb3);
		memcpy(pId->Data4,nb4,sizeof(pId->Data4));
	}
	if (ppStop || bAccolades)
	{
		while(*ptr && _xbSD(xbIsSpace)(*ptr)) 
			ptr++;
		
		if (bAccolades 
			&& *ptr == _xbSL('}')) 
		{
			ptr++;
			if (ppStop) 
			{
				while(*ptr && _xbSD(xbIsSpace)(*ptr)) 
					ptr++;
			}
		}
		if (ppStop) 
			*ppStop = ptr;
	}
	return true;	
}

//Bugs corrigés sur 5.0.3.69 / > 14/1/03 pb base + bad detection for overflow cases
int _xbSD(xbStrToInt64)(const _xbCHAR *ptr,int64 *pNb,int base,const _xbCHAR **ppStop)
{
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) 
		ptr++;
	bool bNegSign = false;
	if (*ptr == _xbSL('+')) 
		ptr++;
	else if (*ptr == _xbSL('-')) 
	{
		ptr++;
		bNegSign = true;
	}
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) 
		ptr++;
	if (base == 0)
	{
		if (*ptr == _xbSL('0'))
		{
			if (*(ptr+1) == _xbSL('x') || *(ptr+1) == _xbSL('X')) 
			{
				base = 16; 
				ptr += 2;
			}
			else if (*(ptr+1) == _xbSL('b') || *(ptr+1) == _xbSL('B')) 
			{
				base = 2; 
				ptr += 2;
			}
			else if (*(ptr+1) == _xbSL('o') || *(ptr+1) == _xbSL('O')) 
			{
				base = 8; 
				ptr += 2;
			}
			else 
				base = 10;
		}
		else 
			base = 10;
	}
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrToInt64() : base = %d is invalid",base);

	int64	nb = 0,oldnb;
	int		ret = 0;

	while(*ptr 
		&& _xbSD(xbIsSpace)(*ptr)) 
		ptr++;
	while(*ptr != 0)
	{
		int nDig;
		if (*ptr >= _xbSL('0') && *ptr <= _xbSL('9')) 
			nDig = int(*ptr) - int(_xbSL('0'));
		else if (*ptr >= _xbSL('A') && *ptr <= _xbSL('Z')) 
			nDig = int(*ptr) - int(_xbSL('A')) + 10;
		else if (*ptr >= _xbSL('a') && *ptr <= _xbSL('z')) 
			nDig = int(*ptr) - int(_xbSL('a')) + 10;
		else 
			nDig = 36;
		if (nDig >= base)
		{
			if (!_xbSD(xbIsSpace)(*ptr)) 
				break;
			ptr++;
			continue;
		}
		oldnb = nb;
		ptr++;
		nb *= base;
		nb += nDig;
		if (nb < oldnb) 
		{
			ret = -ret;
			break; //corrected overflow test
		}
		ret++;
	}

	if (ppStop) 
		*ppStop = ptr;
	if (pNb) 
		*pNb = bNegSign?-nb:nb;
	return ret;
}

//Bugs corrigés sur 5.0.3.69 / > 14/1/03 pb base + bad detection for overflow cases
int _xbSD(xbStrToUint64)(const _xbCHAR *ptr,uint64 *pNb,int base,const _xbCHAR **ppStop)
{
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	if (*ptr == _xbSL('+')) ptr++;
	else if (*ptr == _xbSL('-')) 
	{
		if (ppStop) *ppStop = ptr;
		return 0;
	}
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	if (base == 0)
	{
		if (*ptr == _xbSL('0'))
		{
			if (*(ptr+1) == _xbSL('x') || *(ptr+1) == _xbSL('X')) {base = 16; ptr += 2;}
			else if (*(ptr+1) == _xbSL('b') || *(ptr+1) == _xbSL('B')) {base = 2; ptr += 2;}
			else if (*(ptr+1) == _xbSL('o') || *(ptr+1) == _xbSL('O')) {base = 8; ptr += 2;}
			else base = 10;
		}
		else base = 10;
	}
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrToInt64() : base = %d is invalid",base);
	uint64	nb = 0,oldnb;
	int		ret = 0;

	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	while(*ptr != 0)
	{
		int nDig;
		if (*ptr >= _xbSL('0') && *ptr <= _xbSL('9')) nDig = int(*ptr) - int(_xbSL('0'));
		else if (*ptr >= _xbSL('A') && *ptr <= _xbSL('Z')) nDig = int(*ptr) - int(_xbSL('A')) + 10;
		else if (*ptr >= _xbSL('a') && *ptr <= _xbSL('z')) nDig = int(*ptr) - int(_xbSL('a')) + 10;
		else nDig = 36;
		if (nDig >= base)
		{
			if (!_xbSD(xbIsSpace)(*ptr)) 
				break;
			ptr++;
			continue;
		}
		oldnb = nb;
		ptr++;
		nb *= base;
		nb += uint64(nDig);
		if (nb < oldnb) 
		{
			ret = -ret;
			break; //corrected overflow test
		}
		ret++;
	}

	if (ppStop) *ppStop = ptr;
	if (pNb) *pNb = nb;
	return ret;
}

int _xbSD(xbStrToLong)(const _xbCHAR *ptr,long *pNb,int base,const _xbCHAR **ppStop)
{
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	bool bNegSign = false;
	if (*ptr == _xbSL('+')) ptr++;
	else if (*ptr == _xbSL('-')) {ptr++;bNegSign = true;}
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	if (base == 0)
	{
		if (*ptr == _xbSL('0'))
		{
			if (*(ptr+1) == _xbSL('x') || *(ptr+1) == _xbSL('X')) {base = 16; ptr += 2;}
			else if (*(ptr+1) == _xbSL('b') || *(ptr+1) == _xbSL('B')) {base = 2; ptr += 2;}
			else if (*(ptr+1) == _xbSL('o') || *(ptr+1) == _xbSL('O')) {base = 8; ptr += 2;}
			else base = 10;
		}
		else base = 10;
	}
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrToLong() : base = %d is invalid",base);
	long	nb = 0,oldnb;
	int		ret = 0;

	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	while(*ptr != 0)
	{
		int nDig;
		if (*ptr >= _xbSL('0') && *ptr <= _xbSL('9')) nDig = int(*ptr) - int(_xbSL('0'));
		else if (*ptr >= _xbSL('A') && *ptr <= _xbSL('Z')) nDig = int(*ptr) - int(_xbSL('A')) + 10;
		else if (*ptr >= _xbSL('a') && *ptr <= _xbSL('z')) nDig = int(*ptr) - int(_xbSL('a')) + 10;
		else nDig = 36;
		if (nDig >= base)
		{
			if (!_xbSD(xbIsSpace)(*ptr)) 
				break;
			ptr++;
			continue;
		}
		oldnb = nb;
		ptr++;
		nb *= base;
		nb += nDig;
		if (nb < oldnb) 
		{
			ret = -ret;
			break; //corrected overflow test
		}
		ret++;
	}

	if (ppStop) *ppStop = ptr;
	if (pNb) *pNb = bNegSign?-nb:nb;
	return ret;
}

//Bugs corrigés sur 5.0.3.69 / > 14/1/03 pb base + bad detection for overflow cases
int _xbSD(xbStrToUlong)(const _xbCHAR *ptr,ulong *pNb,int base,const _xbCHAR **ppStop)
{
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	if (*ptr == _xbSL('+')) ptr++;
	else if (*ptr == _xbSL('-')) 
	{
		if (ppStop) *ppStop = ptr;
		return 0;
	}
	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	if (base == 0)
	{
		if (*ptr == _xbSL('0'))
		{
			if (*(ptr+1) == _xbSL('x') || *(ptr+1) == _xbSL('X')) {base = 16; ptr += 2;}
			else if (*(ptr+1) == _xbSL('b') || *(ptr+1) == _xbSL('B')) {base = 2; ptr += 2;}
			else if (*(ptr+1) == _xbSL('o') || *(ptr+1) == _xbSL('O')) {base = 8; ptr += 2;}
			else base = 10;
		}
		else base = 10;
	}
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrToUlong() : base = %d is invalid",base);

	ulong	nb = 0,oldnb;
	int	ret = 0;

	while(*ptr && _xbSD(xbIsSpace)(*ptr)) ptr++;
	while(*ptr != 0)
	{
		int nDig;
		if (*ptr >= _xbSL('0') && *ptr <= _xbSL('9')) nDig = int(*ptr) - int(_xbSL('0'));
		else if (*ptr >= _xbSL('A') && *ptr <= _xbSL('Z')) nDig = int(*ptr) - int(_xbSL('A')) + 10;
		else if (*ptr >= _xbSL('a') && *ptr <= _xbSL('z')) nDig = int(*ptr) - int(_xbSL('a')) + 10;
		else nDig = 36;
		if (nDig >= base)
		{
			if (!_xbSD(xbIsSpace)(*ptr)) 
				break;
			ptr++;
			continue;
		}
		oldnb = nb;
		ptr++;
		nb *= base;
		nb += ulong(nDig);
		if (nb < oldnb) 
		{
			ret = -ret;
			break; //corrected overflow test
		}
		ret++;
	}

	if (ppStop) *ppStop = ptr;
	if (pNb) *pNb = nb;
	return ret;
}

_xbSTR xbEXPORT _xbSD(xbStrFromInt64)(int64 val, int base, bool bUpperCase)
{
	if (base == 0)
		base = 10;
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrFromInt64() : base = %d is invalid",base);

	bool bNeg = false;
	if (val < 0)
	{
		val = -val;
		bNeg = true;
	}
	_xbSTR ret;
	while(val)
	{
		int nDig = int(val % base);
		val /= base;
		if (nDig <= 9)
			ret.Insert(0,_xbSL('0')+nDig);
		else if (bUpperCase)
			ret.Insert(0,_xbSL('A')+nDig-10);
		else
			ret.Insert(0,_xbSL('a')+nDig-10);
	}
	if (ret.IsEmpty())
		return _xbSL("0");
	if (bNeg)
		return _xbSL("-")+ret;
	return ret;
}

_xbSTR xbEXPORT _xbSD(xbStrFromUint64)(uint64 val, int base, bool bUpperCase)
{
	if (base == 0)
		base = 10;
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrFromUint64() : base = %d is invalid",base);

	_xbSTR ret;
	while(val)
	{
		int nDig = int(val % base);
		val /= base;
		if (nDig <= 9)
			ret.Insert(0,_xbSL('0')+nDig);
		else if (bUpperCase)
			ret.Insert(0,_xbSL('A')+nDig-10);
		else
			ret.Insert(0,_xbSL('a')+nDig-10);
	}
	if (ret.IsEmpty())
		return _xbSL("0");
	return ret;
}

_xbSTR xbEXPORT _xbSD(xbStrFromLong)(long val, int base, bool bUpperCase)
{
	if (base == 0)
		base = 10;
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrFromLong() : base = %d is invalid",base);

	bool bNeg = false;
	if (val < 0)
	{
		val = -val;
		bNeg = true;
	}
	_xbSTR ret;
	while(val)
	{
		int nDig = int(val % base);
		val /= base;
		if (nDig <= 9)
			ret.Insert(0,_xbSL('0')+nDig);
		else if (bUpperCase)
			ret.Insert(0,_xbSL('A')+nDig-10);
		else
			ret.Insert(0,_xbSL('a')+nDig-10);
	}
	if (ret.IsEmpty())
		return _xbSL("0");
	if (bNeg)
		return _xbSL("-")+ret;
	return ret;
}

_xbSTR xbEXPORT _xbSD(xbStrFromUlong)(ulong val, int base, bool bUpperCase)
{
	if (base == 0)
		base = 10;
	if (base < 2 || base > 36) 
		xbThrowEsysFmt("xbStrFromUlong() : base = %d is invalid",base);

	_xbSTR ret;
	while(val)
	{
		int nDig = int(val % base);
		val /= base;
		if (nDig <= 9)
			ret.Insert(0,_xbSL('0')+nDig);
		else if (bUpperCase)
			ret.Insert(0,_xbSL('A')+nDig-10);
		else
			ret.Insert(0,_xbSL('a')+nDig-10);
	}
	if (ret.IsEmpty())
		return _xbSL("0");
	return ret;
}
/*
void _xbSTR::ReadFromStream(IxbStreamMode *pMode,IxbInputStream *pStream)
{
	switch(pMode?pMode->GetFormat():xbFormatBinary)
	{
	case xbFormatBinary :
		{
			long lg = pStream->ReadLong();
			if (lg>0) 
				pStream->Read((_xbCHAR*)_xbSD(xbStrExp)(this,lg,false),lg * sizeof(_xbCHAR));
			else 
				Empty();
		}
		break;
	case xbFormatHumanReadable :
		pStream->ReadString(this,NULL,pMode);
		break;
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbString::ReadFromStream() :  format not supported");
	}
}

void _xbSTR::WriteToStream(IxbStreamMode *pMode,IxbOutputStream *pStream) const
{
	switch(pMode?pMode->GetFormat():xbFormatBinary)
	{
	case xbFormatBinary :
		{
			long lg = Length;
			pStream->WriteLong(lg);
			if (lg > 0) 
				pStream->Write((const _xbCHAR*)*this,lg * sizeof(_xbCHAR));
		}
		break;
	case xbFormatHumanReadable :
		pStream->WriteString(*this,false,pMode);
		break;
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbString::ReadFromStream() :  format not supported");
	}
}
*/
int	_xbSTR::Split(const _xbSTR &strToFind, xbArray<_xbSTR> *pArr, bool bTrim) const
{
	if (strToFind.IsEmpty()) 
		return 0;

	int cnt=0,pos = 0, nextpos;
	for(;;)
	{
		nextpos = IndexOf(strToFind,pos);
		if (pArr)
		{
			_xbSTR sub(nextpos>=0?Mid(pos,nextpos - pos):Mid(pos));
			if (bTrim)
				sub.Trim();
			pArr->Add(sub);
		}
		cnt++;
		if (nextpos < 0)
			break;
		pos = nextpos + 1;
	}

	return cnt;
}

int	_xbSTR::SplitNC(const _xbSTR &strToFind, xbArray<_xbSTR> *pArr, bool bTrim) const
{
	if (strToFind.IsEmpty()) 
		return 0;

	int cnt=0,pos = 0, nextpos;
	for(;;)
	{
		nextpos = IndexOfNC(strToFind,pos);
		if (pArr)
		{
			_xbSTR sub(nextpos>=0?Mid(pos,nextpos - pos):Mid(pos));
			if (bTrim)
				sub.Trim();
			pArr->Add(sub);
		}
		cnt++;
		if (nextpos < 0)
			break;
		pos = nextpos + 1;
	}

	return cnt;
}

int	_xbSTR::Split(_xbCHAR chToFind, xbArray<_xbSTR> *pArr, bool bTrim) const
{
	int cnt=0,pos = 0, nextpos;
	for(;;)
	{
		nextpos = IndexOf(chToFind,pos);
		if (pArr)
		{
			_xbSTR sub(nextpos>=0?Mid(pos,nextpos - pos):Mid(pos));
			if (bTrim)
				sub.Trim();
			pArr->Add(sub);
		}
		cnt++;
		if (nextpos < 0)
			break;
		pos = nextpos + 1;
	}

	return cnt;
}

int	_xbSD(xbSortStringNC)(const _xbSTR &str1,const _xbSTR &str2)
{
	return str1.CompareNoCase(str2);
}

int	_xbSD(xbFindStringNC)(const _xbSTR &str1,const void *pStr2)
{
	const _xbSTR &str2 = *(const _xbSTR*)pStr2;
	return str1.CompareNoCase(str2);
}

int _xbSD(xbSortString)(const _xbSTR &str1,const _xbSTR &str2)
{
	return str1.Compare(str2);
}

int _xbSD(xbFindString)(const _xbSTR &str1,const void *pStr2)
{
	const _xbSTR &str2 = *(const _xbSTR*)pStr2;
	return str1.Compare(str2);
}

int _xbSTR::consoleGetArg(ulong cp,const _xbSTR &cmd,xbArray<_xbSTR> *pArrArg,xbOpMode opm,_xbPCSTR pSeps) const
{
	int pos = 0,cnt = 0;

	if (!pSeps)
		pSeps = _xbSL(" ");
	_xbSTR str,str2;
	while((pos = GetToken(pos,pSeps,&str,NULL,NULL,_xbSL("'\""),_xbSL("({["),_xbSL(")}]"))) > 0)
	{
		if (str.IsEmpty())
			continue;
		if (str[1] == _xbSL('"') || str[1] == _xbSL('\''))
		{
			if (str.GetTokenString(0,_xbSL("\"'"),&str2)<0)
			{
				if (opm == xbOp_Optional)
					return -1;//string not terminated (if bReq = false else exception)

				xbThrow xbEapp("Argument n°"+xbString(cnt)+"(0 based) of command ("+xbString(cmd)+") is invalid (string not closed) !");
			}
			str = ::xbUnescape(str2,cp);
		}
		if (pArrArg)
			pArrArg->Add(str);
		cnt++;
	}
	if (pos < xbTokenError_End)
	{
		if (opm == xbOp_Optional)
			return pos;
		if (pos == xbTokenError_StringNotTerminated)
			xbThrow xbEapp("String not terminated in command ("+xbString(cmd)+") !");
		if (pos == xbTokenError_BlockNotTerminated)
			xbThrow xbEapp("Delimited block not terminated in command ("+xbString(cmd)+") !");
		xbThrow xbEapp("Unexpected result for command parse ("+xbString(cmd)+") !");
	}
	return cnt;
}

int	_xbSTR::GetToken(int start,_xbPCSTR pSeps,_xbSTR *pStr,_xbSTR *pStrStaying,_xbCHAR *pChSep,bool bSpcNet) const
{
	if (IsEmpty()) 
		return xbTokenError_End;

	if (!bSpcNet)
		bSpcNet = _xbSD(xbStrchr)(pSeps,_xbSL(' ')) != NULL; 

	_xbPCSTR pStart = m_pData->m_str;
	int			lg = m_pData->m_cChar;

	if (start < 0)
		xbThrowEarg("xbString::GetToken(start="+xbString(start)+") : bad start position");

	if (start >= lg) 
		return xbTokenError_End;

	if (start > 0)
		pStart += start;

	if (bSpcNet)
	{
		while(start <= lg
			&& _xbSD(xbIsSpace)(*pStart))
		{
			pStart++;
			start++;
		}
	}
	const _xbCHAR *ptr = pStart;
	int			pos = start;

	const _xbCHAR *pSepsEnd = pSeps + _xbSD(xbStrlen)(pSeps);

	while(pos < lg)
	{
		const _xbCHAR *pSep = pSeps;
		while(pSep < pSepsEnd)
		{
			if ((*ptr == *pSep) || (*pSep == ' ' && _xbSD(xbIsSpace)(*ptr)))
			{
				int retPos = pos;
				const _xbCHAR *retPtr = ptr;
				if (*pSep == ' ')
				{
					while(retPos < lg && _xbSD(xbIsSpace)(*(retPtr+1)))
					{
						retPtr++;
						retPos++;
					}
					if (retPos < lg - 1)
					{
						const _xbCHAR *pOthSep = _xbSD(xbStrchr)(pSeps,*(retPtr+1));
						if (pOthSep != NULL)
						{
							pSep = pOthSep;
							retPtr++;
							retPos++;
						}
					}
				}
				else if (bSpcNet)
				{
					while(pos > start && _xbSD(xbIsSpace)(*(ptr-1)))
					{
						pos--;
						ptr--;
					}
				}
				//Bugs corrigés sur 5.0.3.69 / 3/3/03 pChSep was bad
				if (pChSep)
					*pChSep = *retPtr;
				_xbSTR str,strStaying;
				if (pStr)
				{
					if (pos - start > 0) 
						str = _xbSTR(pStart,pos - start);
				}
				if (pStrStaying)
				{
					if (lg - retPos - 1> 0) 
					{
						strStaying = _xbSTR(retPtr+1,lg - retPos - 1);
						if (bSpcNet)
							strStaying.Trim();
					}
				}
				if (pStr) 
					*pStr = str;
				if (pStrStaying) 
					*pStrStaying = strStaying;
				return retPos+1;
			}
			pSep++;
		}
		ptr++;
		pos++;
	}
	if (pStr)
	{
		_xbSTR str(pStart,lg - start + 1);
		if (bSpcNet)
			str.Trim();
		*pStr = str;
	}
	if (pStrStaying)
		pStrStaying->Empty();
	if (pChSep)
		*pChSep = 0;
	return lg;
}

int	_xbSTR::GetToken(int start,_xbPCSTR pSeps,_xbSTR *pStr,_xbSTR *pStrStaying,_xbCHAR *pChSep,
					   _xbPCSTR pStringDelimiter,
					   _xbPCSTR pDelimiterStart,_xbPCSTR pDelimiterEnd,bool bSpcNet) const
{
	if (IsEmpty()) 
		return xbTokenError_End;

	if (!bSpcNet)
		bSpcNet = _xbSD(xbStrchr)(pSeps,_xbSL(' ')) != NULL; 

	_xbPCSTR pStart = m_pData->m_str;
	int			lg = m_pData->m_cChar;

	if (start < 0)
		xbThrowEarg("xbString::GetToken(start="+xbString(start)+") : bad start position");

	if (start >= lg) 
		return xbTokenError_End;

	if (start > 0)
		pStart += start;

	const _xbCHAR *pStringDelimiterEnd = NULL;
	if (pStringDelimiter)
		pStringDelimiterEnd = pStringDelimiter + _xbSD(xbStrlen)(pStringDelimiter);

	const _xbCHAR *pDelimiterStartEnd = NULL;
	if (pDelimiterStart)
	{
		pDelimiterStartEnd = pDelimiterStart + _xbSD(xbStrlen)(pDelimiterStart);
		if (!pDelimiterEnd)
			pDelimiterEnd = pDelimiterStart;
		else
		{
			if (_xbSD(xbStrlen)(pDelimiterStart) != _xbSD(xbStrlen)(pDelimiterEnd))
				xbThrowEarg("xbString::GetToken() : delimiters start("+xbString(pDelimiterStart)+") length != delimiters end ("+xbString(pDelimiterEnd)+") length");
		}
	}
	if (bSpcNet)
	{
		while(start <= lg
			&& _xbSD(xbIsSpace)(*pStart))
		{
			pStart++;
			start++;
		}
	}
	const _xbCHAR *ptr = pStart;
	int			pos = start;

	const _xbCHAR *pSepsEnd = pSeps + _xbSD(xbStrlen)(pSeps);

	int level = 0;
	_xbCHAR chInStr = 0;

	xbArray<_xbCHAR,false,false>	arrLevel;

	while(pos < lg)
	{
		if (chInStr != 0)
		{
			if (*ptr == _xbSL('\\'))
			{
				if (pos >= lg - 1)
					return xbTokenError_StringNotTerminated; //string not terminated
				ptr+=2;
				pos+=2;
				continue;
			}
			if (*ptr == chInStr)
				chInStr = 0;
			ptr++;
			pos++;
			continue;
		}
		if (pStringDelimiter)
		{
			const _xbCHAR *pDel = pStringDelimiter;
			while(pDel < pStringDelimiterEnd)
			{
				if (*ptr == *pDel)
				{
					chInStr = *pDel;
					break;
				}
				pDel++;
			}
			if (chInStr != 0)
			{
				ptr++;
				pos++;
				continue;
			}
		}
		if (level > 0)
		{
			if (*ptr == arrLevel.GetAt(level-1))
			{
				level--;
				ptr++;
				pos++;
				continue;
			}
		}
		if (pDelimiterStart)
		{
			const _xbCHAR *pDel = pDelimiterStart;
			const _xbCHAR *pDelEnd = pDelimiterEnd;
			while(pDel < pDelimiterStartEnd)
			{
				if (*ptr == *pDel)
					break;
				pDel++;
				pDelEnd++;
			}
			if (pDel < pDelimiterStartEnd)
			{
				level++;
				if (level > arrLevel.Length)
					arrLevel.Length = level;
				arrLevel.SetAt(level-1,*pDelEnd);
				ptr++;
				pos++;
				continue;
			}
		}
		if (level > 0)
		{
			ptr++;
			pos++;
			continue;
		}
		const _xbCHAR *pSep = pSeps;
		while(pSep < pSepsEnd)
		{
			if ((*ptr == *pSep) || (*pSep == _xbSL(' ') && _xbSD(xbIsSpace)(*ptr)))
			{
				int retPos = pos;
				const _xbCHAR *retPtr = ptr;
				if (*pSep == _xbSL(' '))
				{
					while(retPos < lg && _xbSD(xbIsSpace)(*(retPtr+1)))
					{
						retPtr++;
						retPos++;
					}
					if (retPos < lg - 1)
					{
						const _xbCHAR *pOthSep = _xbSD(xbStrchr)(pSeps,*(retPtr+1));
						if (pOthSep != NULL)
						{
							pSep = pOthSep;
							retPtr++;
							retPos++;
						}
					}
				}
				else if (bSpcNet)
				{
					while(pos > start && _xbSD(xbIsSpace)(*(ptr-1)))
					{
						pos--;
						ptr--;
					}
				}
				//Bugs corrigés sur 5.0.3.69 / 3/3/03 pChSep was bad
				if (pChSep) //
					*pChSep = *retPtr;
				_xbSTR str,strStaying;
				if (pStr)
				{
					if (pos - start > 0) 
						str.Set(pStart,pos - start);
				}
				if (pStrStaying)
				{
					if (lg - retPos - 1> 0) 
					{
						strStaying.Set(retPtr+1,lg - retPos - 1);
						if (bSpcNet)
							strStaying.Trim();
					}
				}
				if (pStr) 
					*pStr = str;
				if (pStrStaying) 
					*pStrStaying = strStaying;
				return retPos+1;
			}
			pSep++;
		}
		ptr++;
		pos++;
	}
	if (chInStr != 0)
		return xbTokenError_StringNotTerminated; //string not terminated
	if (level > 0)
		return xbTokenError_BlockNotTerminated; //delimited block not terminated
	if (pStr)
	{
		_xbSTR str(pStart,lg - start + 1);
		if (bSpcNet)
			str.Trim();
		*pStr = str;
	}
	if (pStrStaying)
		pStrStaying->Empty();
	if (pChSep)
		*pChSep = 0;
	return lg;
}

int _xbSTR::GetTokenString(int start,_xbPCSTR pStringDelimiter, _xbSTR *pStr,
		_xbCHAR *pchDelimiter,bool *pbMixedDelimiter) const
{
	if (pStr) 
		pStr->Empty();
	if (pchDelimiter)
		*pchDelimiter = 0;
	if (pbMixedDelimiter)
		*pbMixedDelimiter = false;

	if (IsEmpty()) 
		return xbTokenError_End;

	_xbPCSTR	ptr = m_pData->m_str;
	int			lg = m_pData->m_cChar;

	if (start <= 0)
		xbThrowEarg("xbString::GetTokenString(start="+xbString(start)+") : bad start position");
	if (start >= lg) 
		return xbTokenError_End;

	if (start > 0)
		ptr += start;

	const _xbCHAR *pStringDelimiterEnd = pStringDelimiter + _xbSD(xbStrlen)(pStringDelimiter);

	int			pos = start;

	_xbCHAR chInStr = 0,chDel = 0;
	const _xbCHAR *pStart = NULL;
	start = 0;

	while(pos < lg)
	{
		if (chInStr != 0)
		{
			if (*ptr == L'\\')
			{
				if (pos >= lg - 1)
					return xbTokenError_StringNotTerminated; //string not terminated
				ptr+=2;
				pos+=2;
				continue;
			}
			if (*ptr == chInStr)
			{
				if (pStr)
					pStr->Add(pStart,pos - start);
				pStart = NULL;
				start = 0;
				chInStr = 0;
			}
			ptr++;
			pos++;
			continue;
		}
		if (pStringDelimiter)
		{
			const _xbCHAR *pDel = pStringDelimiter;
			while(pDel < pStringDelimiterEnd)
			{
				if (*ptr == *pDel)
				{
					chInStr = *pDel;
					if (chDel == 0) chDel = chInStr;
					else if (pbMixedDelimiter && chDel != chInStr)
						*pbMixedDelimiter = true;
					pStart = ptr + 1;
					start = pos + 1;
					break;
				}
				pDel++;
			}
			if (chInStr != 0)
			{
				ptr++;
				pos++;
				continue;
			}
		}
		if (!_xbSD(xbIsSpace)(*ptr))
			break;
		ptr++;
		pos++;
	}
	if (chInStr != 0)
		return xbTokenError_StringNotTerminated; //string not terminated
	if (pchDelimiter)
		*pchDelimiter = chDel;
	return pos;
}

void _xbSTR::trim(_xbPCSTR pTrimChars,bool bLeft,bool bRight)
{
	if (!m_pData)
		return;

	long lgOrig = m_pData->m_cChar;
	long lg = lgOrig;
	_xbPCSTR ptr = m_pData->m_str;

	if (bLeft)
	{
		while(lg > 0 && _xbSD(xbStrchr)(pTrimChars,*ptr))
		{
			lg--;
			ptr++;
		}
	}

	if (bRight)
	{
		const _xbCHAR *pend = ptr + lg - 1;
		while(lg > 0 && _xbSD(xbStrchr)(pTrimChars,*pend))
		{
			lg--;
			pend--;
		}
	}

	if (lg == lgOrig)
		return;

	OwnData();

	if (ptr != m_pData->m_str)
	{
		long pos = long((xbNptr(ptr) - xbNptr(m_pData->m_str))/sizeof(_xbCHAR));
		memmove(m_pData->m_str,m_pData->m_str+pos,lg*sizeof(_xbCHAR));
	}

	setLength(lg);
}

_xbSTR  operator+(const _xbSTR& string1,const _xbSTR& string2)
{
	int lg1 = string1.Length;
	int lg2 = string2.Length;
	_xbSTR ret;
	_xbPSTR pstr = ret.GetWritePtr(lg1+lg2);

	if (lg1)
		memcpy(pstr,(_xbPCSTR)string1,lg1*sizeof(_xbCHAR));

	if (lg2)
		memcpy(pstr+lg1,(_xbPCSTR)string2,lg2*sizeof(_xbCHAR));

	return ret;
}

_xbSTR  operator+(const _xbSTR& string, _xbPCSTR lpsz)
{
	int lg1 = string.Length;
	int lg2 = (int)(lpsz?_xbSD(xbStrlen)(lpsz):0);
	_xbSTR ret;
	_xbPSTR pstr = ret.GetWritePtr(lg1+lg2);

	if (lg1)
		memcpy(pstr,(_xbPCSTR)string,lg1*sizeof(_xbCHAR));

	if (lg2)
		memcpy(pstr+lg1,lpsz,lg2*sizeof(_xbCHAR));

	return ret;
}

_xbSTR  operator+(_xbPCSTR lpsz, const _xbSTR& string)
{
	int lg1 = (int)(lpsz?_xbSD(xbStrlen)(lpsz):0);
	int lg2 = string.Length;
	_xbSTR ret;
	_xbPSTR pstr = ret.GetWritePtr(lg1+lg2);

	if (lg1)
		memcpy(pstr,lpsz,lg1*sizeof(_xbCHAR));

	if (lg2)
		memcpy(pstr+lg1,(_xbPCSTR)string,lg2*sizeof(_xbCHAR));

	return ret;
}

_xbSTR  xbEXPORT operator+(const _xbSTR& string, _xbCHAR ch)
{
	_xbSTR ret(string);
	ret.Add(ch);
	return ret;
}

_xbSTR  xbEXPORT operator+(_xbCHAR ch, const _xbSTR& string)
{
	_xbSTR ret(string);
	ret.Insert(0,ch);
	return ret;
}

bool _xbSTR::StartWith(_xbPCSTR sSub,int len) const
{
	if (len <= 0)
		return true;
	if (!m_pData)
		return false;
	if (len > m_pData->m_cChar)
		return false;
	return _xbSD(xbStrncmp)(m_pData->m_str,sSub,len) == 0;
}

bool _xbSTR::EndWith(_xbPCSTR sSub,int len) const
{
	if (len <= 0)
		return true;
	if (!m_pData)
		return false;
	if (len > m_pData->m_cChar)
		return false;
	return _xbSD(xbStrncmp)(m_pData->m_str+m_pData->m_cChar-len,sSub,len) == 0;
}

bool _xbSTR::StartWithNC(_xbPCSTR sSub,int len) const
{
	if (len <= 0)
		return true;
	if (!m_pData)
		return false;
	if (len > m_pData->m_cChar)
		return false;
	return _xbSD(xbStrnicmp)(m_pData->m_str,sSub,len) == 0;
}

bool _xbSTR::EndWithNC(_xbPCSTR sSub,int len) const
{
	if (len <= 0)
		return true;
	if (!m_pData)
		return false;
	if (len > m_pData->m_cChar)
		return false;
	return _xbSD(xbStrnicmp)(m_pData->m_str+m_pData->m_cChar-len,sSub,len) == 0;
}

bool _xbSTR::StartWith(const _xbSTR & sSub) const
{
	return StartWith(sSub,sSub.Length);
}
bool _xbSTR::StartWith(_xbPCSTR sSub) const
{
	if (!sSub)
		return true;
	return StartWith(sSub,(int)_xbSD(xbStrlen)(sSub));
}

bool _xbSTR::EndWith(const _xbSTR & sSub) const
{
	return EndWith(sSub,sSub.Length);
}
bool _xbSTR::EndWith(_xbPCSTR sSub) const
{
	if (!sSub)
		return true;
	return EndWith(sSub,(int)_xbSD(xbStrlen)(sSub));
}

bool _xbSTR::StartWithNC(const _xbSTR & sSub) const
{
	return StartWithNC(sSub,sSub.Length);
}
bool _xbSTR::StartWithNC(_xbPCSTR sSub) const
{
	if (!sSub)
		return true;
	return StartWithNC(sSub,(int)_xbSD(xbStrlen)(sSub));
}

bool _xbSTR::EndWithNC(const _xbSTR & sSub) const
{
	return EndWithNC(sSub,sSub.Length);
}
bool _xbSTR::EndWithNC(_xbPCSTR sSub) const
{
	if (!sSub)
		return true;
	return EndWithNC(sSub,(int)_xbSD(xbStrlen)(sSub));
}

// concatenate a xbString
const _xbSTR& _xbSTR::operator+=(const _xbSTR& string)
{
	Add(string);
	return *this;
}

// concatenate a zt string
const _xbSTR& _xbSTR::operator+=(_xbPCSTR lpsz)
{
	Add(lpsz);
	return *this;
}

// concatenate a single character
const _xbSTR& _xbSTR::operator+=(_xbCHAR ch)
{
	Add(&ch,1);
	return *this;
}

_xbSTR _xbSD(xbStrFromNptr)(xbNptr val)
{
#ifdef XDCBASE_X64
	return _xbSD(xbStrFromUint64)(val,16);
#else
	return _xbSD(xbStrFromUlong)(val,16);
#endif
}

_xbSTR _xbSD(xbStrFromPtr)(const void *pVal)
{
	return _xbSD(xbStrFromNptr)((xbNptr)pVal);
}

long _xbSD(xbStrToLong)(const _xbCHAR *ptr,int base)
{
	long nb;
	const _xbCHAR *pStop=NULL;
	if (_xbSD(xbStrToLong)(ptr,&nb,base,&pStop)<=0 || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid number","xbStrToLong");
	return nb;
}

ulong _xbSD(xbStrToUlong)(const _xbCHAR *ptr,int base)
{
	ulong nb;
	const _xbCHAR *pStop=NULL;
	if (_xbSD(xbStrToUlong)(ptr,&nb,base,&pStop)<=0 || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid number","xbStrToUlong");
	return nb;
}

int64 _xbSD(xbStrToInt64)(const _xbCHAR *ptr,int base)
{
	int64 nb;
	const _xbCHAR *pStop=NULL;
	if (_xbSD(xbStrToInt64)(ptr,&nb,base,&pStop)<=0 || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid number","xbStrToInt64");
	return nb;
}

uint64 _xbSD(xbStrToUint64)(const _xbCHAR *ptr,int base)
{
	uint64 nb;
	const _xbCHAR *pStop=NULL;
	if (_xbSD(xbStrToUint64)(ptr,&nb,base,&pStop)<=0 || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid number","xbStrToUint64");
	return nb;
}

double _xbSD(xbStrToDouble)(const _xbCHAR *ptr)
{
	double nb;
	const _xbCHAR *pStop=NULL;
	if (_xbSD(xbStrToDouble)(ptr,&nb,&pStop)<=0 || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid number","xbStrToDouble");
	return nb;
}

_xbSTR _xbSD(xbStrFromBool)(bool val)
{
	return val?_xbSL("true"):_xbSL("false");
}

bool xbEXPORT _xbSD(xbStrToBool)(const _xbCHAR *ptr,bool *pBool,const _xbCHAR **ppStop)
{
	if (ptr)
	{
		if (_xbSD(xbStrnicmp)(ptr,_xbSL("true"),4) == 0)
		{
			if (ppStop)
				*ppStop = ptr+4;
			if (pBool)
				*pBool = true;
			return true;
		}
		if (_xbSD(xbStrnicmp)(ptr,_xbSL("false"),5) == 0)
		{
			if (ppStop)
				*ppStop = ptr+5;
			if (pBool)
				*pBool = false;
			return true;
		}
/*		if (_xbSD(xbStrnicmp)(ptr,_xbSL("0"),1) == 0)
		{
			if (ppStop)
				*ppStop = ptr+1;
			if (pBool)
				*pBool = true;
			return true;
		}
		if (_xbSD(xbStrnicmp)(ptr,_xbSL("1"),1) == 0)
		{
			if (ppStop)
				*ppStop = ptr+1;
			if (pBool)
				*pBool = false;
			return true;
		}*/
	}
	if (ppStop)
		*ppStop = ptr;
	return false;
}

bool xbEXPORT _xbSD(xbStrToBool)(const _xbCHAR *ptr)
{
	bool nb;
	const _xbCHAR *pStop=NULL;
	if (!_xbSD(xbStrToBool)(ptr,&nb,&pStop) || !pStop || *pStop != 0)
		xbThrowEarg("String is not a valid boolean","xbStrToBool");
	return nb;
}
#pragma warning(pop)

#undef _xbSD
#undef _xbCHAR
#undef _xbUCHAR
#undef _xbSTR
#undef _xbSL
#undef _xbBYTEtoCHAR
#undef _xbPCSTR
#undef _xbPSTR
