#include <tier0/dbg.h>
#include <vstdlib/strtools.h>
#include <tier1/utlvector.h>

#ifdef _WIN32
#include "winlite.h"
#endif

#ifdef POSIX
	#include <unistd.h>
#endif

#include "tier0/memdbgon.h"

#if defined( _WIN32 ) || defined( WIN32 )
#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
#else	
#define PATHSEPARATOR(c) ('/' == (c))
#endif	

static char* AllocString( const char *pStr, int nMaxChars )
{
	int allocLen;
	if ( nMaxChars == -1 )
		allocLen = V_strlen( pStr ) + 1;
	else
		allocLen = Min( V_strlen(pStr), nMaxChars ) + 1;

	char *pOut = new char[allocLen];
	V_strncpy( pOut, pStr, allocLen );
	return pOut;
}

int V_strncmp( const char *s1, const char *s2, int count )
{
	Assert( count >= 0 );
	Assert( count == 0 || s1 != NULL );
	Assert( count == 0 || s2 != NULL );

	while ( count-- > 0 )
	{
		if ( *s1 != *s2 )
			return *s1 < *s2 ? -1 : 1; 
		if ( *s1 == '\0' )
			return 0; 
		s1++;
		s2++;
	}

	return 0; 
}

char const* V_strnistr( char const* pStr, char const* pSearch, int n )
{
	Assert( pStr != NULL );
	Assert( pSearch != NULL );

	if ( pStr == NULL || pSearch == NULL ) 
		return 0;

	char const* pLetter = pStr;

	while ( *pLetter != 0 )
	{
		if ( n <= 0 )
			return 0;

		if ( tolower( *pLetter ) == tolower( *pSearch ) )
		{
			int n1 = n - 1;

			char const* pMatch = pLetter + 1;
			char const* pTest = pSearch + 1;
			while (*pTest != 0)
			{
				if ( n1 <= 0 )
					return 0;

				if (*pMatch == 0)
					return 0;

				if ( tolower( *pMatch ) != tolower( *pTest ) )
					break;

				++pMatch;
				++pTest;
				--n1;
			}

			if (*pTest == 0)
				return pLetter;
		}

		++pLetter;
		--n;
	}

	return 0;
}

const char* V_strnchr( const char* pStr, char c, int n )
{
	char const* pLetter = pStr;
	char const* pLast = pStr + n;

	while ( (pLetter < pLast) && (*pLetter != 0) )
	{
		if (*pLetter == c)
			return pLetter;
		++pLetter;
	}
	return NULL;
}

int V_strnicmp( const char *s1, const char *s2, int n )
{
	Assert( n >= 0 );
	Assert( n == 0 || s1 != NULL );
	Assert( n == 0 || s2 != NULL );

	while ( n-- > 0 )
	{
		int c1 = *s1++;
		int c2 = *s2++;

		if ( c1 != c2 )
		{
			if ( c1 >= 'a' && c1 <= 'z' )
				c1 -= ('a' - 'A');
			if ( c2 >= 'a' && c2 <= 'z' )
				c2 -= ( 'a' - 'A' );
			if ( c1 != c2 )
				return c1 < c2 ? -1 : 1;
		}
		if ( c1 == '\0' )
			return 0; 
	}

	return 0; 
}

char const* V_stristr( char const* pStr, char const* pSearch )
{
	Assert( pStr != NULL );
	Assert( pSearch != NULL );

	if ( pStr == NULL || pSearch == NULL ) 
		return NULL;

	char const* pLetter = pStr;

	while ( *pLetter != 0 )
	{

		if ( tolower( (unsigned char) *pLetter ) == tolower( (unsigned char) *pSearch ) )
		{

			char const* pMatch = pLetter + 1;
			char const* pTest = pSearch + 1;
			while (*pTest != 0)
			{

				if (*pMatch == 0)
					return 0;

				if ( tolower( (unsigned char) *pMatch ) != tolower( (unsigned char) *pTest ) )
					break;

				++pMatch;
				++pTest;
			}

			if ( *pTest == 0 )
				return pLetter;
		}

		++pLetter;
	}

	return 0;
}

void V_strncpy( char *pDest, char const *pSrc, size_t maxLen )
{
	Assert( maxLen == 0 || pDest != NULL );
	Assert( pSrc != NULL );

	size_t nCount = maxLen;
	char *pstrDest = pDest;
	const char *pstrSource = pSrc;

	while ( 0 < nCount && 0 != ( *pstrDest++ = *pstrSource++ ) )
		nCount--;

	if ( maxLen > 0 )
		pstrDest[-1] = 0;
}

int V_snprintf( char *pDest, int bufferLen, char const *pFormat, ... )
{
	Assert( bufferLen > 0 );
	Assert( pDest != NULL );
	Assert( pFormat != NULL );

	va_list marker;

	va_start( marker, pFormat );

	int len = _vsnprintf( pDest, bufferLen, pFormat, marker );
	va_end( marker );

	if (( len < 0 ) || (len >= bufferLen ) )
	{
		len = bufferLen-1;
	}
	pDest[len] = 0;

	return len;
}

int V_vsnprintf( char *pDest, int bufferLen, char const *pFormat, va_list params )
{
	Assert( bufferLen > 0 );
	Assert( pDest != NULL );
	Assert( pFormat != NULL );

	int len = _vsnprintf( pDest, bufferLen, pFormat, params );

	if (( len < 0 ) || (len >= bufferLen ) )
	{
		len = bufferLen-1;
	}
	pDest[len] = 0;

	return len;
}

int V_vsnprintfRet( char *pDest, int bufferLen, const char *pFormat, va_list params, bool *pbTruncated )
{
	Assert( bufferLen > 0 );
	Assert( bufferLen == 0 || pDest != NULL );
	Assert( pFormat != NULL );

	bool bTruncatedUnderstudy;
	if ( !pbTruncated )
		pbTruncated = &bTruncatedUnderstudy;

	int len = _vsnprintf( pDest, bufferLen, pFormat, params );

	if (( len < 0 ) || (len >= bufferLen ) )
	{
		*pbTruncated = true;
		len = bufferLen-1;
	}
	else
	{
		*pbTruncated = false;
	}

	pDest[len] = 0;

	return len;
}

char *V_strncat(char *pDest, const char *pSrc, size_t destBufferSize, int max_chars_to_copy )
{
	size_t charstocopy = (size_t)0;

	Assert( pDest != NULL );
	Assert( pSrc != NULL );

	size_t len = strlen(pDest);
	size_t srclen = strlen( pSrc );
	if ( max_chars_to_copy <= COPY_ALL_CHARACTERS )
	{
		charstocopy = srclen;
	}
	else
	{
		charstocopy = (size_t)Min( max_chars_to_copy, (int)srclen );
	}

	if ( len + charstocopy >= destBufferSize )
	{
		charstocopy = destBufferSize - len - 1;
	}

	if ( (ptrdiff_t)charstocopy <= 0 )
	{
		return pDest;
	}

	char *pOut = strncat( pDest, pSrc, charstocopy );
	return pOut;
}

void V_SplitString2( const char *pString, const char * const *pSeparators, int nSeparators, CUtlVector<char*> &outStrings, bool bIncludeEmptyStrings )
{
	outStrings.Purge();
	const char *pCurPos = pString;
	for (;;)
	{
		int iFirstSeparator = -1;
		const char *pFirstSeparator = 0;
		for ( int i=0; i < nSeparators; i++ )
		{
			const char *pTest = V_stristr( pCurPos, pSeparators[i] );
			if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) )
			{
				iFirstSeparator = i;
				pFirstSeparator = pTest;
			}
		}

		if ( pFirstSeparator )
		{

			int separatorLen = (int)strlen( pSeparators[iFirstSeparator] );
			if ( pFirstSeparator > pCurPos || ( pFirstSeparator == pCurPos && bIncludeEmptyStrings ) )
			{
				outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) );
			}

			pCurPos = pFirstSeparator + separatorLen;
		}
		else
		{

			if ( pCurPos[0] != 0 )
			{
				outStrings.AddToTail( AllocString( pCurPos, -1 ) );
			}
			return;
		}
	}
}

void V_SplitString( const char *pString, const char *pSeparator, CUtlVector<char*> &outStrings, bool bIncludeEmptyStrings )
{
	V_SplitString2( pString, &pSeparator, 1, outStrings, bIncludeEmptyStrings );
}

void V_StripTrailingWhitespaceASCII( char *pch )
{
	if ( !pch )
		return;

	char *t = pch;

	while ( *pch != '\0' )
	{

		if ( !V_isspace( *pch ) )
			t = pch+1;
		++pch;
	}

	*t = '\0';
}

int V_StrTrim( char *pStr )
{
	char *pSource = pStr;
	char *pDest = pStr;

	while ( *pSource != 0 && isspace( *pSource ) )
	{
		pSource++;
	}

	char *pLastWhiteBlock = NULL;
	char *pStart = pDest;
	while ( *pSource != 0 )
	{
		*pDest = *pSource++;
		if ( isspace( *pDest ) )
		{
			if ( pLastWhiteBlock == NULL )
				pLastWhiteBlock = pDest;
		}
		else
		{
			pLastWhiteBlock = NULL;
		}
		pDest++;
	}
	*pDest = 0;

	if ( pLastWhiteBlock != NULL )
	{

		pDest = pLastWhiteBlock;
		*pLastWhiteBlock = 0;
	}

	return pDest - pStart;
}

void V_AppendSlash( char *pStr, int strSize )
{
	int len = V_strlen( pStr );
	if ( len > 0 && !PATHSEPARATOR(pStr[len-1]) )
	{
		if ( len+1 >= strSize )
			Error( "V_AppendSlash: ran out of space on %s.", pStr );

		pStr[len] = CORRECT_PATH_SEPARATOR;
		pStr[len+1] = 0;
	}
}

void V_FixSlashes( char *pname, char separator  )
{
	while ( *pname )
	{
		if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR )
		{
			*pname = separator;
		}
		pname++;
	}
}

void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize )
{
	V_strncpy( dest, path, destSize );
	V_AppendSlash( dest, destSize );
	V_strncat( dest, filename, destSize, COPY_ALL_CHARACTERS );
	V_FixSlashes( dest );
}

bool V_RemoveDotSlashes( char *pFilename, char separator )
{

	V_FixSlashes( pFilename, separator );	

	int len = (int)strlen( pFilename );

	int i=0;
	while ( i <= (len-3) )
	{
		if ( pFilename[i] == separator && pFilename[i+1] == '.' && pFilename[i+2] == separator )
		{
			memmove( pFilename+i, pFilename+i+2, len-i-1 );
			len-=2; 
		}
        else
        {
            i++;
        }
	}

	if ( len > 2 && pFilename[len-1] == '.' && separator == pFilename[len-2] )
	{
		pFilename[len-1] = 0;
		len--;
	}

	if ( len > 1 && pFilename[0] == '.' && pFilename[1] == separator )
	{
		memmove( pFilename, pFilename+2, len-1 );
		len-=2;
	}

	char *pIn = pFilename;
	while ( *pIn )
	{
		if ( pIn[0] == '.' && 
			 pIn[1] == '.' && 
			 (pIn == pFilename || separator == pIn[-1] ) &&	
			 (pIn[2] == 0 || separator == pIn[2] ) )			
		{

			if ( pIn == pFilename || pIn - 1 == pFilename )
				return false;

			char *pEndOfDots = pIn + 2;
			char *pStart = pIn - 2;

			while ( pStart > pFilename && separator != *pStart )
			{
				--pStart;
			}

			memmove( pStart, pEndOfDots, strlen( pEndOfDots ) + 1 );

			pIn = pFilename;
		}
		else
		{
			++pIn;
		}
	}

	return true;
}

bool V_IsAbsolutePath( const char *pStr )
{
	return strchr( pStr, ':' ) != NULL || pStr[0] == '/' || pStr[0] == '\\';
}

#ifdef _WIN32

static __success( return != 0 ) DWORD ConvertWideToUTF8( __in_z const wchar_t *pwszNullTerm, __out_ecount_part_z_opt( nMaxOut, return ) char *pchOut, __in DWORD nMaxOut )
{
	if ( !pchOut )
		return WideCharToMultiByte( CP_UTF8, 0, pwszNullTerm, -1, NULL, 0, NULL, NULL );

	if ( !nMaxOut )
		return 0;

	DWORD ret = WideCharToMultiByte( CP_UTF8, 0, pwszNullTerm, -1, pchOut, nMaxOut, NULL, NULL );
	if ( ret == 0 )
	{

		pchOut[ nMaxOut - 1 ] = 0;
	}
	return ret;
}

char *getcwd_utf8( char *pszUtf8PathOut, size_t size )
{

	wchar_t *pwszMallocTemp = _wgetcwd( NULL, 0 );
	if ( !pwszMallocTemp )
		return NULL;

	DWORD nBytesRequired = ConvertWideToUTF8( pwszMallocTemp, NULL, 0 );
	if ( !pszUtf8PathOut )
	{
		if ( size < nBytesRequired )
			size = nBytesRequired;

		pszUtf8PathOut = (char*) malloc( size );
	}

	ConvertWideToUTF8( pwszMallocTemp, pszUtf8PathOut, (DWORD)MIN( size, (size_t)INT_MAX ) );
	free( pwszMallocTemp );

	if ( size < nBytesRequired )
	{

		errno = ERANGE;
		return NULL;
	}

	return pszUtf8PathOut;
}

#elif defined(OSX) || defined(LINUX) || defined(POSIX)
	#define getcwd_utf8 getcwd
#else
	#error
#endif

void V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir )
{
	if ( V_IsAbsolutePath( pPath ) )
	{

		V_strncpy( pOut, pPath, outLen );
	}
	else
	{

		if ( pStartingDir && V_IsAbsolutePath( pStartingDir ) )
		{
			V_strncpy( pOut, pStartingDir, outLen );
		}
		else
		{
			if ( !getcwd_utf8( pOut, outLen ) )
			{
				AssertMsg( false, "V_MakeAbsolutePath: _getcwd failed." );
				if ( outLen )
					*pOut = 0;
			}

			if ( pStartingDir )
			{
				V_AppendSlash( pOut, outLen );
				V_strncat( pOut, pStartingDir, outLen, COPY_ALL_CHARACTERS );
			}
		}

		V_AppendSlash( pOut, outLen );
		V_strncat( pOut, pPath, outLen, COPY_ALL_CHARACTERS );
	}

	if ( !V_RemoveDotSlashes( pOut ) )
		AssertMsg1( false, "V_RemoveDotSlashes failed for \"%s\".", pOut );
}
//========= Copyright Valve Corporation, All rights reserved. ============//

#include <tier0/dbg.h>
#include <vstdlib/strtools.h>
#include <tier1/utlvector.h>

#ifdef _WIN32
#include "winlite.h"
#endif

#ifdef POSIX
	#include <unistd.h>
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

#if defined( _WIN32 ) || defined( WIN32 )
#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
#else	//_WIN32
#define PATHSEPARATOR(c) ('/' == (c))
#endif	//_WIN32



static char* AllocString( const char *pStr, int nMaxChars )
{
	int allocLen;
	if ( nMaxChars == -1 )
		allocLen = V_strlen( pStr ) + 1;
	else
		allocLen = Min( V_strlen(pStr), nMaxChars ) + 1;

	char *pOut = new char[allocLen];
	V_strncpy( pOut, pStr, allocLen );
	return pOut;
}

int V_strncmp( const char *s1, const char *s2, int count )
{
	Assert( count >= 0 );
	Assert( count == 0 || s1 != NULL );
	Assert( count == 0 || s2 != NULL );

	while ( count-- > 0 )
	{
		if ( *s1 != *s2 )
			return *s1 < *s2 ? -1 : 1; // string different
		if ( *s1 == '\0' )
			return 0; // null terminator hit - strings the same
		s1++;
		s2++;
	}

	return 0; // count characters compared the same
}

//-----------------------------------------------------------------------------
// Finds a string in another string with a case insensitive test w/ length validation
//-----------------------------------------------------------------------------
char const* V_strnistr( char const* pStr, char const* pSearch, int n )
{
	Assert( pStr != NULL );
	Assert( pSearch != NULL );

	if ( pStr == NULL || pSearch == NULL ) 
		return 0;

	char const* pLetter = pStr;

	// Check the entire string
	while ( *pLetter != 0 )
	{
		if ( n <= 0 )
			return 0;

		// Skip over non-matches
		if ( tolower( *pLetter ) == tolower( *pSearch ) )
		{
			int n1 = n - 1;

			// Check for match
			char const* pMatch = pLetter + 1;
			char const* pTest = pSearch + 1;
			while (*pTest != 0)
			{
				if ( n1 <= 0 )
					return 0;

				// We've run off the end; don't bother.
				if (*pMatch == 0)
					return 0;

				if ( tolower( *pMatch ) != tolower( *pTest ) )
					break;

				++pMatch;
				++pTest;
				--n1;
			}

			// Found a match!
			if (*pTest == 0)
				return pLetter;
		}

		++pLetter;
		--n;
	}

	return 0;
}

const char* V_strnchr( const char* pStr, char c, int n )
{
	char const* pLetter = pStr;
	char const* pLast = pStr + n;

	// Check the entire string
	while ( (pLetter < pLast) && (*pLetter != 0) )
	{
		if (*pLetter == c)
			return pLetter;
		++pLetter;
	}
	return NULL;
}

int V_strnicmp( const char *s1, const char *s2, int n )
{
	Assert( n >= 0 );
	Assert( n == 0 || s1 != NULL );
	Assert( n == 0 || s2 != NULL );
	
	while ( n-- > 0 )
	{
		int c1 = *s1++;
		int c2 = *s2++;

		if ( c1 != c2 )
		{
			if ( c1 >= 'a' && c1 <= 'z' )
				c1 -= ('a' - 'A');
			if ( c2 >= 'a' && c2 <= 'z' )
				c2 -= ( 'a' - 'A' );
			if ( c1 != c2 )
				return c1 < c2 ? -1 : 1;
		}
		if ( c1 == '\0' )
			return 0; // null terminator hit - strings the same
	}
	
	return 0; // n characters compared the same
}

//-----------------------------------------------------------------------------
// Finds a string in another string with a case insensitive test
//-----------------------------------------------------------------------------
char const* V_stristr( char const* pStr, char const* pSearch )
{
	Assert( pStr != NULL );
	Assert( pSearch != NULL );

	if ( pStr == NULL || pSearch == NULL ) 
		return NULL;

	char const* pLetter = pStr;

	// Check the entire string
	while ( *pLetter != 0 )
	{
		// Skip over non-matches
		if ( tolower( (unsigned char) *pLetter ) == tolower( (unsigned char) *pSearch ) )
		{
			// Check for match
			char const* pMatch = pLetter + 1;
			char const* pTest = pSearch + 1;
			while (*pTest != 0)
			{
				// We've run off the end; don't bother.
				if (*pMatch == 0)
					return 0;

				if ( tolower( (unsigned char) *pMatch ) != tolower( (unsigned char) *pTest ) )
					break;

				++pMatch;
				++pTest;
			}

			// Found a match!
			if ( *pTest == 0 )
				return pLetter;
		}

		++pLetter;
	}

	return 0;
}

//-----------------------------------------------------------------------------
// Purpose: copy a string while observing the maximum length of the target buffer
//
// Inputs:	pDest	- pointer to the destination string
//		pSrc	- pointer to the source string
//		maxLen	- number of bytes available at pDest
//
// This function will always leave pDest nul-terminated if maxLen > 0
//
// maxLen is a count of bytes, not a count of characters.
// maxLen includes budget for the nul terminator at pDest,
// so using maxLen = 3 would copy two characters from pSrc and add a trailing nul.
//-----------------------------------------------------------------------------
void V_strncpy( char *pDest, char const *pSrc, size_t maxLen )
{
	Assert( maxLen == 0 || pDest != NULL );
	Assert( pSrc != NULL );

	size_t nCount = maxLen;
	char *pstrDest = pDest;
	const char *pstrSource = pSrc;

	while ( 0 < nCount && 0 != ( *pstrDest++ = *pstrSource++ ) )
		nCount--;

	if ( maxLen > 0 )
		pstrDest[-1] = 0;
}

int V_snprintf( char *pDest, int bufferLen, char const *pFormat, ... )
{
	Assert( bufferLen > 0 );
	Assert( pDest != NULL );
	Assert( pFormat != NULL );

	va_list marker;

	va_start( marker, pFormat );
	// _vsnprintf will not write a terminator if the output string uses the entire buffer you provide
	int len = _vsnprintf( pDest, bufferLen, pFormat, marker );
	va_end( marker );

	// Len < 0 represents an overflow on windows; len > buffer length on posix
	if (( len < 0 ) || (len >= bufferLen ) )
	{
		len = bufferLen-1;
	}
	pDest[len] = 0;

	return len;
}

int V_vsnprintf( char *pDest, int bufferLen, char const *pFormat, va_list params )
{
	Assert( bufferLen > 0 );
	Assert( pDest != NULL );
	Assert( pFormat != NULL );

	int len = _vsnprintf( pDest, bufferLen, pFormat, params );

	// Len < 0 represents an overflow on windows; len > buffer length on posix
	if (( len < 0 ) || (len >= bufferLen ) )
	{
		len = bufferLen-1;
	}
	pDest[len] = 0;

	return len;
}

int V_vsnprintfRet( char *pDest, int bufferLen, const char *pFormat, va_list params, bool *pbTruncated )
{
	Assert( bufferLen > 0 );
	Assert( bufferLen == 0 || pDest != NULL );
	Assert( pFormat != NULL );

	bool bTruncatedUnderstudy;
	if ( !pbTruncated )
		pbTruncated = &bTruncatedUnderstudy;

	int len = _vsnprintf( pDest, bufferLen, pFormat, params );

	// Len < 0 represents an overflow on windows; len > buffer length on posix
	if (( len < 0 ) || (len >= bufferLen ) )
	{
		*pbTruncated = true;
		len = bufferLen-1;
	}
	else
	{
		*pbTruncated = false;
	}

	pDest[len] = 0;

	return len;
}

//-----------------------------------------------------------------------------
// Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise
//  we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less).
// Input  : *pDest - destination buffer
//			*pSrc - string to append
//			destBufferSize - sizeof the buffer pointed to by pDest
//			max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy
// Output : char * the copied buffer
//-----------------------------------------------------------------------------
char *V_strncat(char *pDest, const char *pSrc, size_t destBufferSize, int max_chars_to_copy )
{
	size_t charstocopy = (size_t)0;

	Assert( pDest != NULL );
	Assert( pSrc != NULL );
	
	size_t len = strlen(pDest);
	size_t srclen = strlen( pSrc );
	if ( max_chars_to_copy <= COPY_ALL_CHARACTERS )
	{
		charstocopy = srclen;
	}
	else
	{
		charstocopy = (size_t)Min( max_chars_to_copy, (int)srclen );
	}

	if ( len + charstocopy >= destBufferSize )
	{
		charstocopy = destBufferSize - len - 1;
	}

	if ( (ptrdiff_t)charstocopy <= 0 )
	{
		return pDest;
	}

	char *pOut = strncat( pDest, pSrc, charstocopy );
	return pOut;
}

void V_SplitString2( const char *pString, const char * const *pSeparators, int nSeparators, CUtlVector<char*> &outStrings, bool bIncludeEmptyStrings )
{
	outStrings.Purge();
	const char *pCurPos = pString;
	for (;;)
	{
		int iFirstSeparator = -1;
		const char *pFirstSeparator = 0;
		for ( int i=0; i < nSeparators; i++ )
		{
			const char *pTest = V_stristr( pCurPos, pSeparators[i] );
			if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) )
			{
				iFirstSeparator = i;
				pFirstSeparator = pTest;
			}
		}

		if ( pFirstSeparator )
		{
			// Split on this separator and continue on.
			int separatorLen = (int)strlen( pSeparators[iFirstSeparator] );
			if ( pFirstSeparator > pCurPos || ( pFirstSeparator == pCurPos && bIncludeEmptyStrings ) )
			{
				outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) );
			}

			pCurPos = pFirstSeparator + separatorLen;
		}
		else
		{
			// Copy the rest of the string, if there's anything there
			if ( pCurPos[0] != 0 )
			{
				outStrings.AddToTail( AllocString( pCurPos, -1 ) );
			}
			return;
		}
	}
}

void V_SplitString( const char *pString, const char *pSeparator, CUtlVector<char*> &outStrings, bool bIncludeEmptyStrings )
{
	V_SplitString2( pString, &pSeparator, 1, outStrings, bIncludeEmptyStrings );
}

//-----------------------------------------------------------------------------
void V_StripTrailingWhitespaceASCII( char *pch )
{
	if ( !pch )
		return;

	// Remember where we would terminate the string
	char *t = pch;

	// Scan until we hit the end of the string
	while ( *pch != '\0' )
	{

		// Non-whitespace?  Then assume termination immediately
		// after, if we don't find any more whitespace
		if ( !V_isspace( *pch ) )
			t = pch+1;
		++pch;
	}

	// Terminate
	*t = '\0';
}

int V_StrTrim( char *pStr )
{
	char *pSource = pStr;
	char *pDest = pStr;

	// skip white space at the beginning
	while ( *pSource != 0 && isspace( *pSource ) )
	{
		pSource++;
	}

	// copy everything else
	char *pLastWhiteBlock = NULL;
	char *pStart = pDest;
	while ( *pSource != 0 )
	{
		*pDest = *pSource++;
		if ( isspace( *pDest ) )
		{
			if ( pLastWhiteBlock == NULL )
				pLastWhiteBlock = pDest;
		}
		else
		{
			pLastWhiteBlock = NULL;
		}
		pDest++;
	}
	*pDest = 0;

	// did we end in a whitespace block?
	if ( pLastWhiteBlock != NULL )
	{
		// yep; shorten the string
		pDest = pLastWhiteBlock;
		*pLastWhiteBlock = 0;
	}

	return pDest - pStart;
}

void V_AppendSlash( char *pStr, int strSize )
{
	int len = V_strlen( pStr );
	if ( len > 0 && !PATHSEPARATOR(pStr[len-1]) )
	{
		if ( len+1 >= strSize )
			Error( "V_AppendSlash: ran out of space on %s.", pStr );
		
		pStr[len] = CORRECT_PATH_SEPARATOR;
		pStr[len+1] = 0;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Changes all '/' or '\' characters into separator
// Input  : *pname - 
//			separator - 
//-----------------------------------------------------------------------------
void V_FixSlashes( char *pname, char separator /* = CORRECT_PATH_SEPARATOR */ )
{
	while ( *pname )
	{
		if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR )
		{
			*pname = separator;
		}
		pname++;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Composes a path and filename together, inserting a path separator
//			if need be
// Input:	path - path to use
//			filename - filename to use
//			dest - buffer to compose result in
//			destSize - size of destination buffer
//-----------------------------------------------------------------------------
void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize )
{
	V_strncpy( dest, path, destSize );
	V_AppendSlash( dest, destSize );
	V_strncat( dest, filename, destSize, COPY_ALL_CHARACTERS );
	V_FixSlashes( dest );
}


bool V_RemoveDotSlashes( char *pFilename, char separator )
{
	// make all slashes the requested separator
	V_FixSlashes( pFilename, separator );	

	int len = (int)strlen( pFilename );

	// strip all "/./" and replace by "/"
	int i=0;
	while ( i <= (len-3) )
	{
		if ( pFilename[i] == separator && pFilename[i+1] == '.' && pFilename[i+2] == separator )
		{
			memmove( pFilename+i, pFilename+i+2, len-i-1 );
			len-=2; // stripped /. leaving the /
		}
        else
        {
            i++;
        }
	}
	
	// Get rid of a trailing "/." (needless), but leave the path separator.
	if ( len > 2 && pFilename[len-1] == '.' && separator == pFilename[len-2] )
	{
		pFilename[len-1] = 0;
		len--;
	}

	// get rid of leading "./"
	if ( len > 1 && pFilename[0] == '.' && pFilename[1] == separator )
	{
		memmove( pFilename, pFilename+2, len-1 );
		len-=2;
	}

	// Each time we encounter a "..", back up until we've read the previous directory name,
	// then get rid of it.
	char *pIn = pFilename;
	while ( *pIn )
	{
		if ( pIn[0] == '.' && 
			 pIn[1] == '.' && 
			 (pIn == pFilename || separator == pIn[-1] ) &&	// Preceding character must be a slash.
			 (pIn[2] == 0 || separator == pIn[2] ) )			// Following character must be a slash or the end of the string.
		{
			// If there is no previous directory we cannot go up
			if ( pIn == pFilename || pIn - 1 == pFilename )
				return false;

			char *pEndOfDots = pIn + 2;
			char *pStart = pIn - 2;

			// Ok, now scan back for the path separator that starts the preceding directory.
			while ( pStart > pFilename && separator != *pStart )
			{
				--pStart;
			}

			// Now slide the string down to get rid of the previous directory and the ".."
			memmove( pStart, pEndOfDots, strlen( pEndOfDots ) + 1 );

			// Start over.
			pIn = pFilename;
		}
		else
		{
			++pIn;
		}
	}
	
	return true;
}

// small helper function shared by lots of modules
bool V_IsAbsolutePath( const char *pStr )
{
	return strchr( pStr, ':' ) != NULL || pStr[0] == '/' || pStr[0] == '\\';
}



#ifdef _WIN32

static __success( return != 0 ) DWORD ConvertWideToUTF8( __in_z const wchar_t *pwszNullTerm, __out_ecount_part_z_opt( nMaxOut, return ) char *pchOut, __in DWORD nMaxOut )
{
	if ( !pchOut )
		return WideCharToMultiByte( CP_UTF8, 0, pwszNullTerm, -1, NULL, 0, NULL, NULL );
		
	if ( !nMaxOut )
		return 0;

	DWORD ret = WideCharToMultiByte( CP_UTF8, 0, pwszNullTerm, -1, pchOut, nMaxOut, NULL, NULL );
	if ( ret == 0 )
	{
		// XXX is it better to generate incomplete UTF-8 or to drop multiple bytes?
		// we'll go with incomplete UTF-8 since the return value is bogus anyway.
		pchOut[ nMaxOut - 1 ] = 0;
	}
	return ret;
}

char *getcwd_utf8( char *pszUtf8PathOut, size_t size )
{
	// explicitly defined to return a malloc'd buffer
	wchar_t *pwszMallocTemp = _wgetcwd( NULL, 0 );
	if ( !pwszMallocTemp )
		return NULL;

	DWORD nBytesRequired = ConvertWideToUTF8( pwszMallocTemp, NULL, 0 );
	if ( !pszUtf8PathOut )
	{
		if ( size < nBytesRequired )
			size = nBytesRequired;

		// explicitly defined to return a CRT malloc'd buffer
		pszUtf8PathOut = (char*) malloc( size );
	}

	ConvertWideToUTF8( pwszMallocTemp, pszUtf8PathOut, (DWORD)MIN( size, (size_t)INT_MAX ) );
	free( pwszMallocTemp );

	if ( size < nBytesRequired )
	{
		// won't get hit in our malloc case, we made sure size >= nBytesRequired
		errno = ERANGE;
		return NULL;
	}

	return pszUtf8PathOut;
}

#elif defined(OSX) || defined(LINUX) || defined(POSIX)
	#define getcwd_utf8 getcwd
#else
	#error
#endif

void V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir )
{
	if ( V_IsAbsolutePath( pPath ) )
	{
		// pPath is not relative.. just copy it.
		V_strncpy( pOut, pPath, outLen );
	}
	else
	{
		// Make sure the starting directory is absolute..
		if ( pStartingDir && V_IsAbsolutePath( pStartingDir ) )
		{
			V_strncpy( pOut, pStartingDir, outLen );
		}
		else
		{
			if ( !getcwd_utf8( pOut, outLen ) )
			{
				AssertMsg( false, "V_MakeAbsolutePath: _getcwd failed." );
				if ( outLen )
					*pOut = 0;
			}

			if ( pStartingDir )
			{
				V_AppendSlash( pOut, outLen );
				V_strncat( pOut, pStartingDir, outLen, COPY_ALL_CHARACTERS );
			}
		}

		// Concatenate the paths.
		V_AppendSlash( pOut, outLen );
		V_strncat( pOut, pPath, outLen, COPY_ALL_CHARACTERS );
	}

	// this will also fix slashes
	if ( !V_RemoveDotSlashes( pOut ) )
		AssertMsg1( false, "V_RemoveDotSlashes failed for \"%s\".", pOut );
}
