//------------------------------------------------------------------------------
// Desc:	This file contains the Unicode conversion routines
//
// Tabs:	3
//
//		Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved.
//
//		This program is free software; you can redistribute it and/or
//		modify it under the terms of version 2 of the GNU General Public
//		License as published by the Free Software Foundation.
//
//		This program is distributed in the hope that it will be useful,
//		but WITHOUT ANY WARRANTY; without even the implied warranty of
//		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//		GNU General Public License for more details.
//
//		You should have received a copy of the GNU General Public License
//		along with this program; if not, contact Novell, Inc.
//
//		To contact Novell about this file by physical or electronic mail,
//		you may find current contact information at www.novell.com
//
// $Id: funicode.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------

//#define DEF_FLM_UNI_GLOBALS
#include "flaimsys.h"

/****************************************************************************
Desc:		Encode a string into FLAIM's internal text format
			(SEN-prefixed, null-terminated UTF8).  The string is prefixed with
			a SEN that indicates the number of characters, not including the
			terminating NULL.

			This routine can be called with a NULL input buffer.  If it is
			called in this way, the length of the encoded string will be
			returned in *puiBufLength, but no encoding will actually be
			performed.
****************************************************************************/
RCODE	flmUnicode2Storage(
	const FLMUNICODE *	puzStr,			// UNICODE string to encode
	FLMUINT					uiStrLen,		// 0 = Unknown
	FLMBYTE *				pucBuf,			// Destination buffer
	FLMUINT *				puiBufLength,	// [IN ] Size of pucBuf,
													// [OUT] Amount of pucBuf used
	FLMUINT *				puiCharCount)
{
	RCODE						rc = NE_XFLM_OK;
	FLMBYTE *				pucEncPtr;
	const FLMUNICODE *	puzPtr = NULL;
	FLMUINT					uiMaxLen;
	FLMUINT					uiCharsEncoded = 0;
	FLMUINT					uiEncodedLen = 0;
	FLMUINT					uiTmp;
	FLMUNICODE				uChar;
	FLMBYTE					ucTmpSen[ 5];
	FLMBYTE *				pucTmpSen = &ucTmpSen[ 0];

	if( !pucBuf)
	{
		uiMaxLen = (~(FLMUINT)0);
	}
	else
	{
		uiMaxLen = *puiBufLength;
	}

	// If uiStrLen is 0, determine the number of characters.

	if( !uiStrLen)
	{
		uiStrLen = f_unilen( puzStr);
	}
	else if( puzStr[ uiStrLen] != 0)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM);
		goto Exit;
	}

	if( puiCharCount)
	{
		*puiCharCount = uiStrLen;
	}

	if( !uiStrLen)
	{
		// Nothing to encode

		*puiBufLength = 0;
		goto Exit;
	}

	pucEncPtr = pucBuf;

	// Encode the number of characters as a SEN.  If pucEncPtr is
	// NULL, the caller is only interested in the length of the encoded
	// string, so a temporary buffer is used to call f_encodeSEN.

	uiTmp = f_encodeSEN( uiStrLen, &pucTmpSen);

	if( pucEncPtr)
	{
		if( (uiEncodedLen + uiTmp) >= uiMaxLen)
		{
			rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
			goto Exit;
		}
		
		if( uiTmp == 1)
		{
			*pucEncPtr++ = ucTmpSen[ 0];
		}
		else
		{
			f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp);
			pucEncPtr += uiTmp;
		}
	}
	uiEncodedLen += uiTmp;

	// Encode the string using UTF-8

	puzPtr = puzStr;
	if( uiStrLen)
	{
		while( (uChar = *puzPtr) != 0)
		{
			if( (uiTmp = uiMaxLen - uiEncodedLen) == 0)
			{
				rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
				goto Exit;
			}

			if( uChar <= 127)
			{
				if( pucEncPtr)
				{
					*pucEncPtr++ = (FLMBYTE)uChar;
				}

				uiEncodedLen++;
			}
			else
			{
				if( RC_BAD( rc = f_uni2UTF8( uChar, pucEncPtr, &uiTmp)))
				{
					goto Exit;
				}

				if( pucEncPtr)
				{
					pucEncPtr += uiTmp;
				}

				uiEncodedLen += uiTmp;
			}

			puzPtr++;
			uiCharsEncoded++;
		}

		// Make sure the string length (which may have been provided by
		// the caller) was correct.

		if( uiCharsEncoded != uiStrLen)
		{
			rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM);
			goto Exit;
		}
	}

	// Terminate the string with a 0 byte

	if( (uiMaxLen - uiEncodedLen) < 1)
	{
		rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

	if( pucEncPtr)
	{
		*pucEncPtr++ = 0;
	}
	uiEncodedLen++;

	// Return the length of the encoded string

	*puiBufLength = uiEncodedLen;

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Encode a string into FLAIM's internal text format
			(SEN-prefixed, null-terminated UTF8).  The string is prefixed with
			a SEN that indicates the number of characters, not including the
			terminating NULL.

			This routine can be called with a NULL input buffer.  If it is
			called in this way, the length of the encoded string will be
			returned in *puiBufLength, but no encoding will actually be
			performed.
****************************************************************************/
RCODE	flmNative2Storage(
	char *			pszStr,			// Native string to encode
	FLMUINT			uiStrLen,		// 0 = Unknown
	FLMBYTE *		pucBuf,			// Destination buffer
	FLMUINT *		puiBufLength,	// [IN ] Size of pucBuf,
											// [OUT] Amount of pucBuf used
	FLMUINT *		puiCharCount)
{
	FLMBYTE *		pucEncPtr;
	char *			pszPtr = NULL;
	FLMUINT			uiMaxLen;
	FLMUINT			uiCharsEncoded = 0;
	FLMUINT			uiEncodedLen = 0;
	FLMUINT			uiTmp;
	FLMBYTE			ucTmpSen[ 5];
	FLMBYTE *		pucTmpSen = &ucTmpSen[ 0];
	FLMBOOL			bDirectCopy = FALSE;
	RCODE				rc = NE_XFLM_OK;

	if( !pucBuf)
	{
		uiMaxLen = (~(FLMUINT)0);
	}
	else
	{
		uiMaxLen = *puiBufLength;
	}

	// If uiStrLen is 0, determine the number of characters.

	if( !uiStrLen)
	{
#ifdef FLM_ASCII_PLATFORM
		bDirectCopy = TRUE;
		for( pszPtr = pszStr; *pszPtr; pszPtr++, uiStrLen++)
		{
			if( (FLMBYTE)*pszPtr > 0x7F)
			{
				bDirectCopy = FALSE;
			}
		}
#else
		uiStrLen = f_strlen( pszStr);
#endif
	}
	else if( pszStr[ uiStrLen] != 0)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM);
		goto Exit;
	}

	if( puiCharCount)
	{
		*puiCharCount = uiStrLen;
	}

	if( !uiStrLen)
	{
		// Nothing to encode

		*puiBufLength = 0;
		goto Exit;
	}

	pucEncPtr = pucBuf;

	// Encode the number of characters as a SEN.  If pucEncPtr is
	// NULL, the caller is only interested in the length of the encoded
	// string, so a temporary buffer is used to call f_encodeSEN.

	uiTmp = f_encodeSEN( uiStrLen, &pucTmpSen);
	if( pucEncPtr)
	{
		if( (uiEncodedLen + uiTmp) >= uiMaxLen)
		{
			rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
			goto Exit;
		}
		f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp);
		pucEncPtr += uiTmp;
	}
	uiEncodedLen += uiTmp;

	// Encode the string using UTF-8

	if( uiStrLen)
	{
		if( bDirectCopy)
		{
			// Since all of the characters are ASCII and have
			// values <= 0x7F, the string can be copied directly
			// into the destination buffer buffer.

			if( uiEncodedLen + uiStrLen >= uiMaxLen)
			{
				rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
				goto Exit;
			}

			if( pucEncPtr)
			{
				f_memcpy( pucEncPtr, pszStr, uiStrLen);
				pucEncPtr += uiStrLen;
			}
			uiEncodedLen += uiStrLen;
		}
		else
		{
			pszPtr = pszStr;
			while( *pszPtr)
			{
				if( (uiTmp = uiMaxLen - uiEncodedLen) == 0)
				{
					rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
					goto Exit;
				}

				// Convert the native character to ASCII before
				// mapping it to Unicode.

				if( RC_BAD( rc = f_uni2UTF8( (FLMUNICODE)f_toascii( *pszPtr),
					pucEncPtr, &uiTmp)))
				{
					goto Exit;
				}

				uiEncodedLen += uiTmp;

				if( pucEncPtr)
				{
					pucEncPtr += uiTmp;
				}

				pszPtr++;
				uiCharsEncoded++;
			}

			// Make sure the string length (which may have been provided by
			// the caller) was correct.

			if( uiCharsEncoded != uiStrLen)
			{
				rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM);
				goto Exit;
			}
		}
	}

	// Terminate the string with a 0 byte

	if( (uiMaxLen - uiEncodedLen) == 0)
	{
		rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

	if( pucEncPtr)
	{
		*pucEncPtr++ = 0;
	}
	uiEncodedLen++;

	// Return the length of the encoded string

	*puiBufLength = uiEncodedLen;

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Encode a string into FLAIM's internal text format
			(SEN-prefixed, null-terminated UTF8).  The string is prefixed with
			a SEN that indicates the number of characters, not including the
			terminating NULL.

			This routine can be called with a NULL input buffer.  If it is
			called in this way, the length of the encoded string will be
			returned in *puiBufLength, but no encoding will actually be
			performed.
****************************************************************************/
RCODE	flmUTF8ToStorage(
	const FLMBYTE *	pucUTF8,					// UTF8 string to encode
	FLMUINT				uiBytesInBuffer,		// Maximum bytes to process from source
	FLMBYTE *			pucBuf,					// Destination buffer
	FLMUINT *			puiBufLength)			// [IN ] Size of pucBuf,
														// [OUT] Amount of pucBuf used
{
	RCODE					rc = NE_XFLM_OK;
	FLMBYTE *			pucEncPtr;
	const FLMBYTE *	pucPtr = NULL;
	FLMUINT				uiMaxLen;
	FLMUINT				uiEncodedLen = 0;
	FLMUINT				uiCharCount;
	FLMUINT				uiByteCount;
	FLMUINT				uiTmp;
	FLMUNICODE			uChar;
	FLMBYTE				ucTmpSen[ 5];
	FLMBYTE *			pucTmpSen = &ucTmpSen[ 0];
	const FLMBYTE *	pucEnd = NULL;

	if( !pucBuf)
	{
		uiMaxLen = (~(FLMUINT)0);
	}
	else
	{
		uiMaxLen = *puiBufLength;
	}
	
	if( uiBytesInBuffer)
	{
		pucEnd = &pucUTF8[ uiBytesInBuffer];
	}

	// Determine the number of bytes and characters in the
	// string

	uiCharCount = 0;
	pucPtr = pucUTF8;
	for( ;;)
	{
		if( RC_BAD( rc = f_getCharFromUTF8Buf( &pucPtr, pucEnd, &uChar)))
		{
			goto Exit;
		}

		if( !uChar)
		{
			break;
		}
		
		uiCharCount++;
	}

	if( !uiCharCount)
	{
		*puiBufLength = 0;
		goto Exit;
	}

	pucEncPtr = pucBuf;

	// Encode the number of characters as a SEN.  If pucEncPtr is
	// NULL, the caller is only interested in the length of the encoded
	// string, so a temporary buffer is used to call f_encodeSEN.

	uiTmp = f_encodeSEN( uiCharCount, &pucTmpSen);
	if( pucEncPtr)
	{
		if( (uiEncodedLen + uiTmp) >= uiMaxLen)
		{
			rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
			goto Exit;
		}
		f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp);
		pucEncPtr += uiTmp;
	}
	uiEncodedLen += uiTmp;

	// Copy the UTF8 characters into the destination buffer

	uiByteCount = (FLMUINT)(pucPtr - pucUTF8);
	if( pucEncPtr)
	{
		if( (uiMaxLen - uiEncodedLen) < uiByteCount)
		{
			rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
			goto Exit;
		}
		f_memcpy( pucEncPtr, pucUTF8, uiByteCount);
		pucEncPtr += uiByteCount;
	}
	uiEncodedLen += uiByteCount;

	// Terminate the string with a 0 byte

	if( uiEncodedLen == uiMaxLen)
	{
		rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

	if( pucEncPtr)
	{
		*pucEncPtr++ = 0;
	}
	uiEncodedLen++;

	// Return the length of the encoded string

	*puiBufLength = uiEncodedLen;

Exit:

	return( rc);
}

/****************************************************************************
Desc: 	Convert text storage string to Unicode.
Notes:	If puzOutBuf is NULL, only a count is returned
			in puiOutBufLen to indicate the number of bytes needed to
			contain the data.  Two (unicode) bytes must be
			added to this value to account for null termination.
****************************************************************************/
RCODE flmStorage2Unicode(
	FLMUINT				uiType,
	FLMUINT				uiBufLength,
	const FLMBYTE *	pucBuffer,
	FLMUINT *			puiOutBufLen,
			// [IN] Number of bytes available in buffer
			// [OUT] Returns the number of bytes that are needed to
			// represent the data.  The null termination byte(s) are
			// not included in this value.
	void *				pOutBuf)
			// [IN/OUT] Buffer to hold the data.
{
	RCODE					rc = NE_XFLM_OK;
	FLMUNICODE			uChar;
	FLMUINT				uiOffset = 0;
	FLMUINT				uiSenLen;
	FLMUINT				uiNumChars;
	FLMUINT				uiMaxOutChars;
	FLMBYTE				ucTempBuf[ 64];
	FLMUNICODE *		puzOutBuf = NULL;
	FLMBYTE *			pszOutBuf = NULL;
	const FLMBYTE *	pucEnd;
	FLMUINT				uiDecodeCount;

	if( !pucBuffer || !uiBufLength)
	{
		ucTempBuf[ 0] = 0;	// SEN encoding of 0
		ucTempBuf[ 1] = 0;	// String terminator
		pucBuffer = &ucTempBuf[ 0];
		uiBufLength = 2;
	}
	else if( uiType != XFLM_TEXT_TYPE)
	{
		// If the value is a number, convert to text

		if( uiType == XFLM_NUMBER_TYPE)
		{
			FLMUINT	uiTmp;

			uiTmp = sizeof( ucTempBuf);
			if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength,
				ucTempBuf, &uiTmp)))
			{
				goto Exit;
			}
			pucBuffer = &ucTempBuf[ 0];
			uiBufLength = uiTmp;
		}
		else
		{
			rc = RC_SET( NE_XFLM_CONV_ILLEGAL);
			goto Exit;
		}
	}

	if( !uiBufLength)
	{
		if( puiOutBufLen)
		{
			if( *puiOutBufLen >= 2)
			{
				*((FLMUNICODE *)pOutBuf) = 0;
			}
			
			*puiOutBufLen = 0;
		}
		goto Exit;
	}

	pucEnd = &pucBuffer[ uiBufLength];

	uiSenLen = f_getSENLength( *pucBuffer);
	if( pucBuffer + uiSenLen >= pucEnd)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
		goto Exit;
	}

	if( RC_BAD( rc = f_decodeSEN( &pucBuffer, pucEnd, &uiNumChars)))
	{
		goto Exit;
	}

	// If only a length is needed (number of bytes), we can
	// return that without parsing the string

	if( !pOutBuf)
	{
		uiOffset = uiNumChars;
		goto Exit;
	}

	flmAssert( puiOutBufLen);
	uiMaxOutChars = (*puiOutBufLen) / sizeof( FLMUNICODE);
	puzOutBuf = (FLMUNICODE *)pOutBuf;

	// If we have a zero-length string, jump to exit.

	if( !uiNumChars)
	{
		if( (pucBuffer + 1) != pucEnd || *pucBuffer != 0)
		{
			rc = RC_SET( NE_XFLM_DATA_ERROR);
			goto Exit;
		}

		if( *pucBuffer != 0)
		{
			rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
		}
		else if (!uiMaxOutChars)
		{
			goto Overflow_Error;
		}

		if( puzOutBuf)
		{
			*puzOutBuf = 0;
		}
		else
		{
			*pszOutBuf = 0;
		}
		
		goto Exit;
	}

	// Parse through the string, outputting data to the buffer as we go.

	uChar = 0;
	uiDecodeCount = 0;
	for( ;;)
	{
		// Decode the bytes.

		if( RC_BAD( rc = f_getCharFromUTF8Buf( &pucBuffer, pucEnd, &uChar)))
		{
			goto Exit;
		}

		if( !uChar)
		{
			break;
		}

		if( uiOffset == uiMaxOutChars)
		{
			goto Overflow_Error;
		}

		if( puzOutBuf)
		{
			puzOutBuf[ uiOffset++] = uChar;
		}
		else
		{
			if ( uChar <= 0xFF)
			{
				uChar = (FLMUNICODE)f_tonative( (FLMBYTE)uChar);
				pszOutBuf[ uiOffset++] = (FLMBYTE)uChar;
			}
			else
			{
				rc = RC_SET( NE_XFLM_CONV_ILLEGAL);
				goto Exit;
			}
		}
		
		uiDecodeCount++;
	}

	if( uChar || uiDecodeCount != uiNumChars)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
		goto Exit;
	}

	// There is room for the 0 terminating character, but we
	// will not increment return length.

	if( uiOffset < uiMaxOutChars)
	{
		if( puzOutBuf)
		{
			puzOutBuf[ uiOffset] = 0;
		}
		else
		{
			pszOutBuf[ uiOffset] = 0;
		}
	}
	else
	{
Overflow_Error:
		flmAssert( uiOffset == uiMaxOutChars);

		// If uiOffset is zero, so is uiMaxOutChars, which means
		// that we can't even put out the zero terminator.

		if (uiOffset)
		{
			uiOffset--;
			if( puzOutBuf)
			{
				puzOutBuf[ uiOffset] = 0;
			}
			else
			{
				pszOutBuf[ uiOffset] = 0;
			}
		}
		rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW);
		goto Exit;
	}

Exit:

	if( puiOutBufLen)
	{
		*puiOutBufLen = uiOffset + uiOffset;
	}

	return( rc);
}

/****************************************************************************
Desc:	Converts storage formats to UNICODE
****************************************************************************/
RCODE flmStorage2Unicode(
	FLMUINT				uiType,
	FLMUINT				uiStorageLength,
	const FLMBYTE *	pucStorageBuffer,
	F_DynaBuf *			pBuffer)
{
	RCODE					rc = NE_XFLM_OK;
	FLMBYTE				ucTempBuf[ 80];
	const FLMBYTE *	pucEnd;
	FLMUINT				uiSenLen;
	FLMUINT				uiNumChars;
	FLMUNICODE *		puzDestBuffer;

	pBuffer->truncateData( 0);
	
	if( uiType != XFLM_TEXT_TYPE)
	{
		// If the value is a number, convert to text

		if( uiType == XFLM_NUMBER_TYPE)
		{
			FLMUINT	uiTmp;

			uiStorageLength = sizeof( ucTempBuf);
			if( RC_BAD( rc = flmStorageNum2StorageText( pucStorageBuffer, 
				uiStorageLength, ucTempBuf, &uiTmp)))
			{
				goto Exit;
			}
			pucStorageBuffer = &ucTempBuf[ 0];
			uiStorageLength = uiTmp;
		}
		else
		{
			rc = RC_SET( NE_XFLM_CONV_ILLEGAL);
			goto Exit;
		}
	}

	pucEnd = &pucStorageBuffer[ uiStorageLength];
	uiSenLen = f_getSENLength( *pucStorageBuffer);
	
	if( pucStorageBuffer + uiSenLen >= pucEnd)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
		goto Exit;
	}
	
	if( RC_BAD( rc = f_decodeSEN( &pucStorageBuffer, pucEnd, &uiNumChars)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = pBuffer->allocSpace( 
		(uiNumChars + 1) * sizeof( FLMUNICODE), (void **)&puzDestBuffer)))
	{
		goto Exit;
	}

	// Parse through the string outputting data to the buffer as we go

	for( ;;)
	{
		if( RC_BAD( rc = f_getCharFromUTF8Buf( 
			&pucStorageBuffer, pucEnd, puzDestBuffer)))
		{
			goto Exit;
		}
		
		if( !(*puzDestBuffer))
		{
			break;
		}

		puzDestBuffer++;
	}
	
Exit:

	return( rc);
}

/****************************************************************************
Desc: 	Converts a storage buffer to UTF-8 (null-terminated) text
****************************************************************************/
RCODE flmStorage2UTF8(
	FLMUINT				uiType,
	FLMUINT				uiBufLength,
	const FLMBYTE *	pucBuffer,
	FLMUINT *			puiOutBufLen,
			// [IN] Number of bytes available in buffer
			// [OUT] Returns the number of bytes that are needed to
			// represent the data.  The null termination byte is not
			// included in this value.
	FLMBYTE *			pucOutBuf)
			// [OUT] Buffer to hold the data.
{
	RCODE					rc = NE_XFLM_OK;
	const FLMBYTE *	pucEnd;
	FLMBYTE				ucTempBuf[ 64];
	FLMUINT				uiSenLen;

	if( !pucBuffer)
	{
		ucTempBuf[ 0] = 0;	// SEN encoding of 0
		ucTempBuf[ 1] = 0;	// String terminator
		pucBuffer = &ucTempBuf[ 0];
		uiBufLength = 2;
	}
	else if( uiType != XFLM_TEXT_TYPE)
	{
		// If the value is a number, convert to text

		if( uiType == XFLM_NUMBER_TYPE)
		{
			FLMUINT	uiTmp;

			uiTmp = sizeof( ucTempBuf);
			if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength,
				ucTempBuf, &uiTmp)))
			{
				goto Exit;
			}
			pucBuffer = &ucTempBuf[ 0];
			uiBufLength = uiTmp;
		}
		else
		{
			rc = RC_SET( NE_XFLM_CONV_ILLEGAL);
			goto Exit;
		}
	}

	if( !uiBufLength)
	{
		if( *puiOutBufLen && pucOutBuf)
		{
			*pucOutBuf = 0;
		}
		*puiOutBufLen = 0;
		goto Exit;
	}

	pucEnd = &pucBuffer[ uiBufLength];

	uiSenLen = f_getSENLength( *pucBuffer);
	if( pucBuffer + uiSenLen >= pucEnd)
	{
		rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
		goto Exit;
	}

	if( RC_BAD( rc = f_decodeSEN( &pucBuffer, pucEnd, NULL)))
	{
		goto Exit;
	}

	if( pucOutBuf)
	{
		if( *puiOutBufLen >= uiBufLength - uiSenLen)
		{
			f_memcpy( pucOutBuf, pucBuffer, uiBufLength - uiSenLen);
		}
	}

	*puiOutBufLen = (uiBufLength - uiSenLen) - 1;

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Reads and returns the value of the SEN indicating the number of
		characters encoded into the storage (UTF-8) string
****************************************************************************/
RCODE flmGetCharCountFromStorageBuf(
	const FLMBYTE **	ppucBuf,
	FLMUINT				uiBufSize,
	FLMUINT *			puiNumChars,
	FLMUINT *			puiSenLen)
{
	RCODE			rc = NE_XFLM_OK;
	FLMUINT		uiSenLen;
	FLMUINT		uiNumChars;

	if( !uiBufSize)
	{
		if( puiNumChars)
		{
			*puiNumChars = 0;
		}

		if( puiSenLen)
		{
			*puiSenLen = 0;
		}
		goto Exit;
	}

	if( (uiSenLen = f_getSENLength( (*ppucBuf)[ 0])) >= uiBufSize)
	{
		rc = RC_SET( NE_XFLM_DATA_ERROR);
		goto Exit;
	}

	if( RC_BAD( rc = f_decodeSEN( ppucBuf, *ppucBuf + uiSenLen, &uiNumChars)))
	{
		goto Exit;
	}

	if( puiNumChars)
	{
		*puiNumChars = uiNumChars;
	}

	if( puiSenLen)
	{
		*puiSenLen = uiSenLen;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		This routine converts an internal number to internal (ASCII) text.
Notes:	If the buffer pointer is NULL, the routine just determines how
			much buffer space is needed to store the number in a text string.
****************************************************************************/
RCODE flmStorageNum2StorageText(
	const FLMBYTE *	pucNum,
	FLMUINT				uiNumLen,
	FLMBYTE *			pucBuffer,
	FLMUINT *			puiBufLen)
{
	RCODE			rc = NE_XFLM_OK;
	FLMUINT64	ui64Num;
	FLMINT64		i64Num;
	FLMUINT		uiOffset = 0;
	FLMBOOL		bNeg = FALSE;
	char			szTmpBuf[ 64];

	if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen,
		pucNum, &ui64Num, NULL)))
	{
		if( rc == NE_XFLM_CONV_NUM_UNDERFLOW)
		{
			if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen,
				pucNum, NULL, &i64Num)))
			{
				goto Exit;
			}

			ui64Num = (FLMUINT64)-i64Num;
			bNeg = TRUE;
		}
		else
		{
			goto Exit;
		}
	}

	if( bNeg)
	{
		szTmpBuf[ uiOffset++] = '-';
	}

	uiOffset += f_sprintf( &szTmpBuf[ uiOffset], "%I64u", ui64Num);

	if( RC_BAD( rc = flmNative2Storage( szTmpBuf, uiOffset,
									pucBuffer, puiBufLen, NULL)))
	{
		goto Exit;
	}

Exit:

	return( rc);
}
