/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include <cstdio>
		#include <cctype>
		#include <cstring>

	#if		__PLATFORM_WINDOWS__
		#include <cstdarg>
	#elif	__PLATFORM_NINTENDO_WII__
		#include <revolution/os.h>
	#elif	__PLATFORM_POSIX__
		#include <stdarg.h>
		#include <stdlib.h>
	#endif

		#include "nstring/nstring.h"
		#include "container/nlist.h"
		#include "container/narray.h"
		#include "platform_config.h"
		#include "alloc/ialloc.h"

		using namespace GS;


//------------------------------------------------------------------------------
static char	__lowercase(char c)
{
	if	((c >= 'A') && (c <= 'Z'))
		c = c - 'A' + 'a';
	return c;
}
static	bool __isValidSymbolCharacter(char c)
{
	return	(
				((c >= 'a') && (c <= 'z')) ||
				((c >= 'A') && (c <= 'Z')) ||
				((c >= '0') && (c <= '9')) ||
				(c == '_')
			);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#if		(__PLATFORM_LINUX__ || __PLATFORM_OSX__ || __PLATFORM_IOS__ || __PLATFORM_ANDROID_NDK__)
void		strupr(char *s)
{
	for	(; s[0]; ++s)
		if	((s[0] >= 'a') || (s[0] <= 'z'))
			s[0] = toupper(s[0]);
}
void		strlwr(char *s)
{
	for	(; s[0]; ++s)
		if	((s[0] >= 0x41) || (s[0] <= 0x5A))
			s[0] = tolower(s[0]);
}
#endif
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void		String::NormalizeEOL(EOLConvention to_eol)
{
	switch (to_eol)
	{
		case EOLUnix:
		{
			uint invalid_eol_count = 0;
			for (char *p = _p.c_ptr(); p[0] && p[1]; ++p)
				if	((p[0] == '\r') && (p[1] == '\n'))
				{
					++invalid_eol_count;
					++p;
				}

			Array <char> _o(Size() - invalid_eol_count + 1);	// remove 1 byte per invalid EOL

			char *o = _o.c_ptr(), *p = _p.c_ptr();
			for ( ; p[0] && p[1]; ++p)
				if	((p[0] == '\r') && (p[1] == '\n'))
				{
					*o++ = '\n';
					++p;
				}
				else
					*o++ = p[0];

			if	(p[0] != 0)
				*o++ = *p++;

			*o = 0;
			_p.Transfer(_o);
		}
		break;

		case EOLWindows:
		{
			uint invalid_eol_count = 0;
			char *p = _p.c_ptr();
			for (int n = 0; p[n]; ++n)
				if	((p[n] == '\n') && ((n > 0) && (p[n - 1] != '\r')))
					++invalid_eol_count;

			Array <char> _o(Size() + invalid_eol_count + 1);	// add 1 byte per invalid EOL

			char *o = _o.c_ptr();
			p = _p.c_ptr();
			for (int n = 0; p[n]; ++n)
				if	((p[n] == '\n') && ((n > 0) && (p[n - 1] != '\r')))
				{
					*o++ = '\r';
					*o++ = '\n';
				}
				else
					*o++ = p[n];

			*o = 0;
			_p.Transfer(_o);
		}
		break;
	}
	Touch();
}
String		String::NormalizedEOL(EOLConvention to_eol) const
{
	String o(*this);
	o.NormalizeEOL(to_eol);
	return o;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uint		String::Len() const
{
	if	(len == -1)
		(const_cast <int &> (len)) = _p.IsValid() ? strlen(_p) : 0;
	return len;
}
size_t		String::Size() const
{
	size_t size = 0;
	for (char *p = _p; p[0]; ++p)
		++size;
	return size;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		String::RewritePatternAll(const char *p, const char *pattern, RewriteFunc &rewrite_func)
{
	String out;

	forever
	{
		const char *s, *e;
		s = String::LocatePattern(p, pattern, &e);

		if	(!s)
		{
			// append reminder
			out += String(p, p + String::strlen(p));
			break;
		}

		// append skipped segment
		out += String(p, s);

		// extract args...
		List <String> args;

		String source(s, e);
		if	(!source.Extract(pattern, args))
			break;

		// ...do rewrite.
		out += rewrite_func(source, args);

		p = e;
	}
	return out;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::Extract(const char *format, List <String> &arg)
{
	arg.Clear();

	const char *o = format;
	for (const char *i = c_str(); i[0] && o[0]; )
	{
		if	(o[0] == '%')
		{
			++o;

			// get arg value
			const char *e = i;
			for (; (e[0] != o[0]) && e[0]; ++e);

			arg.Add(String(i, e));
			i = e;
		}
		else
		{
			if	(i[0] != o[0])
			{
				// ignore character mismatch on space
				if	(i[0] == ' ')
				{
					++i;
					continue;
				}

				// source format mismatch
				return false;
			}

			++i;
			++o;
		}
	}
	return true;
}
const char	*String::LocatePattern(const char *s, const char *format, const char **e)
{
	for (; s[0]; ++s)
	{
		const char *o = format;

		if	(s[0] != o[0])
			continue;

		const char *i = s;
		while (i[0] && o[0])
		{
			if	(o[0] == '%')
			{
				++o;
				for (; (i[0] != o[0]) && i[0]; ++i);
			}
			else
			{
				if	(i[0] != o[0])
				{
					// ignore character mismatch on space
					if	(i[0] == ' ')
					{
						++i;
						continue;
					}

					// source format mismatch
					o = NULL;
					break;
				}

				++i;
				++o;
			}
		}
		if	(o == NULL)
			continue;

		if	(e)
			*e = i;
		return s;
	}
	return NULL;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::Compare(const char *s, CaseSensitivity cs) const
{
	char *p = _p.c_ptr();

	if	(!s)
		return true;
	if	(!p)
		return false;

	if	(cs == CaseSensitive)
	{
		while (*p && *s)
			if	(*p++ != *s++)
				return false;
	}
	else
	{
		while (*p && *s)
			if	(__lowercase(*p++) != __lowercase(*s++))
				return false;
	}
	return p[0] == s[0];
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::StartsWith(const char *b, CaseSensitivity cs) const
{
	uint b_len = strlen(b);
	if	(b_len > Len())
		return false;
	return c_str() ? Left(b_len).Compare(b, cs) : false;
}
bool		String::EndsWith(const char *b, CaseSensitivity cs) const
{
	uint b_len = strlen(b);
	if	(b_len > Len())
		return false;
	return c_str() ? Right(b_len).Compare(b, cs) : false;
}
bool		String::Contains(const char *b, CaseSensitivity cs) const
{	return c_str() ? asbool(FindString(b, cs)) : false;	}
bool		String::Replace(const char *what, const char *by, CaseSensitivity cs)
{
	char *sp = _p.c_ptr() ? FindString(what, 0, cs) : NULL;
	if	(!sp)
		return false;

	uint what_len = strlen(what),
		 by_len = strlen(by);
	uint final_len = Len() - what_len + by_len;

	Array <char> np(final_len + 1, Alloc::StringBuffer);
	char *wp = np;
	if	(np.IsNull())
		return false;

	memcpy(wp, _p.c_ptr(), sp - _p.c_ptr());
	wp += sp - _p;
	memcpy(wp, by, by_len);
	wp += by_len;
	memcpy(wp, sp + what_len, (_p + Len()) - (sp + what_len) + 1);

	Clear();
	_p = np;
	return true;
}
bool		String::ReplaceAll(const char **what, const char **by, bool match_whole_word)
{
	if	(!_p)
		return false;	// no replace

	uint final_len = Len();

	Array <char> p_out(Alloc::StringBuffer);
	char *_p_out = NULL;

	for	(uint p = 0; p < 2; ++p)
	{
		bool has_replacement = false;
		for	(char *c_p = _p; *c_p; )
		{
			// Search for a match.
			uint n;
			for	(n = 0; what[n] && by[n]; ++n)
				if	(!strccmp(c_p, what[n]))
					break;

			if	(what[n] && by[n])
			{
				// Got a match.
				size_t what_len = strlen(what[n]),
					   by_len = strlen(by[n]);

				// Check whole word constraint.
				bool do_replace = true;

				if	(match_whole_word)
				{
					if	((c_p > _p) && __isValidSymbolCharacter(c_p[-1]))
						do_replace = false;
					else
						if	(c_p[what_len] && __isValidSymbolCharacter(c_p[what_len]))
							do_replace = false;
				}

				if	(do_replace)
				{
					if	(_p_out)
					{
						strncpy(_p_out, by[n], by_len);
						_p_out += by_len;
					}
					else
						final_len += by_len - what_len;

					has_replacement = true;
				}
				else
					if	(_p_out)
					{
						strncpy(_p_out, c_p, what_len);
						_p_out += what_len;
					}

				c_p += what_len;
			}
			else
			{
				if	(_p_out)
					*_p_out++ = *c_p;
				++c_p;
			}
		}

		if	(!has_replacement)
			return false;	// no replace

		if	(!_p_out)
		{
			p_out.Allocate(final_len + 1);
			_p_out = p_out;
		}
	}

	// Commit modified buffer to string.
	Clear();
	p_out[final_len] = 0;
	_p = p_out;
	len = final_len;
	hash = 0;
	return true;
}
bool		String::ReplaceAll(const char *what, const char *by, bool match_whole_word)
{
	const char *_what[] = { what, NULL }, *_by[] = { by, NULL };
	return ReplaceAll(_what, _by, match_whole_word);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t		String::Utf8toUtf32(const uchar *utf8, uint *utf32)
{
	__ASSERT__(utf8);
	__ASSERT__(utf32);

	const uchar	*p = utf8;
	int length = GetUtf8CharSize(utf8);

	switch (length)
	{
		case 4:	*utf32 = (*p ^ 0xf0);	break;
		case 3:	*utf32 = (*p ^ 0xe0);	break;
		case 2:	*utf32 = (*p ^ 0xc0);	break;
		case 1:	*utf32 = *p;			break;

		default:
			*utf32 = 0;
			break;
	}

	for	(int n = length; n > 1; --n)
	{
		++p;
		*utf32 <<= 6;
		*utf32 |= (*p ^ 0x80);
	}
	return length;
}
size_t		String::GetUtf8CharSize(const uchar *utf8)
{
	__ASSERT__(utf8);

	uchar c = utf8[0] >> 3;

	// 6 => 0x7e
	// 5 => 0x3e
	if	(c == 0x1e)
		return 4;

	c >>= 1;
	if	(c == 0xe)
		return 3;
	c >>= 1;
	if	(c == 0x6)
		return 2;

	return 1;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		String::FromUcs2(const ushort *ucs2)
{
	Array <char> out(256);
	size_t out_count = 0;

	for	(uint n = 0; ucs2[n]; ++n)
	{
		wchar_t w = ucs2[n];

		if	(out_count > (out.GetCount() - 4))
			out.Reallocate(out.GetCount() * 2);

		if	(w <= 0x7f)
			out[(int)out_count++] = (char)w;

		else if (w <= 0x7ff)
		{
			out[(int)out_count++] = 0xc0 | ((w >> 6) & 0x1f);
			out[(int)out_count++] = 0x80 | (w & 0x3f);
		}
		else /* if (w <= 0xffff) */
		{
			out[(int)out_count++] = 0xe0 | ((w >> 12) & 0x0f);
			out[(int)out_count++] = 0x80 | ((w >> 6) & 0x3f);
			out[(int)out_count++] = 0x80 | (w & 0x3f);
		}
/*		else if (w <= 0x10ffff)
		{
			out[out_count++] = 0xf0 | ((w >> 18) & 0x07);
			out[out_count++] = 0x80 | ((w >> 12) & 0x3f);
			out[out_count++] = 0x80 | ((w >> 6) & 0x3f);
			out[out_count++] = 0x80 | (w & 0x3f);
		}
		else
			out[out_count++] = '?';	*/
	}

	return String(out.c_ptr(), out.c_ptr() + out_count);
}
Array <ushort>	String::toUcs2() const
{
	if	(!_p)
		return Array <ushort> (Alloc::StringBuffer);

	// Get utf-8 character count.
	int ccount = 0;

	char *p = _p;
	for	(; p[0]; ++ccount)
		p += GetUtf8CharSize((const uchar *)p);

	// Allocate array.
	Array <ushort>	ucs2(ccount + 1, Alloc::StringBuffer);

	p = _p;
	for	(ccount = 0; p[0]; ++ccount)
	{
		uint utf32;
		p += Utf8toUtf32((const uchar *)p, &utf32);
		ucs2[ccount] = utf32 & 0xffff;
	}
	ucs2[ccount] = 0;

	return ucs2;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::IsAbsolutePath() const
{	return asbool((Len() > 2) && (c_str()[1] == ':'));	}
String		String::BeautifyFileName(bool allow_spaces) const
{
	if	(!_p)
		return String();

	Array <char> p(Len() + 1, Alloc::StringBuffer);
	strncpy(p.c_ptr(), c_str(), Len() + 1);

	for	(uint n = 0; n < Len(); ++n)
	{
		if	(p[n] == ' ')
		{
			if	(!allow_spaces)
				p[n] = '_';
		}
		else
		{
			if	(p[n] == '_')
				p[n] = ' ';

			else
			{
				if	(n > 0)
				{
					if	((p[n - 1] == ' ') && (p[n] >= 'a') && (p[n] <= 'z'))
						p[n] += 'A' - 'a';
				}
				else
					if	((p[n] >= 'a') && (p[n] <= 'z'))
						p[n] += 'A' - 'a';
			}
		}
	}
	return String(&p[0]);
}
String		String::CutFilePath() const
{
	String	b = *this;
	b.FileCutPath();
	return b;
}
String		String::CutFileExtension() const
{
	String	b = *this;
	b.FileCutExtension();
	return b;
}
String		String::CutFileName() const
{
	String	b = *this;
	b.FileCutName();
	return b;
}
String		String::CleanFilePath() const
{
	String	b = *this;
	b.FileCleanName();
	return b;
}
String		String::GetFileName() const
{
	String	b = *this;
	b.FileCutPathAndExtension();
	return b;
}
String		String::GetFileNameAndExtension() const
{
	String	b = *this;
	b.FileCutPath();
	return b;
}
String		String::GetFilePath() const
{
	String	b = *this;
	b.FileCutName();
	return b;
}
String		String::GetFileExtension() const
{	return FileGetExtension(c_str());	}
String		String::GetSwappedExtension(const char *ext) const
{
	String	b = *this;
	b.FileSwapExtension(ext);
	return b;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
int			String::Compare(const String &c, const String &n)
{
	const char *a = c.c_str(), *b = n.c_str();

	if	(!a || !b)
		return a == b ? 0 : 1;

	while (a[0] || b[0])
	{
		if	(!b[0])	return -1;
		if	(!a[0])	return 1;

		if	(b[0] < a[0])	return -1;
		if	(b[0] > a[0])	return 1;

		a++;
		b++;
	}
	return 0;
}
String		String::Upper() const
{
	String	s(_p);
	if	(!s.IsEmpty())
		strupr(s._p);
	s.Touch();
	return s;
}
String		String::Lower() const
{
	String	s(_p);
	if	(!s.IsEmpty())
		strlwr(s._p);
	s.Touch();
	return s;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		String::Slice(uint s, uint e) const
{
	if	(s > Len())	s = Len();
	if	(e > Len())	e = Len();
	return String(_p + s, _p + e);
}
String		String::TrimChar(char c)
{
	int occurence = 0;
	for (char *p = _p; p[0]; p++)
		if	(p[0] == c)
			occurence++;
	if	(!occurence)
		return *this;

	// Trim character occurrences.
	Array <char> new_string(Len() - occurence + 1, Alloc::StringBuffer);
	char *pnew_string = &new_string[0];

	for (char *p = _p; p[0]; p++)
		if	(p[0] != c)
			*pnew_string++ = p[0];
	pnew_string[0] = 0;

	return String(&new_string[0]);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t		String::IndexOf(char c, uint p) const
{
	for	(size_t n = p; n < Len(); ++n)
		if	(_p[(int)n] == c)
			return n;
	return (size_t)-1;
}
char		*String::FindString(const char *c, uint p, CaseSensitivity cs) const
{
	size_t len_c = strlen(c);

	if	(len_c <= Len())
		for	(size_t n = p; n <= (Len() - len_c); ++n)
		{
			size_t i = 0;

			switch (cs)
			{
				case CaseSensitive:
					for	(; i < len_c; ++i)
						if	(_p[(int)(n + i)] != c[i])
							break;
					break;

				case CaseInsensitive:
					for	(; i < len_c; ++i)
						if	(__lowercase(_p[(int)(n + i)]) != __lowercase(c[i]))
							break;
					break;
			}

			if	(i == len_c)
				return &_p[(int)n];		// match
		}

	return NULL;
}
String		String::Left(uint n) const
{	return String(_p, _p + Types::Min(n, Len()));	}
String		String::Right(uint n) const
{
	const uint l = Len();
	return String(_p + Types::Min(n, l), _p + l);
}
String		String::Mid(uint p, int n) const
{
	uint l = Len();
	p = Types::Min(p, l);

	int char_left = int(l) - int(p);
	n = (n < 0) || (n > char_left) ? char_left : n;

	return String(_p + p, _p + p + n);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		String::Format(const char *format, ...)
{
	char static_buffer[4096];

    va_list	varg;
	va_start(varg, format);
	vsnprintf(static_buffer, 4095, format, varg);
	va_end(varg);

	return String(static_buffer);
}
String		String::operator + (const char *_ptr) const
{
	String _s = *this;
	_s += _ptr;
	return _s;
}
String		String::operator + (const String &s) const
{
	String _s = *this;
	_s += s.c_str();
	return _s;
}
void		String::operator += (const char *_ptr)
{
	if	(!_ptr)
		return;

	if	(_p.IsNull())
		*this = _ptr;

	else
	{
		size_t in_len = strlen(_ptr), total_len = Len() + in_len;

		if	((total_len + 1) <= _p.GetCount())
		{
			memcpy(&_p[Len()], _ptr, in_len);
			_p[(int)total_len] = 0;
			Touch();
		}
		else
		{
			Array <char> n_p(total_len + 1, Alloc::StringBuffer);

			if	(n_p.IsValid())
			{
				memcpy(&n_p[0], c_str(), Len());
				memcpy(&n_p[Len()], _ptr, in_len);
				n_p[(int)total_len] = 0;

				_p.Transfer(n_p);
				Touch();
			}
			else
				Clear();
		}
	}
}
void		String::operator += (float v)
{
	char tmp[32];
	_snprintf(tmp, 31, "%.4f", v);
	*this += tmp;
}
void		String::operator += (double v)
{
	char tmp[32];
	_snprintf(tmp, 31, "%.4f", v);
	*this += tmp;
}
void		String::operator += (int v)
{
	char tmp[32];
	_snprintf(tmp, 31, "%d", v);
	*this += tmp;
}
void		String::operator += (uint v)
{
	char tmp[32];
	_snprintf(tmp, 31, "%d", (int)v);
	*this += tmp;
}
void		String::operator += (bool v)
{	*this += v ? "true" : "false";	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		&String::operator = (const String &b)
{
	if	(b.IsEmpty())
		Clear();
	else
	{
		Set(b.c_str(), b.c_str() + b.Len());
		hash = b.hash;
		len = b.len;
	}
	return *this;
}
String		&String::operator = (const char *s)
{
	Set(s);
	return *this;
}
void		String::Set(const char *s, const char *e)
{
	// TODO properly handle copying one part of a string to itself.
	if	(s == _p)
		return;

	size_t new_len = s ? (e ? e - s : strlen(s)) : 0;

	if	(new_len > 0)
	{
		if	((new_len + 1) > _p.GetCount())
		{
			Clear();
			_p.Allocate(new_len + 8);		// Note: +8 to help reduce allocator pressure on string concatenation.
		}

		if	(_p.IsValid())
		{
			memcpy(_p, s, new_len);
			_p[(int)new_len] = 0;
			Touch();		
		}
		else
			Clear();
	}
	else
		Clear();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
String		&String::operator << (const String &v)
{	*this += v; return *this;	}
String		&String::operator << (const char *v)
{	*this += v; return *this;	}
String		&String::operator << (float v)
{	*this += v; return *this;	}
String		&String::operator << (double v)
{	*this += v; return *this;	}
String		&String::operator << (int v)
{	*this += v; return *this;	}
String		&String::operator << (uint v)
{	*this += v; return *this;	}
String		&String::operator << (bool v)
{	*this += v ? "true" : "false"; return *this;	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
int			String::atoh(const char *_sr)
{
	ushort p = 0, s = 0;
	int v = 0;
	char str[32];

	strncpy(str, _sr, 8);
	str[8] = 0;

	strlwr(str);

	while (str[p])
	{
		if	((str[p] == 'x') || (str[p] == 'h'))
		{
			s = (ushort)(p + 1);
			break;
		}
		p++;
	}
	while (str[s])
	{
		v *= 16;
		if	((str[s] >= '0') && (str[s] <= '9'))
			v += str[s] - '0';
		else if ((str[s] >= 'a') && (str[s] <= 'f'))
			v += str[s] - 'a' + 10;
		s++;
	}
	return v;
}
int			String::atoi(const char *p)
{
	char			sign = 1;
	if	(p[0] == '-')
	{
		sign = -1;
		p++;
	}
	int			integer = 0;
	while	((p[0] >= '0') && (p[0] <= '9'))
	{
		integer *= 10;
		integer += *p++ - '0';
	}
	return (integer * sign);
}
float		String::atof(const char *s, const char *e, bool support_comma)
{
	if	(!s)
		return 0.f;
	if	(!e)
		e = s + strlen(s);

	float sign = 1;
	if	(s == e)
		return 0.f;
	if	(s[0] == '-')
	{
		sign = -1;
		s++;
	}
	if	(s == e)
		return -0.f;

   	float tt = 1.f;
   	float real = 0.f;
	float integer = 0.f;

	while ((s < e) && (s[0] != '.') && ((s[0] != ',') || !support_comma))
	{
		if	(s[0] == 'e')
			goto exponent;
		if	((s[0] < '0') || (s[0] > '9'))
			return (float)(integer * sign);

		integer *= 10.f;
		integer += *s++ - '0';
	}
	if	(s == e)
		return (float)(integer * sign);

	s++;
   	while ((s < e) && (s[0] != 'e'))
   	{
   		if	((s[0] != '-') && ((s[0] < '0') || (s[0] > '9')))
   			return	sign * (integer + (float)real / tt);
   		real *= 10.f;
   		real += *s++ - '0';
   		tt *= 10.f;
   	}
   	if	(s == e)
   		return sign * (integer + real / tt);

exponent:;

	s++;
	if	(s == e)
		return (float)(integer * sign);

	int expsign = (*s++ == '-') ? -1 : 1;

	if	(s == e)
		return (float)(integer * sign);

	uint exp = 0;
	float mulexp = 1;

	while ((s < e) && (s[0] >= '0') && (s[0] <= '9'))
	{
		exp *= 10;
		exp += *s++ - '0';
	}
	for	(uint expc = 0; expc < exp; expc++)
		mulexp *= 10.f;

	if	(expsign == -1)
		return (float)(sign * (integer + (float)real / tt)) / mulexp;
	return (float)(sign * (integer + (float)real / tt)) * mulexp;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::FileCleanName()
{
	if	(!_p)
		return false;

	char *f = _p;
	Array <char> n(Len() + 1, Alloc::StringBuffer);
	if	(n.IsNull())
		return false;

	// Remove drive letter case under the Windows platform.
#if	__PLATFORM_WINDOWS__
	if	((Len() > 2) && (f[1] == ':'))
		if	((f[0] >= 'A') && (f[0] <= 'Z'))
			f[0] += 'a' - 'A';
#endif

	// Convert directory separator from backslash to forward slash.
	char *c = n;
	for	(; f[0]; ++f)
		if (f[0] == '\\')
			f[0] = '/';

	// Remove redundant forward slashes.
	f = _p;
	while   (f[0])
	{
		if  (f[0] == '/')
		{
			f++;
			while (f[0] && (f[0] == '/'))
				f++;
			*c++ = '/';
		}
		else
			*c++ = *f++;
    }
    c[0] = 0;

	// Replace illegal characters.
	f = n;
#if	__PLATFORM_WINDOWS__
	if	(!strcmp(f, "file:/"))
		f += 6;
#endif

	if	(f[0] == '@')		// Allow internal mount points.
		f++;

	for	(int i = 0; f[i]; )
	{
		uint	char_size = GetUtf8CharSize((const uchar *)&f[i]);

		if	(char_size == 1)
			if	(
					// Allowed ranges.
					!((f[i] >= 'a') && (f[i] <= 'z')) &&
					!((f[i] >= 'A') && (f[i] <= 'Z')) &&
					!((f[i] >= '0') && (f[i] <= '9')) &&

					// Allowed characters.
					(f[i] != ' ') && (f[i] != '.') && (f[i] != '_') && (f[i] != '/') &&
					(f[i] != '-') && (f[i] != '!') && (f[i] != '+') && (f[i] != '(') &&
					(f[i] != ')') && (f[i] != '#') && (f[i] != '\'') && (f[i] != '&')

					// Windows drive letter.
#if	__PLATFORM_WINDOWS__
					&& !((f[i] == ':') && (i == 1))
#endif
				)
				f[i] = '_';

		i += char_size;
	}

	// Now commit.
	Clear();
	_p = n;
	Touch();
	return true;
}
String		String::FileGetExtension(const char *path)
{
	if	(!path)
		return "";

	int n = (int)strlen(path);
	while (path[n] != '.' && (n >= 0))
		n--;

	return n < 0 ? "" : &path[n + 1];
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t		String::strlen(const char *s)
{
	size_t n = 0;
	if	(s)
		for ( ; s[n]; ++n);
	return n;
}
bool		String::strccmp(const char *sa, const char *sb)
{
	if	(!sa || !sb)
		return false;

	while (sa[0] && sb[0])
		if	(*sa++ != *sb++)
			return true;

	return false;
}
char		*String::strfindchar(const char *f, const char c, const char *t)
{
	if	(t)
		while (f[0] && (f[0] != c) && (f < t))
			f++;
	else
		while (f[0] && (f[0] != c))
			f++;

	return const_cast <char *> (f);
}
void		String::rmname(char *s)
{
	if	(!s)
		return;
	int		n = (int)strlen(s) - 1;
	while	((n >= 0) && (s[n] != ':') && (s[n] != '\\') && (s[n] != '/'))
		n--;
	if	(n < 0)
		s[0] = 0;
	else
		if	(s[n + 1])
			s[n + 1] = 0;
}

void		String::rmpath(char *s)
{
	String	tmp(s);

	int n = (int)tmp.Len();
	const char *p = tmp.c_str();
	if	(!p)
		return;

	while	((n >= 0) && (p[n] != '\\') && (p[n] != '/'))
		n--;
	if	(n < 0)
		return;

	strcpy(s, &p[n+1]);
}
void		String::rmext(char *s)
{
	if	(!s)
		return;

	int n = (int)strlen(s) - 1;
	while ((n >= 0) && (s[n] != '.'))
	{
		if	((s[n] == '\\') || (s[n] == '/') || (s[n] == ':'))
			break;
		n--;
	}
	if	(n < 0)
		return;
	s[n] = 0;
}
void		String::rmpathext(char *s)
{
	rmpath(s);
	rmext(s);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::Allocate(uint size)
{
	Touch();
	if	(!_p.Allocate(size + 1))
		return false;

	Memory::Set(_p.c_ptr(), 0, _p.GetSize());
	return true;
}
void		String::Touch()
{
	len = -1;
	hash = 0;
}
void		String::Clear()
{
	_p.Free();
	Touch();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::equals(const char *s, const uint hash) const
{
	if	(hash && (hash != Hash()))
		return false;
	if	(_p == s)
		return true;
	return (_p && !strcmp(s, _p)) ? true : false;
}
bool		String::operator == (const String &b) const
{	return b.IsEmpty() ? IsEmpty() : equals(b, b.Hash());	}
bool		String::operator == (const char *s) const
{	return s ? equals(s) : IsEmpty();	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool		String::operator != (const String &b) const
{	return !(*this == b);	}
bool		String::operator != (const char *s) const
{	return !(*this == s);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void		String::operator += (const String &b)
{
	if	(!b.IsEmpty())
		*this += b.c_str();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uint		String::strhash(const char *s)
{
	uint hash = 0;

	if	(s)
		while (s[0])
		{
			hash = hash * 37 + s[0];
			s++;
		}

	hash |= 1;
	return hash;
}
uint		String::Hash() const
{
	if	(hash == 0)
		hash = strhash(_p);
	return hash;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void		String::FileCutPath()
{	rmpath(_p); Touch();	}
void		String::FileSwapExtension(const char *ext)
{	rmext(_p); Touch(); *this += ext;	}
void		String::FileCutExtension()
{	rmext(_p); Touch();	}
void		String::FileCutPathAndExtension()
{	rmpathext(_p); Touch();	}
void		String::FileCutName()
{	rmname(_p); Touch();	}
//------------------------------------------------------------------------------
