#include "pstypes.h"

/* It is a condition of use that safe_strings.cpp, safe_strings.h, safe_strings_test.cpp remain together.
 *
 * Maintained by portej05 - contact via PM on www.hard-light.net/forums
 * Why have we got this, what is it for?
 * VC2005+ define some safe string functions which check buffer sizes before doing anything
 * Unfortunately, GCC (on Linux and Mac OS X) does not yet provide these functions, therefore, we must!
 * (if only to reduce the amount of noise the static analysis tools are spitting out)
 * They are part of ISO/IEC TR 24731 and may find their way into the CRTs at some point, at which
 * point these functions must be removed from the engine.
 * While these functions do not add a huge amount of benefit for heap-allocated strings, they
 * can protect against a class of buffer overruns in stack allocated situations.
 *
 */

/* There is no safe strings below VS2005
 * scp safe_strings are used in VS2005+ DEBUG because they give more info
 */



#if !defined(NO_SAFE_STRINGS) && ( !defined( _MSC_VER ) || ( defined( _MSC_VER ) && _MSC_VER >= 1400 /* && !defined(NDEBUG) */ ))

/* An implementation of strcpy_s 
 * We're not going to actually fully behave like the MS debug version.
 */
errno_t scp_strcpy_s( const char* file, int line, char* strDest, size_t sizeInBytes, const char* strSource )
{
	char* pDest;
	const char* pSource;
	size_t bufferLeft = sizeInBytes;
	
	if ( !strDest || !strSource )
	{
		if ( strDest )
			*strDest = '\0';
		__safe_strings_error_handler( EINVAL );
		return EINVAL;
	}

	if ( sizeInBytes == 0 )
	{
		*strDest = '\0';
		__safe_strings_error_handler( ERANGE );
		return ERANGE;
	}

	pDest = strDest;
	pSource = strSource;
	
	while ((*pDest++ = *pSource++) != 0 && --bufferLeft > 0);

	if ( bufferLeft == 0 )
	{
		*strDest = '\0';
		__safe_strings_error_handler( ERANGE );
		return ERANGE;
	}

	return 0;
}

errno_t scp_strncpy_s(const char* file, int line, char* strDest, size_t sizeInBytes, const char* strSource, size_t inputSizeInBytes) {
	char* pDest;
	const char* pSource;
	size_t bufferLeft = sizeInBytes;

	if (!strDest || !strSource)
	{
		if (strDest)
			*strDest = '\0';
		__safe_strings_error_handler(EINVAL);
		return EINVAL;
	}

	if (sizeInBytes == 0)
	{
		*strDest = '\0';
		__safe_strings_error_handler(ERANGE);
		return ERANGE;
	}

	if (inputSizeInBytes == 0)
	{
		*strDest = '\0';
		return 0;
	}

	pDest = strDest;
	pSource = strSource;

	while ((*pDest++ = *pSource++) != 0 && (--inputSizeInBytes > 0) && --bufferLeft > 0);

	if (bufferLeft == 0 || (inputSizeInBytes == 0 && bufferLeft <= 1))
	{
		*strDest = '\0';
		__safe_strings_error_handler(ERANGE);
		return ERANGE;
	}

	if (inputSizeInBytes == 0) {
		*pDest = '\0';
	}

	return 0;
}

errno_t scp_strcat_s( const char* file, int line, char* strDest, size_t sizeInBytes, const char* strSource )
{
	char* pDest;
	const char* pSource;
	size_t bufferLeft = sizeInBytes;

	if ( !strDest || !strSource )
	{
		if ( strDest )
			*strDest = '\0';
		__safe_strings_error_handler( EINVAL );
		return EINVAL;
	}

	if ( bufferLeft == 0 )
	{
		*strDest = '\0';
		__safe_strings_error_handler( ERANGE );
		return ERANGE;
	}

	/* Find the terminating NULL of the input string */
	pDest = strDest;
	pSource = strSource;
	while ( *pDest )
	{
		pDest++;
		bufferLeft--;
	}

	if ( bufferLeft == 0 )
	{
		*strDest = '\0';
		__safe_strings_error_handler( ERANGE );
		return ERANGE;
	}

	/* Concatenate the strings */
	while ((*pDest++ = *pSource++) != 0 && --bufferLeft > 0);

	if ( bufferLeft == 0 )
	{
		*strDest = '\0';
		__safe_strings_error_handler( ERANGE );
		return ERANGE;
	}

	return 0;
}

#endif
