//-------------------------------------------------------------------------
// Desc: FLAIM C/S client interface
// Tabs:	3
//
//		Copyright (c) 1998-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: fcs_bios.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------

#include "flaimsys.h"

FSTATIC FLMBOOL flmGetNextHexPacketSlot( 
	FLMBYTE *				pucUsedMap,
	FLMUINT					uiMapSize,
	IF_RandomGenerator *	pRandGen,
	FLMUINT *				puiSlot);
	
FSTATIC RCODE flmGetNextHexPacketBytes( 
	FLMBYTE *				pucUsedMap,
	FLMUINT					uiMapSize,
	FLMBYTE *				pucPacket,
	IF_RandomGenerator *	pRandGen,
	FLMBYTE *				pucBuf,
	FLMUINT					uiCount);

/****************************************************************************
Desc:
*****************************************************************************/
FCS_BIOS::FCS_BIOS( void)
{
	m_pool.poolInit( (FCS_BIOS_BLOCK_SIZE + sizeof( FCSBIOSBLOCK)) * 2);
	m_bMessageActive = FALSE;
	m_pRootBlock = NULL;
	m_pCurrWriteBlock = NULL;
	m_pCurrReadBlock = NULL;
	m_bAcceptingData = FALSE;
	m_pEventHook = NULL;
	m_pvUserData = 0;
}

/****************************************************************************
Desc:
*****************************************************************************/
FCS_BIOS::~FCS_BIOS()
{
	m_pool.poolFree();
}

/****************************************************************************
Desc:	Clears all pending data
*****************************************************************************/
RCODE FCS_BIOS::reset( void)
{
	return( close());
}

/****************************************************************************
Desc:	Flushes any pending data and closes the stream.
*****************************************************************************/
RCODE FCS_BIOS::close( void)
{
	RCODE		rc = FERR_OK;

	m_pool.poolReset();
	m_bMessageActive = FALSE;
	m_pRootBlock = NULL;
	m_pCurrWriteBlock = NULL;
	m_pCurrReadBlock = NULL;
	m_bAcceptingData = FALSE;

	return( rc);
}

/****************************************************************************
Desc:	Writes the requested amount of data to the stream.
*****************************************************************************/
RCODE FCS_BIOS::write(
	FLMBYTE *		pucData,
	FLMUINT			uiLength)
{
	FLMUINT				uiCopySize;
	FLMUINT				uiDataPos = 0;
	FCSBIOSBLOCK *		pPrevBlock = NULL;
	RCODE					rc = FERR_OK;

	if( !m_bAcceptingData)
	{
		m_pool.poolReset();
		m_pCurrWriteBlock = NULL;
		m_pCurrReadBlock = NULL;
		m_pRootBlock = NULL;
		m_bAcceptingData = TRUE;
	}

	while( uiLength)
	{
		if( !m_pCurrWriteBlock ||
			m_pCurrWriteBlock->uiCurrWriteOffset == FCS_BIOS_BLOCK_SIZE)
		{
			pPrevBlock = m_pCurrWriteBlock;
			
			if( RC_BAD( rc = m_pool.poolCalloc( sizeof( FCSBIOSBLOCK), 
				(void **)&m_pCurrWriteBlock)))
			{
				goto Exit;
			}
			
			if( RC_BAD( rc = m_pool.poolAlloc( FCS_BIOS_BLOCK_SIZE, 
				(void **)&m_pCurrWriteBlock->pucBlock)))
			{
				goto Exit;
			}

			if( pPrevBlock)
			{
				pPrevBlock->pNextBlock = m_pCurrWriteBlock;
			}
			else
			{
				m_pRootBlock = m_pCurrWriteBlock;
				m_pCurrReadBlock = m_pCurrWriteBlock;
			}
		}

		uiCopySize = f_min( uiLength,
			(FLMUINT)(FCS_BIOS_BLOCK_SIZE -
			m_pCurrWriteBlock->uiCurrWriteOffset));

		flmAssert( uiCopySize != 0);

		f_memcpy( &(m_pCurrWriteBlock->pucBlock[
			m_pCurrWriteBlock->uiCurrWriteOffset]),
			&(pucData[ uiDataPos]), uiCopySize);
		
		m_pCurrWriteBlock->uiCurrWriteOffset += uiCopySize;
		uiDataPos += uiCopySize;
		uiLength -= uiCopySize;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Terminates the current message
*****************************************************************************/
RCODE FCS_BIOS::endMessage( void)
{
	RCODE		rc = FERR_OK;

	if( !m_bAcceptingData)
	{
		goto Exit;
	}

	if( m_pEventHook)
	{
		if( RC_BAD( rc = m_pEventHook( this,
			FCS_BIOS_EOM_EVENT, m_pvUserData)))
		{
			goto Exit;
		}
	}

Exit:

	m_bAcceptingData = FALSE;
	return( rc);
}

/****************************************************************************
Desc:	Reads the requested amount of data from the stream.
*****************************************************************************/
RCODE FCS_BIOS::read(
	FLMBYTE *		pucData,
	FLMUINT			uiLength,
	FLMUINT *		puiBytesRead)
{
	FLMUINT		uiCopySize;
	FLMUINT		uiDataPos = 0;
	RCODE			rc = FERR_OK;

	if( puiBytesRead)
	{
		*puiBytesRead = 0;
	}

	if( m_bAcceptingData)
	{
		m_bAcceptingData = FALSE;
	}
	
	while( uiLength)
	{
		if( m_pCurrReadBlock &&
			m_pCurrReadBlock->uiCurrReadOffset == 
			m_pCurrReadBlock->uiCurrWriteOffset)
		{
			m_pCurrReadBlock = m_pCurrReadBlock->pNextBlock;
		}

		if( !m_pCurrReadBlock)
		{
			m_pool.poolReset();
			rc = RC_SET( FERR_EOF_HIT);
			goto Exit;
		}

		uiCopySize = f_min( uiLength,
			m_pCurrReadBlock->uiCurrWriteOffset - 
			m_pCurrReadBlock->uiCurrReadOffset);

		f_memcpy( &(pucData[ uiDataPos]),
			&(m_pCurrReadBlock->pucBlock[ m_pCurrReadBlock->uiCurrReadOffset]),
			uiCopySize);

		m_pCurrReadBlock->uiCurrReadOffset += uiCopySize;
		uiDataPos += uiCopySize;

		if( puiBytesRead)
		{
			(*puiBytesRead) += uiCopySize;
		}
		uiLength -= uiCopySize;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	
*****************************************************************************/
FLMBOOL FCS_BIOS::isDataAvailable( void)
{
	if( m_bAcceptingData)
	{
		if( m_pRootBlock && m_pRootBlock->uiCurrWriteOffset)
		{
			return( TRUE);
		}
	}
	else if( m_pCurrReadBlock &&
		((m_pCurrReadBlock->uiCurrReadOffset <
			m_pCurrReadBlock->uiCurrWriteOffset) ||
		(m_pCurrReadBlock->pNextBlock)))
	{
		return( TRUE);
	}

	return( FALSE);
}

/****************************************************************************
Desc:	Returns the amount of data available for reading
*****************************************************************************/
FLMUINT FCS_BIOS::getAvailable( void)
{
	FLMUINT				uiAvail = 0;
	FCSBIOSBLOCK *		pBlk;

	if( m_bAcceptingData)
	{
		if( m_pRootBlock && m_pRootBlock->uiCurrWriteOffset)
		{
			pBlk = m_pRootBlock;
			while( pBlk)
			{
				uiAvail += pBlk->uiCurrWriteOffset;
				pBlk = pBlk->pNextBlock;
			}
		}
	}
	else if( m_pCurrReadBlock &&
		((m_pCurrReadBlock->uiCurrReadOffset <
			m_pCurrReadBlock->uiCurrWriteOffset) ||
		(m_pCurrReadBlock->pNextBlock)))
	{
		pBlk = m_pCurrReadBlock;
		while( pBlk)
		{
			uiAvail += (pBlk->uiCurrWriteOffset -
				pBlk->uiCurrReadOffset);
			pBlk = pBlk->pNextBlock;
		}
	}

	return( uiAvail);
}

/****************************************************************************
Desc: 
****************************************************************************/
FCS_DIS::FCS_DIS( void)
{
	m_pIStream = NULL;
	m_uiBOffset = m_uiBDataSize = 0;
	m_bSetupCalled = FALSE;
}

/****************************************************************************
Desc: 
****************************************************************************/
FCS_DIS::~FCS_DIS( void)
{
	if( m_bSetupCalled)
	{
		(void)close();
	}
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::setup( 
	FCS_ISTM *		pIStream)
{
	m_pIStream = pIStream;
	m_bSetupCalled = TRUE;

	return( FERR_OK);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readByte( 
	FLMBYTE *		pValue)
{
	return( read( pValue, 1, NULL));
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readShort( 
	FLMINT16 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL)))
	{
		*pValue = f_bigEndianToINT16( (FLMBYTE *)pValue);
	}

	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readUShort( 
	FLMUINT16 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL)))
	{
		*pValue = f_bigEndianToUINT16( (FLMBYTE *)pValue);
	}

	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readInt( 
	FLMINT32 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL)))
	{
		*pValue = f_bigEndianToINT32( (FLMBYTE *)pValue);
	}
	
	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readUInt( 
	FLMUINT32 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL)))
	{
		*pValue = f_bigEndianToUINT32( (FLMBYTE *)pValue);
	}

	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readInt64( 
	FLMINT64 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL)))
	{
		*pValue = f_bigEndianToINT64( (FLMBYTE *)pValue);
	}
	
	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::readUInt64( 
	FLMUINT64 *		pValue)
{
	RCODE			rc;
	
	if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL)))
	{
		*pValue = f_bigEndianToUINT64( (FLMBYTE *)pValue);
	}

	return( rc);
}

/****************************************************************************
Desc: 
****************************************************************************/
RCODE FCS_DIS::skip( 
	FLMUINT			uiBytesToSkip)
{
	return( read( NULL, uiBytesToSkip, NULL));
}

/****************************************************************************
Desc:	Flushes any pending data and closes the DIS
****************************************************************************/
RCODE FCS_DIS::close( void)
{
	RCODE		rc = FERR_OK;

	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Terminate and flush.
	
	if( RC_BAD( rc = endMessage()))
	{
		goto Exit;
	}

	// Reset the member variables.

	m_pIStream = NULL;

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Returns the state of the stream (open == TRUE, closed == FALSE)
****************************************************************************/
FLMBOOL FCS_DIS::isOpen( void)
{
	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	if( m_pIStream && m_pIStream->isOpen())
	{
		return( TRUE);
	}

	return( FALSE);
}

/****************************************************************************
Desc:	Flushes and terminates the current parent stream message
****************************************************************************/
RCODE FCS_DIS::endMessage( void)
{
	RCODE		rc = FERR_OK;

	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	if( !m_pIStream)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}
	
	// Flush any pending data.

	if( RC_BAD( rc = flush()))
	{
		goto Exit;
	}

	// Terminate the message.

	if( RC_BAD( rc = m_pIStream->endMessage()))
	{
		goto Exit;
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Flushes any pending data
****************************************************************************/
RCODE FCS_DIS::flush( void)
{
	RCODE		rc = FERR_OK;

	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	if( !m_pIStream)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}
	
	// Flush the passed-in input stream.

	if( RC_BAD( rc = m_pIStream->flush()))
	{
		goto Exit;
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Reads the specified number of bytes.
****************************************************************************/
RCODE FCS_DIS::read(
	FLMBYTE *	pucData,
	FLMUINT		uiLength,
	FLMUINT *	puiBytesRead)
{
	FLMUINT		uiCopySize;
	FLMUINT		uiReadLen;
	FLMBYTE *	pucPos = NULL;
	RCODE			rc = FERR_OK;

	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	if( !m_pIStream)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}

	if( puiBytesRead)
	{
		*puiBytesRead = uiLength;
	}

	pucPos = pucData;
	while( uiLength)
	{
		if( m_uiBOffset == m_uiBDataSize)
		{
			m_uiBOffset = m_uiBDataSize = 0;

			if( RC_BAD( rc = m_pIStream->read( m_pucBuffer,
				FCS_DIS_BUFFER_SIZE, &uiReadLen)))
			{
				if( uiReadLen)
				{
					rc = FERR_OK;
				}
				else
				{
					goto Exit;
				}
			}
			m_uiBDataSize = uiReadLen;
		}

		uiCopySize = m_uiBDataSize - m_uiBOffset;
		if( uiLength < uiCopySize)
		{
			uiCopySize = uiLength;
		}

		if( pucPos)
		{
			f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize);
			pucPos += uiCopySize;
		}
		
		m_uiBOffset += uiCopySize;
		uiLength -= uiCopySize;
	}
	
Exit:

	if( RC_OK( rc) && uiLength)
	{
		// Unable to satisfy the read request.

		rc = RC_SET( FERR_EOF_HIT);
	}

	if( puiBytesRead)
	{
		(*puiBytesRead) -= uiLength;
	}

	return( rc);
}

/****************************************************************************
Desc:	Reads a binary token from the stream.  The token is tagged with a
		length.
****************************************************************************/
RCODE FCS_DIS::readBinary(
	F_Pool *		pPool,
	FLMBYTE **	ppValue,
	FLMUINT *	puiDataSize)
{
	FLMUINT16	ui16DataSize;
	RCODE			rc = FERR_OK;

	if( RC_BAD( rc = readUShort( &ui16DataSize)))
	{
		goto Exit;
	}

	if( pPool)
	{
		// If the data size is non-zero, allocate a buffer and
		// read the entire binary value.

		if( ui16DataSize)
		{
			if( RC_BAD( rc = pPool->poolAlloc( ui16DataSize, (void **)ppValue)))
			{
				goto Exit;
			}
			
			if( RC_BAD( rc = read( *ppValue, ui16DataSize, NULL)))
			{
				goto Exit;
			}
		}
		else
		{
			*ppValue = NULL;
		}
	}
	else
	{
		// The application is not interested in the value.  Just skip the
		// to the end of the value.

		if( RC_BAD( rc = skip( ui16DataSize)))
		{
			goto Exit;
		}
	}

Exit:

	if( puiDataSize)
	{
		*puiDataSize = ui16DataSize;
	}

	return( rc);
}


/****************************************************************************
Desc:	Reads a large binary token from the stream.  The token is tagged with a
		length.
****************************************************************************/
RCODE FCS_DIS::readLargeBinary(
	F_Pool *		pPool,
	FLMBYTE **	ppValue,
	FLMUINT *	puiDataSize)
{
	FLMUINT32	ui32DataSize;
	RCODE			rc = FERR_OK;

	if( RC_BAD( rc = readUInt( &ui32DataSize)))
	{
		goto Exit;
	}

	if( pPool)
	{
		// If the data size is non-zero, allocate a buffer and
		// read the entire binary value.

		if( ui32DataSize)
		{
			if( RC_BAD(rc = pPool->poolAlloc( ui32DataSize, (void **)ppValue)))
			{
				goto Exit;
			}
			
			if( RC_BAD( rc = read( *ppValue, ui32DataSize, NULL)))
			{
				goto Exit;
			}
		}
		else
		{
			*ppValue = NULL;
		}
	}
	else
	{
		// The application is not interested in the value.  Just skip the
		// to the end of the value.

		if( RC_BAD( rc = skip( ui32DataSize)))
		{
			goto Exit;
		}
	}

Exit:

	if( puiDataSize)
	{
		*puiDataSize = (FLMUINT)ui32DataSize;
	}

	return( rc);
}

/****************************************************************************
Desc:	Reads a UTF-8 string from the stream.
****************************************************************************/
RCODE	FCS_DIS::readUTF(
	F_Pool *			pPool,
	FLMUNICODE **	ppValue)
{
	FLMBYTE		ucByte1;
	FLMBYTE		ucByte2;
	FLMBYTE		ucByte3;
	FLMBYTE		ucLoByte;
	FLMBYTE		ucHiByte;
	FLMUINT16	ui16UTFLen;
	FLMUINT		uiOffset = 0;
	RCODE			rc = FERR_OK;
	
	// Read the data.

	if( RC_BAD( rc = readUShort( &ui16UTFLen)))
	{
		goto Exit;
	}

	// Check the size of the UTF string.  FLAIM does not support
	// strings that are larger than 32K characters.

	if( ui16UTFLen >= 32767)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}
	
	// Allocate space for the string.

	if( pPool)
	{
		if( RC_BAD( rc = pPool->poolAlloc(
			(FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * (FLMUINT)(ui16UTFLen + 1)),
			(void **)ppValue)))
		{
			goto Exit;
		}
	}
	else if( ppValue)
	{
		*ppValue = NULL;
	}

	while( ui16UTFLen)
	{
		// Read and decode the bytes.

		if( RC_BAD( rc = read( &ucByte1, 1, NULL)))
		{
			goto Exit;
		}

		if( (ucByte1 & 0xC0) != 0xC0)
		{
			ucHiByte = 0;
			ucLoByte = ucByte1;
		}
		else
		{
			if( RC_BAD( rc = read( &ucByte2, 1, NULL)))
			{
				goto Exit;
			}

			if( (ucByte1 & 0xE0) == 0xE0)
			{
				if( RC_BAD( rc = read( &ucByte3, 1, NULL)))
				{
					goto Exit;
				}

				ucHiByte =
					(FLMBYTE)(((ucByte1 & 0x0F) << 4) | ((ucByte2 & 0x3C) >> 2));
				ucLoByte = (FLMBYTE)(((ucByte2 & 0x03) << 6) | (ucByte3 & 0x3F));
			}
			else
			{
				ucHiByte = (FLMBYTE)(((ucByte1 & 0x1C) >> 2));
				ucLoByte = (FLMBYTE)(((ucByte1 & 0x03) << 6) | (ucByte2 & 0x3F));
			}
		}

		if( pPool)
		{
			(*ppValue)[ uiOffset] = 
				(FLMUNICODE)(((((FLMUNICODE)(ucHiByte)) << 8) | 
					((FLMUNICODE)(ucLoByte))));
		}

		uiOffset++;
		ui16UTFLen--;
	}

	if( pPool)
	{
		(*ppValue)[ uiOffset] = 0;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc: Reads an Hierarchical Tagged Data record from the stream.
****************************************************************************/
RCODE FCS_DIS::readHTD(
	F_Pool *			pPool,
	FLMUINT			uiContainer,
	FLMUINT			uiDrn,
	NODE **			ppNode,
	FlmRecord **	ppRecord)
{

	FLMBYTE		ucType;
	FLMBYTE		ucLevel = 0;
	FLMBYTE		ucPrevLevel = 0;
	FLMBYTE		ucDescriptor;
	FLMBYTE		ucFlags;
	FLMUINT16	ui16Tag;
	FLMBOOL		bHasValue;
	FLMBOOL		bChild;
	FLMBOOL		bSibling;
	FLMBOOL		bLeftTruncated;
	FLMBOOL		bRightTruncated;
	NODE *		pRoot = NULL;
	NODE *		pNode = NULL;
	NODE *		pPrevNode = NULL;
	void *		pField = NULL;
	void *		pvMark = NULL;
	RCODE			rc = FERR_OK;

	if( pPool)
	{
		pvMark = pPool->poolMark();
	}

	for( ;;)
	{
		// Reset variables.

		bChild = FALSE;
		bSibling = FALSE;

		// Read the attribute's tag number.

		if( RC_BAD( rc = readUShort( &ui16Tag)))
		{
			goto Exit;
		}

		// A tag number of 0 indicates that the end of the HTD data
		// stream has been reached.

		if( !ui16Tag)
		{
			break;
		}

		// Read the attribute's descriptor.

		if( RC_BAD(rc = read( &ucDescriptor, 1, NULL)))
		{
			goto Exit;
		}

		// Set the flag indicating whether or not the
		// attribute has a value.

		bHasValue = (FLMBOOL)((ucDescriptor & HTD_HAS_VALUE_FLAG)
							? (FLMBOOL)TRUE
							: (FLMBOOL)FALSE);

		// Set the value type.

		ucType = (FLMBYTE)((ucDescriptor & HTD_VALUE_TYPE_MASK));

		// Get the attribute's level.

		switch( (ucDescriptor & HTD_LEVEL_MASK) >> HTD_LEVEL_POS)
		{
			case HTD_LEVEL_SIBLING:
			{
				bSibling = TRUE;
				ucLevel = ucPrevLevel;
				break;
			}
			
			case HTD_LEVEL_CHILD:
			{
				if( ucLevel < 0xFF)
				{
					bChild = TRUE;
					ucLevel = (FLMBYTE)(ucPrevLevel + 1);
				}
				else
				{
					rc = RC_SET( FERR_BAD_FIELD_LEVEL);
					goto Exit;
				}
				break;
			}
			
			case HTD_LEVEL_BACK:
			{
				if( ucLevel > 0)
				{
					ucLevel = (FLMBYTE)(ucPrevLevel - 1);
				}
				else
				{
					rc = RC_SET( FERR_BAD_FIELD_LEVEL);
					goto Exit;
				}
				break;
			}

			case HTD_LEVEL_BACK_X:
			{
				FLMBYTE ucLevelsBack;

				if( RC_BAD(rc = read( &ucLevelsBack, 1, NULL)))
				{
					goto Exit;
				}

				if( ucPrevLevel >= ucLevelsBack)
				{
					ucLevel = (FLMBYTE)(ucPrevLevel - ucLevelsBack);
				}
				else
				{
					rc = RC_SET( FERR_BAD_FIELD_LEVEL);
					goto Exit;
				}
				break;
			}
		}

		// Allocate the record object

		if( ppRecord && ucLevel == 0)
		{
			if( *ppRecord)
			{
				if( (*ppRecord)->isReadOnly() || 
					(*ppRecord)->getRefCount() > 1)
				{
					(*ppRecord)->Release();

					if( (*ppRecord = f_new FlmRecord) == NULL)
					{
						rc = RC_SET( FERR_MEM);
						goto Exit;
					}
				}
				else
				{
					// Reuse the existing FlmRecord object.
					
					(*ppRecord)->clear();
				}
			}
			else
			{
				if( (*ppRecord = f_new FlmRecord) == NULL)
				{
					rc = RC_SET( FERR_MEM);
					goto Exit;
				}
			}
			
			(*ppRecord)->setContainerID( uiContainer);
			(*ppRecord)->setID( uiDrn);
		}

		// Allocate the attribute.

		if( pPool && ppNode)
		{
			pNode = GedNodeMake( pPool, ui16Tag, &rc);
			if( RC_BAD( rc))
			{
				goto Exit;
			}
		}

		bLeftTruncated = FALSE;
		bRightTruncated = FALSE;

		// Read the attribute's value.
		
		switch( ucType)
		{
			case HTD_TYPE_UNICODE:
			{
				FLMUNICODE * pUTF;
	
				if( pNode)
				{
					GedValTypeSet( pNode, FLM_TEXT_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_TEXT_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read UNICODE text in UTF-8 format.

				if( pPool)
				{
					if( RC_BAD( rc = readUTF( pPool, &pUTF)))
					{
						goto Exit;
					}

					if( pNode)
					{
						if( RC_BAD( rc = GedPutUNICODE( pPool, pNode, pUTF)))
						{
							goto Exit;
						}
					}

					if( ppRecord)
					{
						if( RC_BAD( rc = (*ppRecord)->setUnicode( pField, pUTF)))
						{
							goto Exit;
						}
					}
				}
				else
				{
					if( RC_BAD( rc = readUTF( NULL, NULL)))
					{
						goto Exit;
					}
				}
				break;
			}

			case HTD_TYPE_UINT:
			{
				FLMUINT32		ui32Value;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_NUMBER_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_NUMBER_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read an unsigned 32-bit integer.

				if( RC_BAD( rc = readUInt( &ui32Value)))
				{
					goto Exit;
				}

				if( pNode)
				{
					if( RC_BAD( rc = GedPutUINT( pPool, pNode, ui32Value)))
					{
						goto Exit;
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->setUINT( pField, ui32Value)))
					{
						goto Exit;
					}
				}

				break;
			}
			
			case HTD_TYPE_UINT64:
			{
				FLMUINT64		ui64Value;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_NUMBER_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_NUMBER_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read an unsigned 64-bit integer.

				if( RC_BAD( rc = readUInt64( &ui64Value)))
				{
					goto Exit;
				}

				if( pNode)
				{
					if( RC_BAD( rc = GedPutUINT64( pPool, pNode, ui64Value)))
					{
						goto Exit;
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->setUINT64( pField, ui64Value)))
					{
						goto Exit;
					}
				}

				break;
			}

			case HTD_TYPE_INT:
			{
				FLMINT32		i32Value;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_NUMBER_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_NUMBER_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read a signed 32-bit integer.

				if( RC_BAD( rc = readInt( &i32Value)))
				{
					goto Exit;
				}

				if( pNode)
				{
					if( RC_BAD( rc = GedPutINT( pPool, pNode, i32Value)))
					{
						goto Exit;
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->setINT( pField, i32Value)))
					{
						goto Exit;
					}
				}

				break;
			}

			case HTD_TYPE_INT64:
			{
				FLMINT64		i64Value;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_NUMBER_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_NUMBER_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read a signed 64-bit integer.

				if( RC_BAD( rc = readInt64( &i64Value)))
				{
					goto Exit;
				}

				if( pNode)
				{
					if( RC_BAD( rc = GedPutINT64( pPool, pNode, i64Value)))
					{
						goto Exit;
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->setINT64( pField, i64Value)))
					{
						goto Exit;
					}
				}

				break;
			}

			case HTD_TYPE_CONTEXT:
			{
				FLMUINT32		ui32Value;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_CONTEXT_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_CONTEXT_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read an unsigned 32-bit integer.

				if( RC_BAD( rc = readUInt( &ui32Value)))
				{
					goto Exit;
				}

				if( pNode)
				{
					if( RC_BAD( rc = GedPutRecPtr( pPool, pNode, ui32Value)))
					{
						goto Exit;
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->setRecPointer( pField, ui32Value)))
					{
						goto Exit;
					}
				}

				break;
			}

			case HTD_TYPE_BINARY:
			{
				FLMUINT16	ui16DataSize;
				FLMBYTE *	pucData = NULL;

				if( pNode)
				{
					GedValTypeSet( pNode, FLM_BINARY_TYPE);
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, FLM_BINARY_TYPE, &pField)))
					{
						goto Exit;
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read a binary data stream.

				if( RC_BAD( rc = readUShort( &ui16DataSize)))
				{
					goto Exit;
				}

				if( pPool)
				{
					if( pNode)
					{
						if( (pucData = (FLMBYTE *)GedAllocSpace( pPool, pNode,
							FLM_BINARY_TYPE, ui16DataSize)) == NULL)
						{
							rc = RC_SET( FERR_MEM);
							goto Exit;
						}
					}
					else if( ppRecord)
					{
						if( RC_BAD(rc = (*ppRecord)->allocStorageSpace( pField,
							FLM_BINARY_TYPE, ui16DataSize, 0, 0, 0, &pucData, NULL)))
						{
							goto Exit;
						}
					}

					if( RC_BAD( rc = read( pucData, ui16DataSize, NULL)))
					{
						goto Exit;
					}

					if( pNode)
					{
						if( ppRecord)
						{
							if( RC_BAD( rc = (*ppRecord)->setBinary( pField, 
								pucData, ui16DataSize)))
							{
								goto Exit;
							}
						}
					}
				}
				else
				{
					if( RC_BAD( rc = skip( ui16DataSize)))
					{
						goto Exit;
					}
				}
				break;
			}

			case HTD_TYPE_DATE:
			{
				rc = RC_SET( FERR_NOT_IMPLEMENTED);
				goto Exit;
			}

			case HTD_TYPE_GEDCOM:
			{
				FLMBYTE		ucGedType;
				FLMUINT16	ui16DataSize;
				FLMBYTE *	pucData = NULL;

				// Read the GEDCOM data type and flags

				if( RC_BAD( rc = read( &ucGedType, 1, NULL)))
				{
					goto Exit;
				}
				ucFlags = ucGedType & 0xF0;
				ucGedType &= 0x0F;

				if( ucFlags & 0x10)
				{
					bLeftTruncated = TRUE;
				}

				if( ucFlags & 0x20)
				{
					bRightTruncated = TRUE;
				}

				if( ucGedType != FLM_TEXT_TYPE &&
					ucGedType != FLM_NUMBER_TYPE &&
					ucGedType != FLM_BINARY_TYPE &&
					ucGedType != FLM_BLOB_TYPE &&
					ucGedType != FLM_CONTEXT_TYPE)
				{
					rc = RC_SET( FERR_NOT_IMPLEMENTED);
					goto Exit;
				}

				if( pNode)
				{
					GedValTypeSet( pNode, ucGedType);
					if( bLeftTruncated)
					{
						GedSetLeftTruncated( pNode);
					}

					if( bRightTruncated)
					{
						GedSetRightTruncated( pNode);
					}
				}

				if( ppRecord)
				{
					if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, 
						ui16Tag, ucGedType, &pField)))
					{
						goto Exit;
					}

					if( bLeftTruncated)
					{
						(*ppRecord)->setLeftTruncated( pField, TRUE);
					}

					if( bRightTruncated)
					{
						(*ppRecord)->setRightTruncated( pField, TRUE);
					}
				}

				if( !bHasValue)
				{
					break;
				}

				// Read the data size.

				if( RC_BAD( rc = readUShort( &ui16DataSize)))
				{
					goto Exit;
				}

				// Read the data value.

				if( pPool)
				{
					if( pNode)
					{
						if( (pucData = (FLMBYTE *)GedAllocSpace( pPool, pNode,
							ucGedType, ui16DataSize)) == NULL)
						{
							rc = RC_SET( FERR_MEM);
							goto Exit;
						}
					}
					else if( ppRecord)
					{
						if (RC_BAD( rc = (*ppRecord)->allocStorageSpace( pField,
							ucGedType, ui16DataSize, 0, 0, 0, &pucData, NULL)))
						{
							goto Exit;
						}
					}

					if( RC_BAD( rc = read( pucData, ui16DataSize, NULL)))
					{
						goto Exit;
					}

					if( pNode)
					{
						if( ppRecord)
						{
							if( RC_BAD( rc = (*ppRecord)->setBinary( pField, 
								pucData, ui16DataSize)))
							{
								goto Exit;
							}
						}
					}
				}
				else
				{
					if( RC_BAD( rc = skip( ui16DataSize)))
					{
						goto Exit;
					}
				}
				break;
			}

			default:
			{
				rc = RC_SET( FERR_NOT_IMPLEMENTED);
				goto Exit;
			}
		}

		// Set the truncation flags

		if( ucType != HTD_TYPE_GEDCOM)
		{
			if( pNode)
			{
				if( bLeftTruncated)
				{
					GedSetLeftTruncated( pNode);
				}

				if( bRightTruncated)
				{
					GedSetRightTruncated( pNode);
				}
			}
			else if( pField)
			{
				if( bLeftTruncated)
				{
					(*ppRecord)->setLeftTruncated( pField, TRUE);
				}

				if( bRightTruncated)
				{
					(*ppRecord)->setRightTruncated( pField, TRUE);
				}
			}
		}
		
		// Graft the attribute into the tree.
		
		if( pNode)
		{
			if( pRoot == NULL)
			{
				pRoot = pNode;
			}
			else
			{
				if( bSibling)
				{
					pPrevNode->next = pNode;
					pNode->prior = pPrevNode;
					GedNodeLevelSet( pNode, GedNodeLevel( pPrevNode));
				}
				else if( bChild)
				{
					pPrevNode->next = pNode;
					pNode->prior = pPrevNode;
					GedNodeLevelSet( pNode, GedNodeLevel( pPrevNode) + 1);
				}
				else
				{
					pPrevNode->next = pNode;
					pNode->prior = pPrevNode;
					GedNodeLevelSet( pNode, ucLevel);
				}
			}
		}

		ucPrevLevel = ucLevel;
		pPrevNode = pNode;

		// Reset the pool if a GEDCOM record is not going to be returned.

		if( pPool && !ppNode)
		{
			pPool->poolReset( pvMark);
		}
	}

Exit:

	if( RC_OK( rc))
	{
		if( ppNode)
		{
			*ppNode = pRoot;
		}
	}
	else
	{
		if( ppRecord && *ppRecord)
		{
			(*ppRecord)->Release();
		}
	}

	if( pPool && !ppNode)
	{
		pPool->poolReset( pvMark);
	}

	return( rc);		
}

/****************************************************************************
Desc:
****************************************************************************/
FCS_DOS::FCS_DOS( void)
{
	m_pOStream = NULL;
	m_uiBOffset = 0;
	m_tmpPool.poolInit( 512);
	m_bSetupCalled = FALSE;
}


/****************************************************************************
Desc:
****************************************************************************/
FCS_DOS::~FCS_DOS( void)
{
	if( m_bSetupCalled)
	{
		(void)close();
	}
	
	m_tmpPool.poolFree();
}

/****************************************************************************
Desc:	Writes a specified number of bytes from a buffer to the output
		stream.
****************************************************************************/
RCODE FCS_DOS::write(
	FLMBYTE *		pucData,
	FLMUINT			uiLength)
{
	RCODE		rc = FERR_OK;

	// Verify that setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Write the data.

Retry_Write:

	if( FCS_DOS_BUFFER_SIZE - m_uiBOffset >= uiLength)
	{
		f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength);
		m_uiBOffset += uiLength;
	}
	else
	{
		if( m_uiBOffset > 0)
		{
			if( RC_BAD( rc = flush()))
			{
				goto Exit;
			}
		}

		if( uiLength <= FCS_DOS_BUFFER_SIZE)
		{
			goto Retry_Write;
		}

		if( RC_BAD( rc = m_pOStream->write( pucData, uiLength)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Writes a UNICODE string to the stream in the UTF-8 format.
****************************************************************************/
RCODE	FCS_DOS::writeUTF(
	FLMUNICODE *	puzValue)
{
	FLMUINT			uiUTFLen;
	FLMUNICODE *	puzTmp;
	RCODE				rc = FERR_OK;
	
	// Verify that setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Verify pValue is valid.
	
	flmAssert( puzValue != NULL);

	// Determine the size of the string.
	
	uiUTFLen = 0;
	puzTmp = puzValue;
	while( *puzTmp)
	{
		uiUTFLen++;
		puzTmp++;
	}

	if( RC_BAD( rc = writeUShort( (FLMUINT16)uiUTFLen)))
	{
		goto Exit;
	}

	puzTmp = puzValue;
	while( *puzTmp)
	{
		if( *puzTmp <= 0x007F)
		{
			if( RC_BAD( rc = writeByte( (FLMBYTE)(*puzTmp))))
			{
				goto Exit;
			}
		}
		else if( *puzTmp >= 0x0080 && *puzTmp <= 0x07FF)
		{
			if( RC_BAD( rc = writeUShort((FLMUINT16)
				((((FLMUINT16)(0xC0 | (FLMBYTE)((*puzTmp & 0x07C0) >> 6))) << 8) |
				(FLMUINT16)(0x80 | (FLMBYTE)(*puzTmp & 0x003F))))))
			{
				goto Exit;
			}
		}
		else
		{
			if( RC_BAD( rc = writeUShort((FLMUINT16)
				((((FLMUINT16)(0xE0 | (FLMBYTE)((*puzTmp & 0xF000) >> 12))) << 8) |
				(FLMUINT16)(0x80 | (FLMBYTE)((*puzTmp & 0x0FC0) >> 6))))))
			{
				goto Exit;
			}

			if( RC_BAD( rc = writeByte( (0x80 | (FLMBYTE)(*puzTmp & 0x003F)))))
			{
				goto Exit;
			}
		}

		puzTmp++;
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Writes a binary token (including length) to the stream.
****************************************************************************/
RCODE FCS_DOS::writeBinary(
	FLMBYTE *	pucValue,
	FLMUINT		uiSize)
{
	RCODE			rc = FERR_OK;

	flmAssert( uiSize <= 0x0000FFFF);

	if( RC_BAD( rc = writeUShort( (FLMUINT16)uiSize)))
	{
		goto Exit;
	}

	if( uiSize)
	{
		if( RC_BAD( rc = write( pucValue, uiSize)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Writes a large binary token (including length) to the stream.
****************************************************************************/
RCODE FCS_DOS::writeLargeBinary(
	FLMBYTE *	pucValue,
	FLMUINT		uiSize)
{
	RCODE			rc = FERR_OK;

	if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiSize)))
	{
		goto Exit;
	}

	if( uiSize)
	{
		if( RC_BAD( rc = write( pucValue, uiSize)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Writes a Hierarchical Tagged Data record to the stream.
****************************************************************************/
RCODE FCS_DOS::writeHTD(
	NODE *		pHTD,
	FlmRecord *	pRecord,
	FLMBOOL		bSendForest,
	FLMBOOL		bSendAsGedcom)
{
	FLMUINT		uiPrevLevel = 0;
	FLMUINT		uiLevelsBack = 0;
	FLMUINT		uiDescriptor = 0;
	FLMUINT		uiCurLevel = 0;
	FLMUINT		uiCurValType = 0;
	FLMUINT		uiCurDataLen = 0;
	FLMBOOL		bLeftTruncated;
	FLMBOOL		bRightTruncated;
	FLMBYTE *	pucCurData = NULL;
	FLMBYTE		pucTmpBuf[ 32];
	void *		pvMark = m_tmpPool.poolMark();
	NODE *		pCurNode = NULL;
	void *		pCurField = NULL;
	RCODE			rc = FERR_OK;

	// Verify that setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Set the current node or field

	if( pHTD)
	{
		pCurNode = pHTD;
	}
	else
	{
		pCurField = pRecord->root();
	}

	while( pCurNode || pCurField)
	{
		// See if we are done sending the tree/forest.

		if( pCurNode)
		{
			if( !bSendForest && (pCurNode != pHTD) &&
				(GedNodeLevel( pCurNode) == GedNodeLevel( pHTD)))
			{
				break;
			}
		}

		// Output the attribute's tag number.

		if( pCurNode)
		{
			f_UINT16ToBigEndian( (FLMUINT16)GedTagNum( pCurNode), pucTmpBuf);
		}
		else if( pCurField)
		{
			f_UINT16ToBigEndian( (FLMUINT16)pRecord->getFieldID( pCurField), pucTmpBuf);
		}

		if( RC_BAD( rc = write( pucTmpBuf, 2)))
		{
			goto Exit;
		}

		// Setup the attribute's descriptor.

		uiDescriptor = 0;
		uiLevelsBack = 0;

		if( pCurNode)
		{
			uiCurLevel = GedNodeLevel( pCurNode);
		}
		else
		{
			uiCurLevel = pRecord->getLevel( pCurField);
		}

		if( uiCurLevel == uiPrevLevel)
		{
			(void)(uiDescriptor |= (HTD_LEVEL_SIBLING << HTD_LEVEL_POS));
		}
		else if( uiCurLevel == uiPrevLevel + 1)
		{
			uiDescriptor |= (HTD_LEVEL_CHILD << HTD_LEVEL_POS);
		}
		else if( uiCurLevel == uiPrevLevel - 1)
		{
			uiDescriptor |= (HTD_LEVEL_BACK << HTD_LEVEL_POS);
		}
		else if( uiCurLevel < uiPrevLevel)
		{
			uiDescriptor |= (HTD_LEVEL_BACK_X << HTD_LEVEL_POS);
			uiLevelsBack = uiPrevLevel - uiCurLevel;
		}
		else
		{
			flmAssert( 0);
			rc = RC_SET( FERR_FAILURE);
			goto Exit;
		}

		if( pCurNode)
		{
			uiCurDataLen = GedValLen( pCurNode);
			uiCurValType = GedValType( pCurNode) & 0x0F;
			bLeftTruncated = GedIsLeftTruncated( pCurNode);
			bRightTruncated = GedIsRightTruncated( pCurNode);
			pucCurData = (FLMBYTE *)GedValPtr( pCurNode);
		}
		else
		{
			uiCurDataLen = pRecord->getDataLength( pCurField);
			uiCurValType = (FLMUINT)pRecord->getDataType( pCurField);
			bLeftTruncated = pRecord->isLeftTruncated( pCurField);
			bRightTruncated = pRecord->isRightTruncated( pCurField);
			pucCurData = (FLMBYTE *)(pRecord->getDataPtr( pCurField));
		}

		if( uiCurDataLen)
		{
			uiDescriptor |= HTD_HAS_VALUE_FLAG;
		}

		if( bSendAsGedcom)
		{
			uiDescriptor |= HTD_TYPE_GEDCOM;
		}
		else
		{
			switch( uiCurValType)
			{
				case FLM_TEXT_TYPE:
				{
					uiDescriptor |= HTD_TYPE_UNICODE;
					break;
				}

				case FLM_NUMBER_TYPE:
				{
					// To save conversion time, cheat to determine if
					// the number is negative.

					if( ((*pucCurData & 0xF0) == 0xB0))
					{
						FLMINT64		i64Value;

						if( (pCurNode && RC_BAD( rc = GedGetINT64( 
								pCurNode, &i64Value))) ||
							(pCurField && RC_BAD( rc = pRecord->getINT64( 
								pCurField, &i64Value))))
						{
							goto Exit;
						}
						if (i64Value >= (FLMINT64)(FLM_MIN_INT32) &&
							 i64Value <= (FLMINT64)(FLM_MAX_INT32))
						{
							uiDescriptor |= HTD_TYPE_INT;
						}
						else
						{
							uiDescriptor |= HTD_TYPE_INT64;
						}
					}
					else
					{
						FLMUINT64		ui64Value;

						if( (pCurNode && RC_BAD( rc = GedGetUINT64( 
								pCurNode, &ui64Value))) ||
							(pCurField && RC_BAD( rc = pRecord->getUINT64( 
								pCurField, &ui64Value))))
						{
							goto Exit;
						}
						if (ui64Value <= (FLMUINT64)(FLM_MAX_UINT32))
						{
							uiDescriptor |= HTD_TYPE_UINT;
						}
						else
						{
							uiDescriptor |= HTD_TYPE_UINT64;
						}
					}
					break;
				}

				case FLM_CONTEXT_TYPE:
				{
					uiDescriptor |= HTD_TYPE_CONTEXT;
					break;
				}

				case FLM_BINARY_TYPE:
				{
					uiDescriptor |= HTD_TYPE_BINARY;
					break;
				}

				default:
				{
					rc = RC_SET( FERR_NOT_IMPLEMENTED);
					goto Exit;
				}
			}
		}

		// Output the attribute's descriptor.

		pucTmpBuf[ 0] = (FLMBYTE)uiDescriptor;
		if( RC_BAD( rc = write( pucTmpBuf, 1)))
		{
			goto Exit;
		}

		// Output the "levels back" value (if available).

		if( uiLevelsBack)
		{
			flmAssert( uiLevelsBack <= 0xFF);
			pucTmpBuf[ 0] = (FLMBYTE)uiLevelsBack;
			if( RC_BAD( rc = write( pucTmpBuf, 1)))
			{
				goto Exit;
			}
		}

		// Output the attribute's value.

		if( bSendAsGedcom)
		{
			// Output the GEDCOM data type and flags

			pucTmpBuf[ 0] = (FLMBYTE)uiCurValType;
			if( bLeftTruncated)
			{
				pucTmpBuf[ 0] |= 0x10;
			}

			if( bRightTruncated)
			{
				pucTmpBuf[ 0] |= 0x20;
			}

			if( RC_BAD( rc = write( pucTmpBuf, 1)))
			{
				goto Exit;
			}

			if( uiCurDataLen)
			{
				// Output the data size.
				
				flmAssert( uiCurDataLen <= 0x0000FFFF);

				f_UINT16ToBigEndian( (FLMUINT16)uiCurDataLen, pucTmpBuf);
				if( RC_BAD( rc = write( pucTmpBuf, 2)))
				{
					goto Exit;
				}

				// Send the data.

				if( RC_BAD( rc = write( pucCurData, uiCurDataLen)))
				{
					goto Exit;
				}
			}
		}
		else
		{
			// Send the value.

			switch( uiCurValType)
			{
				case FLM_TEXT_TYPE:
				{
					// Extract the value.

					if( uiCurDataLen)
					{
						FLMUINT			uiBufSize;
						FLMUNICODE *	puzValue;

						// Reset the temporary pool.

						m_tmpPool.poolReset( pvMark);
						if( uiCurDataLen <= 32751)
						{
							// Allocate a buffer that is twice the size of the
							// attribute's value length.  This is necessary because the
							// UNICODE conversion will may double the size of the
							// attribute's value.  A "safety" zone of 32 bytes is added
							// to the buffer size to allow for strings that may require
							// more than 2x the attribute's size and to account for
							// null-termination bytes.

							uiBufSize = (2 * uiCurDataLen) + 32;
						}
						else
						{
							// Allocate a full 64K.

							uiBufSize = 65535;
						}
						
						if( RC_BAD( rc = m_tmpPool.poolAlloc( uiBufSize, 
							(void **)&puzValue)))
						{
							goto Exit;
						}

						// Extract UNICODE from the attribute.
						
						if( (pCurNode && RC_BAD( rc = GedGetUNICODE( 
								pCurNode, puzValue, &uiBufSize))) ||
							(pCurField && RC_BAD( rc = pRecord->getUnicode( 
								pCurField, puzValue, &uiBufSize))))
						{
							if( rc == FERR_CONV_DEST_OVERFLOW)
							{
								// Since we did not correctly guess the buffer size,
								// try again.  This time, take the slow (but safe)
								// approach of calculating the size of the UNICODE string.

								if( (pCurNode && RC_BAD( rc = GedGetUNICODE( 
										pCurNode, NULL, &uiBufSize))) ||
									(pCurField && RC_BAD( rc = pRecord->getUnicodeLength(
										pCurField, &uiBufSize))))
								{
									goto Exit;
								}

								// Add two bytes to account for null-termination.

								uiBufSize += 2;

								// Reset the pool to clear the prior allocation.

								m_tmpPool.poolReset( pvMark);

								// Allocate the new buffer.
								
								if( RC_BAD( rc = m_tmpPool.poolAlloc( uiBufSize,
									(void **)&puzValue)))
								{
									goto Exit;
								}
								
								// Extract the UNICODE string.
								
								if( (pCurNode && RC_BAD( rc = GedGetUNICODE( 
											pCurNode, puzValue, &uiBufSize))) ||
									 (pCurField && RC_BAD( rc = pRecord->getUnicode( 
									 		pCurField, puzValue, &uiBufSize))))
								{
									goto Exit;
								}
							}
							else
							{
								goto Exit;
							}
						}

						// Write the attribute's value.

						if( RC_BAD( rc = writeUTF( puzValue)))
						{
							goto Exit;
						}
					}
						
					break;
				}

				case FLM_NUMBER_TYPE:
				{
					if( uiCurDataLen)
					{	
						if( uiDescriptor & HTD_TYPE_INT64)
						{
							// Since the number is negative, extract and send it
							// as a signed 64-bit value.

							FLMINT64		i64Value;

							if( (pCurNode && RC_BAD( rc = GedGetINT64( 
									pCurNode, &i64Value))) ||
								(pCurField && RC_BAD( rc = pRecord->getINT64( 
									pCurField, &i64Value))))
							{
								goto Exit;
							}

							// Write the value.

							if( RC_BAD( rc = writeInt64( i64Value)))
							{
								goto Exit;
							}
						}
						else if( uiDescriptor & HTD_TYPE_INT)
						{
							// Since the number is negative, extract and send it
							// as a signed 32-bit value.

							FLMINT32		i32Value;

							if( (pCurNode && RC_BAD( rc = GedGetINT32( 
									pCurNode, &i32Value))) ||
								(pCurField && RC_BAD( rc = pRecord->getINT32( 
									pCurField, &i32Value))))
							{
								goto Exit;
							}

							// Write the value.

							if( RC_BAD( rc = writeInt32( i32Value)))
							{
								goto Exit;
							}
						}
						else if( uiDescriptor & HTD_TYPE_UINT64)
						{
							// The number is non-negative 64 bit

							FLMUINT64		ui64Value;

							if( (pCurNode && RC_BAD( rc = GedGetUINT64( 
									pCurNode, &ui64Value))) ||
								(pCurField && RC_BAD( rc = pRecord->getUINT64( 
									pCurField, &ui64Value))))
							{
								goto Exit;
							}

							// Write the value.

							if( RC_BAD( rc = writeUInt64( ui64Value)))
							{
								goto Exit;
							}
						}
						else
						{
							flmAssert( uiDescriptor & HTD_TYPE_UINT);
							
							// The number is non-negative 32 bit

							FLMUINT32		ui32Value;

							if( (pCurNode && RC_BAD( rc = GedGetUINT32( 
									pCurNode, &ui32Value))) ||
								(pCurField && RC_BAD( rc = pRecord->getUINT32( 
									pCurField, &ui32Value))))
							{
								goto Exit;
							}

							// Write the value.

							if( RC_BAD( rc = writeUInt32( ui32Value)))
							{
								goto Exit;
							}
						}
					}
					break;
				}

				case FLM_CONTEXT_TYPE:
				{
					// Extract the value.

					if( uiCurDataLen)
					{
						// The context node has a DRN value associated with
						// it.  Send the value as an unsigned 32-bit number.

						FLMUINT		uiDrn;

						if( (pCurNode && RC_BAD( rc = GedGetRecPtr( 
								pCurNode, &uiDrn))) ||
							(pCurField && RC_BAD( rc = pRecord->getUINT( 
								pCurField, &uiDrn))))
						{
							goto Exit;
						}

						if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiDrn)))
						{
							goto Exit;
						}
					}
					break;
				}

				case FLM_BINARY_TYPE:
				{
					// Extract the value.

					if( uiCurDataLen)
					{
						if( RC_BAD( rc = writeUShort( (FLMUINT16)uiCurDataLen)))
						{
							goto Exit;
						}

						if( RC_BAD( rc = write( pucCurData, uiCurDataLen)))
						{
							goto Exit;
						}
					}
					break;
				}

				default:
				{
					rc = RC_SET( FERR_NOT_IMPLEMENTED);
					goto Exit;
				}
			}
		}

		uiPrevLevel = uiCurLevel;
		if( pCurNode)
		{
			pCurNode = GedNodeNext( pCurNode);
		}
		else
		{
			pCurField = pRecord->next( pCurField);
		}
	}

	// Write a zero tag to indicate the end of the transmission.
	
	if( RC_BAD( rc = writeUShort( 0)))
	{
		goto Exit;
	}

Exit:

	m_tmpPool.poolReset( pvMark);
	return( rc);		
}


/****************************************************************************
Desc:	Flushes any pending data and closes the stream.
****************************************************************************/
RCODE FCS_DOS::close( void)
{
	RCODE		rc = FERR_OK;

	// Verify that setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Flush and terminate any pending message.

	if( RC_BAD( rc = endMessage()))
	{
		goto Exit;
	}
	
Exit:

	return( rc);
}

/****************************************************************************
Desc:	Flushes pending data.
****************************************************************************/
RCODE	FCS_DOS::flush( void)
{
	// Verify that setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	// Flush the output buffer.

	if( m_uiBOffset > 0)
	{
		m_pOStream->write( m_pucBuffer, m_uiBOffset);
		m_uiBOffset = 0;
	}

	// Flush the parent stream.

	return( m_pOStream->flush());
}

/****************************************************************************
Desc:	Flushes and terminates the current parent stream message
****************************************************************************/
RCODE	FCS_DOS::endMessage( void)
{
	RCODE		rc = FERR_OK;

	// Verify that Setup has been called.

	flmAssert( m_bSetupCalled == TRUE);

	if( !m_pOStream)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}
	
	// Flush any pending data.

	if( RC_BAD( rc = flush()))
	{
		goto Exit;
	}

	// Terminate the message.

	if( RC_BAD( rc = m_pOStream->endMessage()))
	{
		goto Exit;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:
*****************************************************************************/
FCS_FIS::FCS_FIS( void)
{
	m_pFileHdl = NULL;
	m_pucBufPos = NULL;
	m_pucBuffer = NULL;
	m_uiFileOffset = 0;
	m_uiBlockSize = 0;
	m_uiBlockEnd = 0;
}

/****************************************************************************
Desc:
*****************************************************************************/
FCS_FIS::~FCS_FIS( void)
{
	if( m_pFileHdl)
	{
		m_pFileHdl->Release();
	}

	if( m_pucBuffer)
	{
		f_free( &m_pucBuffer);
	}
}

/****************************************************************************
Desc:	Configures the input stream for use
*****************************************************************************/
RCODE FCS_FIS::setup(
	const char *	pszFilePath,
	FLMUINT			uiBlockSize)
{
	RCODE			rc = FERR_OK;
	
	flmAssert( uiBlockSize);

	if( RC_BAD( rc = close()))
	{
		goto Exit;
	}

	if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszFilePath,
		FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &m_pFileHdl)))
	{
		goto Exit;
	}

	m_uiBlockSize = uiBlockSize;
	if( RC_BAD( rc = f_alloc( m_uiBlockSize, &m_pucBuffer)))
	{
		goto Exit;
	}
	m_pucBufPos = m_pucBuffer;

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Closes the input stream and frees any resources
*****************************************************************************/
RCODE FCS_FIS::close( void)
{
	if( m_pFileHdl)
	{
		m_pFileHdl->Release();
		m_pFileHdl = NULL;
	}

	if( m_pucBuffer)
	{
		f_free( &m_pucBuffer);
	}
	
	return( FERR_OK);
}

/****************************************************************************
Desc:	Reads the requested amount of data from the stream.
*****************************************************************************/
RCODE FCS_FIS::read(
	FLMBYTE *		pucData,
	FLMUINT			uiLength,
	FLMUINT *		puiBytesRead)
{
	RCODE			rc = FERR_OK;
	FLMUINT		uiBytesRead = 0;
	FLMUINT		uiMaxSize;

	if( puiBytesRead)
	{
		*puiBytesRead = 0;
	}

	if( !m_pFileHdl)
	{
		rc = RC_SET( FERR_READING_FILE);
		goto Exit;
	}

	while( uiLength)
	{
		uiMaxSize = m_uiBlockEnd - (FLMUINT)(m_pucBufPos - m_pucBuffer);

		if( !uiMaxSize)
		{
			if( RC_BAD( rc = getNextPacket()))
			{
				goto Exit;
			}
		}
		else if( uiLength <= uiMaxSize)
		{
			f_memcpy( pucData, m_pucBufPos, uiLength);
			m_pucBufPos += uiLength;
			uiBytesRead += uiLength;
			uiLength = 0;
		}
		else
		{
			f_memcpy( pucData, m_pucBufPos, uiMaxSize);
			m_pucBufPos += uiMaxSize;
			pucData += uiMaxSize;
			uiLength -= uiMaxSize;
			uiBytesRead += uiMaxSize;
		}
	}

Exit:

	if( puiBytesRead)
	{
		*puiBytesRead = uiBytesRead;
	}

	return( rc);
}

/****************************************************************************
Desc:	Flushes any pending data.
*****************************************************************************/
RCODE FCS_FIS::flush( void)
{
	return( FERR_OK);
}

/****************************************************************************
Desc:	Flushes any pending data.
*****************************************************************************/
RCODE FCS_FIS::endMessage( void)
{
	return( FERR_OK);
}

/****************************************************************************
Desc:	Returns TRUE if the stream is open
*****************************************************************************/
FLMBOOL FCS_FIS::isOpen( void)
{
	return( TRUE);
}

/****************************************************************************
Desc:	Reads the next block from the file
*****************************************************************************/
RCODE FCS_FIS::getNextPacket( void)
{
	RCODE			rc = FERR_OK;

	if( RC_BAD( rc = m_pFileHdl->read( m_uiFileOffset, m_uiBlockSize,
		m_pucBuffer, &m_uiBlockEnd)))
	{
		if( rc == FERR_IO_END_OF_FILE)
		{
			if( !m_uiBlockEnd)
			{
				goto Exit;
			}
			else
			{
				rc = FERR_OK;
			}
		}
	}

	m_uiFileOffset += m_uiBlockEnd;
	m_pucBufPos = m_pucBuffer;

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Converts a UNICODE string consisting of 7-bit ASCII characters to
		a native string.
*****************************************************************************/
RCODE fcsConvertUnicodeToNative(
	F_Pool *					pPool,
	const FLMUNICODE *	puzUnicode,
	char **					ppucNative)
{
	RCODE			rc = FERR_OK;
	char *		pucDest = NULL;
	FLMUINT		uiCount;

	uiCount = 0;
	while( puzUnicode[ uiCount])
	{
		if( puzUnicode[ uiCount] > 0x007F)
		{
			rc = RC_SET( FERR_CONV_ILLEGAL);
			goto Exit;
		}
		uiCount++;
	}
	
	if( RC_BAD( rc = pPool->poolAlloc( uiCount + 1, (void **)&pucDest)))
	{
		goto Exit;
	}

	uiCount = 0;
	while( puzUnicode[ uiCount])
	{
		pucDest[ uiCount] = f_tonative( (FLMBYTE)puzUnicode[ uiCount]);
		uiCount++;
	}

	pucDest[ uiCount] = '\0';

Exit:

	*ppucNative = pucDest;
	return( rc);
}

/****************************************************************************
Desc:	Converts a native string to a double-byte UNICODE string.
*****************************************************************************/
RCODE fcsConvertNativeToUnicode(
	F_Pool *				pPool,
	const char *		pszNative,
	FLMUNICODE **		ppuzUnicode)
{
	RCODE				rc = FERR_OK;
	FLMUNICODE *	puzDest;
	FLMUINT			uiCount;
	
	uiCount = f_strlen( pszNative);
	
	if( RC_BAD( rc = pPool->poolAlloc( uiCount + 1, (void **)&puzDest)))
	{
		goto Exit;
	}
	
	uiCount = 0;
	while( pszNative[ uiCount])
	{
		puzDest[ uiCount] = (FLMUNICODE)f_toascii( pszNative[ uiCount]);
		uiCount++;
	}

	puzDest[ uiCount] = 0;

Exit:

	*ppuzUnicode = puzDest;
	return( rc);
}


/****************************************************************************
Desc:	Initializes members of a CREATE_OPTS structure to their default values
*****************************************************************************/
void fcsInitCreateOpts(
	CREATE_OPTS *	pCreateOptsRV)
{
	f_memset( pCreateOptsRV, 0, sizeof( CREATE_OPTS));

	pCreateOptsRV->uiBlockSize = DEFAULT_BLKSIZ;
	pCreateOptsRV->uiMinRflFileSize = DEFAULT_MIN_RFL_FILE_SIZE;
	pCreateOptsRV->uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE;
	pCreateOptsRV->bKeepRflFiles = DEFAULT_KEEP_RFL_FILES_FLAG;
	pCreateOptsRV->bLogAbortedTransToRfl = DEFAULT_LOG_ABORTED_TRANS_FLAG;
	pCreateOptsRV->uiDefaultLanguage = DEFAULT_LANG;
	pCreateOptsRV->uiVersionNum = FLM_CUR_FILE_FORMAT_VER_NUM;
}

/****************************************************************************
Desc:	Converts a CHECKPOINT_INFO structure to an HTD tree
*****************************************************************************/
RCODE fcsBuildCheckpointInfo(
	CHECKPOINT_INFO *		pChkptInfo,
	F_Pool *					pPool,
	NODE **					ppTree)
{
	NODE *		pRootNd = NULL;
	void *		pMark = pPool->poolMark();
	FLMUINT		uiTmp;
	RCODE			rc = FERR_OK;

	*ppTree = NULL;

	// Build the root node of the tree.

	if( (pRootNd = GedNodeMake( pPool, FCS_CPI_CONTEXT, &rc)) == NULL)
	{
		goto Exit;
	}

	// Add fields to the tree.

	if( pChkptInfo->bRunning)
	{
		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_RUNNING, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiRunningTime)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_START_TIME, (void *)&pChkptInfo->uiRunningTime,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->bForcingCheckpoint)
	{
		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_FORCING_CP, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiForceCheckpointRunningTime)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_FORCE_CP_START_TIME,
			(void *)&pChkptInfo->uiForceCheckpointRunningTime,
			4, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->iForceCheckpointReason)
	{
		uiTmp = pChkptInfo->iForceCheckpointReason;
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_FORCE_CP_REASON, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->bWritingDataBlocks)
	{
		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_WRITING_DATA_BLOCKS, (void *)&uiTmp,
			4, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiLogBlocksWritten)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_LOG_BLOCKS_WRITTEN, 
			(void *)&pChkptInfo->uiLogBlocksWritten,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiDataBlocksWritten)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_DATA_BLOCKS_WRITTEN,
			(void *)&pChkptInfo->uiDataBlocksWritten,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiDirtyCacheBytes)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_DIRTY_CACHE_BYTES,
			(void *)&pChkptInfo->uiDirtyCacheBytes,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiBlockSize)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_BLOCK_SIZE, (void *)&pChkptInfo->uiBlockSize,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pChkptInfo->uiWaitTruncateTime)
	{
		if( RC_BAD( rc = gedAddField( pPool, pRootNd,
			FCS_CPI_WAIT_TRUNC_TIME, (void *)&pChkptInfo->uiWaitTruncateTime,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	*ppTree = pRootNd;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Converts a F_LOCK_USER structure (or list of structures) to an HTD tree
*****************************************************************************/
RCODE fcsBuildLockUser(
	F_LOCK_USER *	pLockUser,
	FLMBOOL			bList,
	F_Pool *			pPool,
	NODE **			ppTree)
{
	NODE *		pRootNd = NULL;
	NODE *		pContextNd = NULL;
	void *		pMark = pPool->poolMark();
	RCODE			rc = FERR_OK;

	*ppTree = NULL;

	if( !pLockUser)
	{
		goto Exit;
	}

	// Add fields to the tree.

	for( ;;)
	{
		if( (pContextNd = GedNodeMake( pPool, FCS_LUSR_CONTEXT, &rc)) == NULL)
		{
			goto Exit;
		}

		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_LUSR_THREAD_ID, (void *)&pLockUser->uiThreadId,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_LUSR_TIME, (void *)&pLockUser->uiTime,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}

		if( pRootNd == NULL)
		{
			pRootNd = pContextNd;
		}
		else
		{
			GedSibGraft( pRootNd, pContextNd, GED_LAST);
		}

		if( !bList)
		{
			break;
		}

		pLockUser++;
		if( !pLockUser->uiTime)
		{
			break;
		}
	}

	*ppTree = pRootNd;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Converts an HTD tree to a F_LOCK_USER structure (or list of structures)
*****************************************************************************/
RCODE fcsExtractLockUser(
	NODE *			pTree,
	FLMBOOL			bExtractAsList,
	void *			pvLockUser)
{
	NODE *			pTmpNd;
	FLMUINT			uiItemCount = 0;
	FLMUINT			fieldPath[ 8];
	F_LOCK_USER *	pLockUser = NULL;
	FLMUINT			uiLoop;
	RCODE				rc = FERR_OK;

	if( !pTree)
	{
		if( bExtractAsList)
		{
			*((F_LOCK_USER **)pvLockUser) = NULL;
		}
		else
		{
			f_memset( (F_LOCK_USER *)pvLockUser, 0, sizeof( F_LOCK_USER));
		}
		goto Exit;
	}

	if( bExtractAsList)
	{
		pTmpNd = pTree;
		while( pTmpNd != NULL)
		{
			if( GedTagNum( pTmpNd) == FCS_LUSR_CONTEXT)
			{
				uiItemCount++;
			}
			pTmpNd = pTmpNd->next;
		}

		if( RC_BAD( rc = f_alloc( 
			sizeof( F_LOCK_USER) * (uiItemCount + 1), &pLockUser)))
		{
			goto Exit;
		}

		*((F_LOCK_USER **)pvLockUser) = pLockUser;
	}
	else
	{
		pLockUser = (F_LOCK_USER *)pvLockUser;
		f_memset( pLockUser, 0, sizeof( F_LOCK_USER));
		uiItemCount = 1;
	}
	
	// Parse the tree and extract the values.

	for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++)
	{
		fieldPath[ 0] = FCS_LUSR_CONTEXT;
		fieldPath[ 1] = FCS_LUSR_THREAD_ID;
		fieldPath[ 2] = 0;

		if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pLockUser[ uiLoop].uiThreadId);
		}

		fieldPath[ 0] = FCS_LUSR_CONTEXT;
		fieldPath[ 1] = FCS_LUSR_TIME;
		fieldPath[ 2] = 0;

		if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pLockUser[ uiLoop].uiTime);
		}

		pTree = GedSibNext( pTree);
	}

	if( bExtractAsList)
	{
		f_memset( &(pLockUser[ uiItemCount]), 0, sizeof( F_LOCK_USER));
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:	Extracts a CHECKPOINT_INFO structure from an HTD tree.
*****************************************************************************/
RCODE fcsExtractCheckpointInfo(
	NODE *					pTree,
	CHECKPOINT_INFO *		pChkptInfo)
{
	NODE *		pTmpNd;
	FLMUINT		fieldPath[ 8];
	FLMUINT		uiTmp;
	RCODE			rc = FERR_OK;

	// Initialize the structure

	f_memset( pChkptInfo, 0, sizeof( CHECKPOINT_INFO));

	// Parse the tree and extract the values.

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_RUNNING;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		pChkptInfo->bRunning = uiTmp ? TRUE : FALSE;
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_START_TIME;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiRunningTime);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_FORCING_CP;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		pChkptInfo->bForcingCheckpoint = uiTmp ? TRUE : FALSE;
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_FORCE_CP_START_TIME;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiForceCheckpointRunningTime);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_FORCE_CP_REASON;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetINT( pTmpNd, &pChkptInfo->iForceCheckpointReason);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_WRITING_DATA_BLOCKS;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		pChkptInfo->bWritingDataBlocks = uiTmp ? TRUE : FALSE;
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_LOG_BLOCKS_WRITTEN;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiLogBlocksWritten);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_DATA_BLOCKS_WRITTEN;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiDataBlocksWritten);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_DIRTY_CACHE_BYTES;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiDirtyCacheBytes);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_BLOCK_SIZE;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiBlockSize);
	}

	fieldPath[ 0] = FCS_CPI_CONTEXT;
	fieldPath[ 1] = FCS_CPI_WAIT_TRUNC_TIME;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pChkptInfo->uiWaitTruncateTime);
	}

	return( rc);
}

/****************************************************************************
Desc:	Translates a FLAIM query operator to a c/s query operator
*****************************************************************************/
RCODE fcsTranslateQFlmToQCSOp(
	QTYPES			eFlmOp,
	FLMUINT *		puiCSOp)
{
	RCODE		rc = FERR_OK;

	switch( eFlmOp)
	{
		case FLM_AND_OP:
			*puiCSOp = FCS_ITERATOR_AND_OP;
			break;
		case FLM_OR_OP:
			*puiCSOp = FCS_ITERATOR_OR_OP;
			break;
		case FLM_NOT_OP:
			*puiCSOp = FCS_ITERATOR_NOT_OP;
			break;
		case FLM_EQ_OP:
			*puiCSOp = FCS_ITERATOR_EQ_OP;
			break;
		case FLM_MATCH_OP:
			*puiCSOp = FCS_ITERATOR_MATCH_OP;
			break;
		case FLM_MATCH_BEGIN_OP:
			*puiCSOp = FCS_ITERATOR_MATCH_BEGIN_OP;
			break;
		case FLM_CONTAINS_OP:
			*puiCSOp = FCS_ITERATOR_CONTAINS_OP;
			break;
		case FLM_NE_OP:
			*puiCSOp = FCS_ITERATOR_NE_OP;
			break;
		case FLM_LT_OP:
			*puiCSOp = FCS_ITERATOR_LT_OP;
			break;
		case FLM_LE_OP:
			*puiCSOp = FCS_ITERATOR_LE_OP;
			break;
		case FLM_GT_OP:
			*puiCSOp = FCS_ITERATOR_GT_OP;
			break;
		case FLM_GE_OP:
			*puiCSOp = FCS_ITERATOR_GE_OP;
			break;
		case FLM_BITAND_OP:
			*puiCSOp = FCS_ITERATOR_BITAND_OP;
			break;
		case FLM_BITOR_OP:
			*puiCSOp = FCS_ITERATOR_BITOR_OP;
			break;
		case FLM_BITXOR_OP:
			*puiCSOp = FCS_ITERATOR_BITXOR_OP;
			break;
		case FLM_MULT_OP:
			*puiCSOp = FCS_ITERATOR_MULT_OP;
			break;
		case FLM_DIV_OP:
			*puiCSOp = FCS_ITERATOR_DIV_OP;
			break;
		case FLM_MOD_OP:
			*puiCSOp = FCS_ITERATOR_MOD_OP;
			break;
		case FLM_PLUS_OP:
			*puiCSOp = FCS_ITERATOR_PLUS_OP;
			break;
		case FLM_MINUS_OP:
			*puiCSOp = FCS_ITERATOR_MINUS_OP;
			break;
		case FLM_NEG_OP:
			*puiCSOp = FCS_ITERATOR_NEG_OP;
			break;
		case FLM_LPAREN_OP:
			*puiCSOp = FCS_ITERATOR_LPAREN_OP;
			break;
		case FLM_RPAREN_OP:
			*puiCSOp = FCS_ITERATOR_RPAREN_OP;
			break;
		default:
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			break;
	}

	return( rc);
}

/****************************************************************************
Desc:	Translates a FLAIM query operator to a c/s query operator
*****************************************************************************/
RCODE fcsTranslateQCSToQFlmOp(
	FLMUINT 		uiCSOp,
	QTYPES *		peFlmOp)
{
	RCODE		rc = FERR_OK;

	switch( uiCSOp)
	{
		case FCS_ITERATOR_AND_OP:
			*peFlmOp = FLM_AND_OP;
			break;
		case FCS_ITERATOR_OR_OP:
			*peFlmOp = FLM_OR_OP;
			break;
		case FCS_ITERATOR_NOT_OP:
			*peFlmOp = FLM_NOT_OP;
			break;
		case FCS_ITERATOR_EQ_OP:
			*peFlmOp = FLM_EQ_OP;
			break;
		case FCS_ITERATOR_MATCH_OP:
			*peFlmOp = FLM_MATCH_OP;
			break;
		case FCS_ITERATOR_MATCH_BEGIN_OP:
			*peFlmOp = FLM_MATCH_BEGIN_OP;
			break;
		case FCS_ITERATOR_CONTAINS_OP:
			*peFlmOp = FLM_CONTAINS_OP;
			break;
		case FCS_ITERATOR_NE_OP:
			*peFlmOp = FLM_NE_OP;
			break;
		case FCS_ITERATOR_LT_OP:
			*peFlmOp = FLM_LT_OP;
			break;
		case FCS_ITERATOR_LE_OP:
			*peFlmOp = FLM_LE_OP;
			break;
		case FCS_ITERATOR_GT_OP:
			*peFlmOp = FLM_GT_OP;
			break;
		case FCS_ITERATOR_GE_OP:
			*peFlmOp = FLM_GE_OP;
			break;
		case FCS_ITERATOR_BITAND_OP:
			*peFlmOp = FLM_BITAND_OP;
			break;
		case FCS_ITERATOR_BITOR_OP:
			*peFlmOp = FLM_BITOR_OP;
			break;
		case FCS_ITERATOR_BITXOR_OP:
			*peFlmOp = FLM_BITXOR_OP;
			break;
		case FCS_ITERATOR_MULT_OP:
			*peFlmOp = FLM_MULT_OP;
			break;
		case FCS_ITERATOR_DIV_OP:
			*peFlmOp = FLM_DIV_OP;
			break;
		case FCS_ITERATOR_MOD_OP:
			*peFlmOp = FLM_MOD_OP;
			break;
		case FCS_ITERATOR_PLUS_OP:
			*peFlmOp = FLM_PLUS_OP;
			break;
		case FCS_ITERATOR_MINUS_OP:
			*peFlmOp = FLM_MINUS_OP;
			break;
		case FCS_ITERATOR_NEG_OP:
			*peFlmOp = FLM_NEG_OP;
			break;
		case FCS_ITERATOR_LPAREN_OP:
			*peFlmOp = FLM_LPAREN_OP;
			break;
		case FCS_ITERATOR_RPAREN_OP:
			*peFlmOp = FLM_RPAREN_OP;
			break;
		default:
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			break;
	}

	return( rc);
}

/****************************************************************************
Desc:	Converts an FINDEX_STATUS structure to an HTD tree
*****************************************************************************/
RCODE fcsBuildIndexStatus(
	FINDEX_STATUS *	pIndexStatus,
	F_Pool *				pPool,
	NODE **				ppTree)
{
	NODE *		pContextNd = NULL;
	void *		pMark = pPool->poolMark();
	FLMUINT		uiTmp;
	RCODE			rc = FERR_OK;

	*ppTree = NULL;

	if( !pIndexStatus)
	{
		goto Exit;
	}

	// Add fields to the tree.

	if( (pContextNd = GedNodeMake( pPool, FCS_IXSTAT_CONTEXT, &rc)) == NULL)
	{
		goto Exit;
	}

	if( pIndexStatus->uiIndexNum)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_INDEX_NUM, (void *)&pIndexStatus->uiIndexNum,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pIndexStatus->uiStartTime)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_START_TIME, (void *)&pIndexStatus->uiStartTime,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}

		// Send the "auto-online" flag for backwards compatibility

		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_AUTO_ONLINE, 
			(void *)&uiTmp, 0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}

		// Send the priority (high) for backwards compatibility

		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_PRIORITY,
			(void *)&uiTmp, 0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	// Set the suspended time field (for backwards compatibility)
	// if the index is suspended

	if( pIndexStatus->bSuspended)
	{
		f_timeGetSeconds( &uiTmp);
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_SUSPEND_TIME, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pIndexStatus->uiLastRecordIdIndexed)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_LAST_REC_INDEXED, 
			(void *)&pIndexStatus->uiLastRecordIdIndexed,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pIndexStatus->uiKeysProcessed)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_KEYS_PROCESSED, 
			(void *)&pIndexStatus->uiKeysProcessed,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pIndexStatus->uiRecordsProcessed)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_RECS_PROCESSED, 
			(void *)&pIndexStatus->uiRecordsProcessed,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pIndexStatus->bSuspended)
	{
		uiTmp = (FLMUINT)pIndexStatus->bSuspended;
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_IXSTAT_STATE, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	*ppTree = pContextNd;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Extracts an FINDEX_STATUS structure from an HTD tree.
*****************************************************************************/
RCODE fcsExtractIndexStatus(
	NODE *					pTree,
	FINDEX_STATUS *		pIndexStatus)
{
	NODE *		pTmpNd;
	FLMUINT		fieldPath[ 8];
	RCODE			rc = FERR_OK;

	// Initialize the structure

	f_memset( pIndexStatus, 0, sizeof( FINDEX_STATUS));

	// Make sure pTree is non-null

	if( !pTree)
	{
		goto Exit;
	}

	// Parse the tree and extract the values.

	fieldPath[ 0] = FCS_IXSTAT_CONTEXT;
	fieldPath[ 1] = FCS_IXSTAT_INDEX_NUM;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pIndexStatus->uiIndexNum);
	}

	fieldPath[ 1] = FCS_IXSTAT_START_TIME;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pIndexStatus->uiStartTime);
	}

	fieldPath[ 1] = FCS_IXSTAT_LAST_REC_INDEXED;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pIndexStatus->uiLastRecordIdIndexed);
	}

	fieldPath[ 1] = FCS_IXSTAT_KEYS_PROCESSED;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pIndexStatus->uiKeysProcessed);
	}

	fieldPath[ 1] = FCS_IXSTAT_RECS_PROCESSED;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pIndexStatus->uiRecordsProcessed);
	}

	fieldPath[ 1] = FCS_IXSTAT_STATE;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		FLMUINT		uiTmp;
		(void)GedGetUINT( pTmpNd, &uiTmp);
		pIndexStatus->bSuspended = uiTmp ? TRUE : FALSE;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Converts an FLM_MEM_INFO structure to an HTD tree
*****************************************************************************/
RCODE fcsBuildMemInfo(
	FLM_MEM_INFO *		pMemInfo,
	F_Pool *				pPool,
	NODE **				ppTree)
{
	FLMUINT				uiTmp;
	NODE *				pContextNd = NULL;
	NODE *				pSubContext = NULL;
	void *				pMark = pPool->poolMark();
	FLM_CACHE_USAGE *	pUsage;
	RCODE					rc = FERR_OK;

	*ppTree = NULL;

	if( !pMemInfo)
	{
		goto Exit;
	}

	// Add fields to the tree.

	if( (pContextNd = GedNodeMake( pPool, 
		FCS_MEMINFO_CONTEXT, &rc)) == NULL)
	{
		goto Exit;
	}

	if( pMemInfo->bDynamicCacheAdjust)
	{
		uiTmp = 1;
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_MEMINFO_DYNA_CACHE_ADJ, (void *)&uiTmp,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pMemInfo->uiCacheAdjustPercent)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_MEMINFO_CACHE_ADJ_PERCENT, 
			(void *)&pMemInfo->uiCacheAdjustPercent,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pMemInfo->uiCacheAdjustMin)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_MEMINFO_CACHE_ADJ_MIN,
			(void *)&pMemInfo->uiCacheAdjustMin,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pMemInfo->uiCacheAdjustMax)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_MEMINFO_CACHE_ADJ_MAX,
			(void *)&pMemInfo->uiCacheAdjustMax,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pMemInfo->uiCacheAdjustMinToLeave)
	{
		if( RC_BAD( rc = gedAddField( pPool, pContextNd,
			FCS_MEMINFO_CACHE_ADJ_MIN_LEAVE,
			(void *)&pMemInfo->uiCacheAdjustMinToLeave,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	pUsage = &pMemInfo->RecordCache;
	if( (pSubContext = GedNodeMake( pPool, 
		FCS_MEMINFO_RECORD_CACHE, &rc)) == NULL)
	{
		goto Exit;
	}

add_usage:

	if( pUsage->uiMaxBytes)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_MAX_BYTES,
			(void *)&pUsage->uiMaxBytes,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiCount)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_COUNT,
			(void *)&pUsage->uiCount,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiOldVerCount)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_OLD_VER_COUNT,
			(void *)&pUsage->uiOldVerCount,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiTotalBytesAllocated)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_TOTAL_BYTES_ALLOC,
			(void *)&pUsage->uiTotalBytesAllocated,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiOldVerBytes)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_OLD_VER_BYTES,
			(void *)&pUsage->uiOldVerBytes,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiCacheHits)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_CACHE_HITS,
			(void *)&pUsage->uiCacheHits,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiCacheHitLooks)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_CACHE_HIT_LOOKS,
			(void *)&pUsage->uiCacheHitLooks,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiCacheFaults)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_CACHE_FAULTS,
			(void *)&pUsage->uiCacheFaults,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( pUsage->uiCacheFaultLooks)
	{
		if( RC_BAD( rc = gedAddField( pPool, pSubContext,
			FCS_MEMINFO_CACHE_FAULT_LOOKS,
			(void *)&pUsage->uiCacheFaultLooks,
			0, FLM_NUMBER_TYPE)))
		{
			goto Exit;
		}
	}

	if( GedChild( pSubContext))
	{
		GedChildGraft( pContextNd, pSubContext, GED_LAST);
	}

	if( pUsage != &pMemInfo->BlockCache)
	{
		pUsage = &pMemInfo->BlockCache;
		if( (pSubContext = GedNodeMake( pPool, 
			FCS_MEMINFO_BLOCK_CACHE, &rc)) == NULL)
		{
			goto Exit;
		}
		goto add_usage;
	}

	*ppTree = pContextNd;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Extracts a FLM_MEM_INFO structure from an HTD tree.
*****************************************************************************/
RCODE fcsExtractMemInfo(
	NODE *				pTree,
	FLM_MEM_INFO *		pMemInfo)
{
	NODE *					pTmpNd;
	FLMUINT					fieldPath[ 8];
	FLMUINT					uiTmp;
	FLM_CACHE_USAGE *		pUsage;
	FLMUINT					uiUsageTag;
	RCODE						rc = FERR_OK;

	// Initialize the structure

	f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO));

	// Make sure pTree is non-null

	if( !pTree)
	{
		goto Exit;
	}

	// Parse the tree and extract the values.

	fieldPath[ 0] = FCS_MEMINFO_CONTEXT;
	fieldPath[ 1] = FCS_MEMINFO_DYNA_CACHE_ADJ;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		pMemInfo->bDynamicCacheAdjust = (FLMBOOL)(uiTmp ? TRUE : FALSE);
	}

	fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_PERCENT;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustPercent);
	}

	fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MIN;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMin);
	}

	fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MAX;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMax);
	}

	fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MIN_LEAVE;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMinToLeave);
	}

	pUsage = &pMemInfo->RecordCache;
	uiUsageTag = FCS_MEMINFO_RECORD_CACHE;

get_usage:

	fieldPath[ 0] = FCS_MEMINFO_CONTEXT;
	fieldPath[ 1] = uiUsageTag;
	fieldPath[ 2] = FCS_MEMINFO_MAX_BYTES;
	fieldPath[ 3] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiMaxBytes);
	}

	fieldPath[ 2] = FCS_MEMINFO_COUNT;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiCount);
	}

	fieldPath[ 2] = FCS_MEMINFO_OLD_VER_COUNT;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiOldVerCount);
	}

	fieldPath[ 2] = FCS_MEMINFO_TOTAL_BYTES_ALLOC;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiTotalBytesAllocated);
	}

	fieldPath[ 2] = FCS_MEMINFO_OLD_VER_BYTES;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiOldVerBytes);
	}

	fieldPath[ 2] = FCS_MEMINFO_CACHE_HITS;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiCacheHits);
	}

	fieldPath[ 2] = FCS_MEMINFO_CACHE_HIT_LOOKS;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiCacheHitLooks);
	}

	fieldPath[ 2] = FCS_MEMINFO_CACHE_FAULTS;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiCacheFaults);
	}

	fieldPath[ 2] = FCS_MEMINFO_CACHE_FAULT_LOOKS;
	if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &pUsage->uiCacheFaultLooks);
	}

	if( pUsage != &pMemInfo->BlockCache)
	{
		pUsage = &pMemInfo->BlockCache;
		uiUsageTag = FCS_MEMINFO_BLOCK_CACHE;
		goto get_usage;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Builds a GEDCOM tree containing information on all FLAIM threads
*****************************************************************************/
RCODE fcsBuildThreadInfo(
	F_Pool *				pPool,
	NODE **				ppTree)
{
	NODE *				pContextNd = NULL;
	NODE *				pRootNd = NULL;
	void *				pMark = pPool->poolMark();
	F_THREAD_INFO *	pThreadInfo = NULL;
	FLMUINT				uiNumThreads;
	FLMUINT				uiLoop;
	RCODE					rc = FERR_OK;

	*ppTree = NULL;

	// Query FLAIM for available threads

	if( RC_BAD( rc = FlmGetThreadInfo( pPool, &pThreadInfo, &uiNumThreads)))
	{
		goto Exit;
	}

	if( (pRootNd = GedNodeMake( pPool, 
		FCS_THREAD_INFO_ROOT, &rc)) == NULL)
	{
		goto Exit;
	}

	if( RC_BAD( rc = GedPutRecPtr( pPool, pRootNd, uiNumThreads)))
	{
		goto Exit;
	}

	for( uiLoop = 0; uiLoop < uiNumThreads; uiLoop++)
	{
		// Add fields to the tree.

		if( (pContextNd = GedNodeMake( pPool, 
			FCS_THREAD_INFO_CONTEXT, &rc)) == NULL)
		{
			goto Exit;
		}

		GedChildGraft( pRootNd, pContextNd, GED_LAST);

		if( pThreadInfo->uiThreadId)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_THREAD_ID, (void *)&pThreadInfo->uiThreadId,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}
		}

		if( pThreadInfo->uiThreadGroup)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_THREAD_GROUP, (void *)&pThreadInfo->uiThreadGroup,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}
		}

		if( pThreadInfo->uiAppId)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_APP_ID, (void *)&pThreadInfo->uiAppId,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}
		}

		if( pThreadInfo->uiStartTime)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_START_TIME, (void *)&pThreadInfo->uiStartTime,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}
		}

		if( pThreadInfo->pszThreadName)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_THREAD_NAME, (void *)pThreadInfo->pszThreadName,
				0, FLM_TEXT_TYPE)))
			{
				goto Exit;
			}
		}

		if( pThreadInfo->pszThreadStatus)
		{
			if( RC_BAD( rc = gedAddField( pPool, pContextNd,
				FCS_THREADINFO_THREAD_STATUS, (void *)pThreadInfo->pszThreadStatus,
				0, FLM_TEXT_TYPE)))
			{
				goto Exit;
			}
		}

		pThreadInfo++;
	}

	*ppTree = pRootNd;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Extracts a list of F_THREAD_INFO structure from an HTD tree.
*****************************************************************************/
RCODE fcsExtractThreadInfo(
	NODE *				pTree,
	F_Pool *				pPool,
	F_THREAD_INFO **	ppThreadInfo,
	FLMUINT *			puiNumThreads)
{
	NODE *				pTmpNd;
	NODE *				pContextNd;
	void *				pMark = pPool->poolMark();
	FLMUINT				uiTmp;
	F_THREAD_INFO *	pThreadInfo;
	F_THREAD_INFO *	pCurThread;
	FLMUINT				uiNumThreads;
	FLMUINT				uiLoop;
	RCODE					rc = FERR_OK;

	*ppThreadInfo = NULL;
	*puiNumThreads = 0;

	if( GedTagNum( pTree) != FCS_THREAD_INFO_ROOT)
	{
		rc = RC_SET( FERR_SYNTAX);
		goto Exit;
	}

	if( RC_BAD( rc = GedGetUINT( pTree, &uiNumThreads)))
	{
		goto Exit;
	}

	if( !uiNumThreads)
	{
		goto Exit;
	}
	
	if( RC_BAD( rc = pPool->poolAlloc( uiNumThreads * sizeof( F_THREAD_INFO),
		(void **)&pThreadInfo)))
	{
		goto Exit;
	}

	if( (pContextNd = GedFind( 1, pTree, 
		FCS_THREAD_INFO_CONTEXT, 1)) == NULL)
	{
		rc = RC_SET( FERR_SYNTAX);
		goto Exit;
	}

	for( uiLoop = 0, pCurThread = pThreadInfo; 
		uiLoop < uiNumThreads; 
		uiLoop++, pCurThread++)
	{

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_THREAD_ID, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pCurThread->uiThreadId);
		}

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_THREAD_GROUP, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pCurThread->uiThreadGroup);
		}

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_APP_ID, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pCurThread->uiAppId);
		}

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_START_TIME, 1)) != NULL)
		{
			(void) GedGetUINT( pTmpNd, &pCurThread->uiStartTime);
		}

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_THREAD_NAME, 1)) != NULL)
		{
			if( RC_BAD( rc = GedGetNATIVE( pTmpNd, NULL, &uiTmp)))
			{
				goto Exit;
			}

			if( uiTmp)
			{
				uiTmp++;
				
				if( RC_BAD( rc = pPool->poolAlloc( uiTmp,
					(void **)&pCurThread->pszThreadName)))
				{
					goto Exit;
				}
			}

			if( RC_BAD( rc = GedGetNATIVE( pTmpNd, 
				pCurThread->pszThreadName, &uiTmp)))
			{
				goto Exit;
			}
		}

		if( (pTmpNd = GedFind( 1, pContextNd, 
			FCS_THREADINFO_THREAD_STATUS, 1)) != NULL)
		{
			if( RC_BAD( rc = GedGetNATIVE( pTmpNd, NULL, &uiTmp)))
			{
				goto Exit;
			}

			if( uiTmp)
			{
				uiTmp++;
				
				if( RC_BAD( rc = pPool->poolAlloc( uiTmp,
					(void **)pCurThread->pszThreadStatus)))
				{
					goto Exit;
				}
			}

			if( RC_BAD( rc = GedGetNATIVE( pTmpNd, 
				pCurThread->pszThreadStatus, &uiTmp)))
			{
				goto Exit;
			}
		}

		if( (pContextNd = GedSibNext( pContextNd)) != NULL)
		{
			if( GedTagNum( pContextNd) != FCS_THREAD_INFO_CONTEXT)
			{
				rc = RC_SET( FERR_SYNTAX);
				goto Exit;
			}
		}
	}

	*ppThreadInfo = pThreadInfo;
	*puiNumThreads = uiNumThreads;

Exit:

	if( RC_BAD( rc))
	{
		pPool->poolReset( pMark);
	}

	return( rc);
}

/****************************************************************************
Desc:	Reads a block from a remote database
*****************************************************************************/
RCODE fcsGetBlock(
	HFDB			hDb,
	FLMUINT		uiAddress,
	FLMUINT		uiMinTransId,
	FLMUINT *	puiCount,
	FLMUINT *	puiBlocksExamined,
	FLMUINT *	puiNextBlkAddr,
	FLMUINT		uiFlags,
	FLMBYTE *	pucBlock)
{
	FDB *				pDb = (FDB *)hDb;
	RCODE				rc = FERR_OK;

	flmAssert( IsInCSMode( hDb));

	fdbInitCS( pDb);
	CS_CONTEXT *		pCSContext = pDb->pCSContext;
	FCL_WIRE				Wire( pCSContext, pDb);

	if( !pCSContext->bConnectionGood)
	{
		rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.sendOp(
		FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_GET_BLOCK)))
	{
		goto Exit;
	}

	if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_ADDRESS, uiAddress)))
	{
		goto Transmission_Error;
	}

	if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_TRANSACTION_ID,
		uiMinTransId)))
	{
		goto Transmission_Error;
	}

	if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_COUNT, *puiCount)))
	{
		goto Transmission_Error;
	}

	if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiFlags)))
	{
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.sendTerminate()))
	{
		goto Transmission_Error;
	}

	// Read the response

	if (RC_BAD( rc = Wire.read()))
	{
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.getRCode()))
	{
		if( rc != FERR_IO_END_OF_FILE)
		{
			goto Exit;
		}
	}

	*puiBlocksExamined = (FLMUINT)Wire.getNumber2();
	*puiCount = (FLMUINT)Wire.getCount();
	*puiNextBlkAddr = Wire.getAddress();
	if( *puiCount)
	{
		f_memcpy( pucBlock, Wire.getBlock(), Wire.getBlockSize());
	}

	goto Exit;

Transmission_Error:
	pCSContext->bConnectionGood = FALSE;
	goto Exit;

Exit:

	fdbExit( pDb);
	return( rc);
}

/****************************************************************************
Desc:	Instructs the server to generate a serial number
*****************************************************************************/
RCODE fcsCreateSerialNumber(
	void *			pvCSContext,
	FLMBYTE *		pucSerialNum)
{
	RCODE				rc = FERR_OK;
	CS_CONTEXT *	pCSContext = (CS_CONTEXT *)pvCSContext;
	FCL_WIRE			Wire( pCSContext);

	if( !pCSContext->bConnectionGood)
	{
		rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.sendOp(
		FCS_OPCLASS_MISC, FCS_OP_CREATE_SERIAL_NUM)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = Wire.sendTerminate()))
	{
		goto Transmission_Error;
	}

	// Read the response

	if (RC_BAD( rc = Wire.read()))
	{
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.getRCode()))
	{
		goto Exit;
	}

	if( !Wire.getSerialNum())
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}

	f_memcpy( pucSerialNum, Wire.getSerialNum(), F_SERIAL_NUM_SIZE);
	goto Exit;

Transmission_Error:
	pCSContext->bConnectionGood = FALSE;
	goto Exit;

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Sets or clears the backup active flag for the database
Note:	This should only be called internally from the backup routines.
*****************************************************************************/
RCODE fcsSetBackupActiveFlag(
	HFDB			hDb,
	FLMBOOL		bBackupActive)
{
	FDB *				pDb = (FDB *)hDb;
	RCODE				rc = FERR_OK;

	flmAssert( IsInCSMode( hDb));

	fdbInitCS( pDb);
	CS_CONTEXT *		pCSContext = pDb->pCSContext;
	FCL_WIRE				Wire( pCSContext, pDb);

	if( !pCSContext->bConnectionGood)
	{
		rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.sendOp(
		FCS_OPCLASS_DATABASE, FCS_OP_DB_SET_BACKUP_FLAG)))
	{
		goto Exit;
	}

	if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_BOOLEAN, bBackupActive)))
	{
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.sendTerminate()))
	{
		goto Transmission_Error;
	}

	/* Read the response. */

	if (RC_BAD( rc = Wire.read()))
	{
		goto Transmission_Error;
	}

	if( RC_BAD( rc = Wire.getRCode()))
	{
		goto Exit;
	}

	goto Exit;

Transmission_Error:
	pCSContext->bConnectionGood = FALSE;
	goto Exit;

Exit:

	fdbExit( pDb);
	return( rc);
}

/****************************************************************************
Desc:	Commits an update transaction and updates the log header.
Note:	This should only be called internally from the backup routines.
*****************************************************************************/
RCODE fcsDbTransCommitEx(
	HFDB			hDb,
	FLMBOOL		bForceCheckpoint,
	FLMBYTE *	pucLogHdr)
{
	RCODE			rc = FERR_OK;
	FDB *			pDb = (FDB *)hDb;
	FLMBOOL		bInitializedFdb = FALSE;

	if( IsInCSMode( hDb))
	{
		fdbInitCS( pDb);
		bInitializedFdb = TRUE;
		FCL_WIRE Wire( pDb->pCSContext, pDb);

		if (!pDb->pCSContext->bConnectionGood)
		{
			rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
		}
		else
		{
			rc = Wire.doTransOp(
				FCS_OP_TRANSACTION_COMMIT_EX, 0, 0, 0,
				pucLogHdr, bForceCheckpoint);
		}
	}
	else
	{
		rc = RC_SET( FERR_ILLEGAL_OP);
		goto Exit;
	}

Exit:

	if( bInitializedFdb)
	{
		fdbExit( pDb);
	}

	return( rc);
}

/****************************************************************************
Desc: Generates a hex-encoded, obfuscated string consisting of characters
		0-9, A-F from the passed-in data buffer.
*****************************************************************************/
RCODE flmGenerateHexPacket(
	FLMBYTE *		pucData,
	FLMUINT			uiDataSize,
	FLMBYTE **		ppucPacket)
{
	RCODE						rc = FERR_OK;
	FLMBYTE *				pucBinPacket = NULL;
	FLMBYTE *				pucHexPacket = NULL;
	FLMBYTE *				pucUsedMap = NULL;
	FLMUINT32				ui32Tmp;
	FLMUINT					uiLoop;
	FLMUINT					uiSlot = 0;
	FLMBYTE					ucTmp[ 32];
	FLMUINT					uiBinPacketSize;
	FLMBOOL					bTmp;
	IF_RandomGenerator *	pRandGen = NULL;

	// Determine the packet size.  Make the minimum packet size 128 bytes
	// to account for the 64-byte "header" and for the overhead of the
	// CRC bytes, etc.  Round the packet size up to the nearest 64-byte
	// boundary after adding on the data size.

	uiBinPacketSize = 128 + uiDataSize;
	if( (uiBinPacketSize % 64) != 0)
	{
		uiBinPacketSize += (64 - (uiBinPacketSize % 64));
	}

	// Allocate buffers for building the packet

	if( RC_BAD( rc = f_alloc( uiBinPacketSize, &pucBinPacket)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = f_calloc( uiBinPacketSize, &pucUsedMap)))
	{
		goto Exit;
	}

	// First 64-bytes of the packet are reserved as a header

	f_memset( pucUsedMap, 0xFF, 64);

	// Initialize the random number generator and seed with the current
	// time.
	
	if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen)))
	{
		goto Exit;
	}

	// Fill the packet with random "noise"

	for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop += 4)
	{
		ui32Tmp = pRandGen->getUINT32();
		UD2FBA( ui32Tmp, &pucBinPacket[ uiLoop]);
	}

	for( uiLoop = 0; uiLoop < 512; uiLoop++)
	{
		ui32Tmp = pRandGen->getUINT32();
		UD2FBA( ui32Tmp, &pucBinPacket[ 
			pRandGen->getUINT32( 1, (int)(uiBinPacketSize / 4)) - 1]);
	}

	// Determine a new random seed based on bytes in the
	// packet header

	if( (ui32Tmp = (FLMUINT32)FB2UD( &pucBinPacket[ 
		pRandGen->getUINT32( 1, 61) - 1])) == 0)
	{
		ui32Tmp = 1;
	}

	pRandGen->setSeed( ui32Tmp);

	// Use the CRC of the header and the also first four bytes
	// of the header as an 8-byte validation signature.  This will
	// be needed to decode the packet.

	// Initialize the CRC to 0xFFFFFFFF and then compute the 1's
	// complement of the returned CRC.  This implements the 
	// "standard" CRC used by PKZIP, etc.

	ui32Tmp = 0xFFFFFFFF;
	f_updateCRC( pucBinPacket, 64, &ui32Tmp);
	ui32Tmp = ~ui32Tmp;
	UD2FBA( ui32Tmp, &ucTmp[ 0]);
	f_memcpy( &ucTmp[ 4], pucBinPacket, 4);

	for( uiLoop = 0; uiLoop < 8; uiLoop++)
	{
		bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize,
			pRandGen, &uiSlot);

		flmAssert( bTmp);
		pucBinPacket[ uiSlot] = ucTmp[ uiLoop];
	}

	// Encode the data size

	UD2FBA( (FLMUINT32)uiDataSize, &ucTmp[ 0]);
	for( uiLoop = 0; uiLoop < 4; uiLoop++)
	{
		bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize,
			pRandGen, &uiSlot);

		flmAssert( bTmp);
		pucBinPacket[ uiSlot] = ucTmp[ uiLoop];
	}

	// Randomly dispurse the data throughout the buffer.  Obfuscate the
	// data using the first 64-bytes of the buffer.

	for( uiLoop = 0; uiLoop < uiDataSize; uiLoop++)
	{
		bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize,
			pRandGen, &uiSlot);

		flmAssert( bTmp);
		pucBinPacket[ uiSlot] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64];
	}

	// Calculate and encode the data CRC

	ui32Tmp = 0xFFFFFFFF;
	f_updateCRC( pucData, uiDataSize, &ui32Tmp);
	ui32Tmp = ~ui32Tmp;
	UD2FBA( ui32Tmp, &ucTmp[ 0]);

	for( uiLoop = 0; uiLoop < 4; uiLoop++)
	{
		bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize,
			pRandGen, &uiSlot);

		flmAssert( bTmp);
		pucBinPacket[ uiSlot] = ucTmp[ uiLoop];
	}

	// Hex encode the binary packet

	if( RC_BAD( rc = f_alloc(
		(uiBinPacketSize * 2) + 1, &pucHexPacket)))
	{
		goto Exit;
	}

	for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop++)
	{
		FLMBYTE		ucLowNibble = pucBinPacket[ uiLoop] & 0x0F;
		FLMBYTE		ucHighNibble = (pucBinPacket[ uiLoop] & 0xF0) >> 4;

		pucHexPacket[ uiLoop << 1] = (ucHighNibble <= 9 
													? (ucHighNibble + '0') 
													: ((ucHighNibble - 0xA) + 'A'));

		pucHexPacket[ (uiLoop << 1) + 1] = (ucLowNibble <= 9 
													? (ucLowNibble + '0') 
													: ((ucLowNibble - 0xA) + 'A'));
	}

	pucHexPacket[ uiBinPacketSize * 2] = 0;
	*ppucPacket = pucHexPacket;
	pucHexPacket = NULL;

Exit:

	if( pucUsedMap)
	{
		f_free( &pucUsedMap);
	}

	if( pucBinPacket)
	{
		f_free( &pucBinPacket);
	}

	if( pucHexPacket)
	{
		f_free( &pucHexPacket);
	}
	
	if( pRandGen)
	{
		pRandGen->Release();
	}

	return( rc);
}

/****************************************************************************
Desc: Extracts a data buffer from the passed-in hex-encoded, obfuscated
		string.
*****************************************************************************/
RCODE flmExtractHexPacketData(
	FLMBYTE *		pucPacket,
	FLMBYTE **		ppucData,
	FLMUINT *		puiDataSize)
{
	RCODE						rc = FERR_OK;
	FLMBYTE *				pucUsedMap = NULL;
	FLMBYTE *				pucData = NULL;
	FLMBYTE *				pucBinPacket = NULL;
	FLMBYTE *				pucTmp;
	FLMUINT32				ui32Tmp;
	FLMUINT32				ui32FirstCRC;
	FLMUINT32				ui32Seed;
	FLMUINT					uiPacketSize;
	FLMUINT					uiLoop;
	FLMUINT					uiDataSize;
	FLMBYTE					ucTmp[ 32];
	FLMBYTE					ucVal = 0;
	FLMBOOL					bValid;
	IF_RandomGenerator *	pRandGen = NULL;

	if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen)))
	{
		goto Exit;
	}
	
	// Determine the packet size, ignoring all characters except 0-9, A-F

	uiPacketSize = 0;
	pucTmp = pucPacket;
	while( *pucTmp)
	{
		if( (*pucTmp >= '0' && *pucTmp <= '9') ||
			(*pucTmp >= 'A' && *pucTmp <= 'F'))
		{
			uiPacketSize++;
		}
		pucTmp++;
	}

	if( uiPacketSize & 0x00000001 || 
		(uiPacketSize % 4) != 0 || uiPacketSize < 128)
	{
		rc = RC_SET( FERR_INVALID_CRC);
		goto Exit;
	}

	// Get the actual size of the decoded binary data by dividing
	// the packet size by 2

	uiPacketSize >>= 1;

	// Allocate a buffer and convert the data from hex ASCII to binary

	if( RC_BAD( rc = f_calloc( 
		uiPacketSize, &pucBinPacket)))
	{
		goto Exit;
	}

	uiLoop = 0;
	pucTmp = pucPacket;
	while( *pucTmp)
	{
		bValid = FALSE;
		if( *pucTmp >= '0' && *pucTmp <= '9')
		{
			ucVal = *pucTmp - '0';
			bValid = TRUE;
		}
		else if( *pucTmp >= 'A' && *pucTmp <= 'F')
		{
			ucVal = (*pucTmp - 'A') + 0x0A;
			bValid = TRUE;
		}

		if( bValid)
		{
			if( (uiLoop & 0x00000001) == 0)
			{
				ucVal <<= 4;
			}
			pucBinPacket[ uiLoop >> 1] |= ucVal;
			uiLoop++;
		}

		pucTmp++;
	}

	// Allocate the data map

	if( RC_BAD( rc = f_calloc( uiPacketSize, &pucUsedMap)))
	{
		goto Exit;
	}

	// First 64-bytes of the packet are reserved

	f_memset( pucUsedMap, 0xFF, 64);

	// Determine the CRC of the 1st 64-bytes

	ui32FirstCRC = 0xFFFFFFFF;
	f_updateCRC( pucBinPacket, 64, &ui32FirstCRC);
	ui32FirstCRC = ~ui32FirstCRC;

	// Search for the random seed within the first 64 bytes

	ui32Seed = 0;
	for( uiLoop = 0; uiLoop < 61; uiLoop++)
	{
		ui32Tmp = FB2UD( &pucBinPacket[ uiLoop]);
		pRandGen->setSeed( ui32Tmp);

		if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, 
			pucBinPacket, pRandGen, ucTmp, 8)))
		{
			goto Exit;
		}

		if( FB2UD( &ucTmp[ 0]) == ui32FirstCRC && 
			f_memcmp( &ucTmp[ 4], &pucBinPacket[ 0], 4) == 0)
		{
			ui32Seed = ui32Tmp;
			break;
		}

		// Reset the "used" map
		
		f_memset( pucUsedMap, 0, uiPacketSize);
		f_memset( pucUsedMap, 0xFF, 64);
	}

	if( !ui32Seed)
	{
		rc = RC_SET( FERR_INVALID_CRC);
		goto Exit;
	}

	// Get the data size

	if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, 
		pucBinPacket, pRandGen, ucTmp, 4)))
	{
		goto Exit;
	}

	uiDataSize = (FLMUINT)FB2UD( &ucTmp[ 0]);
	if( uiDataSize > uiPacketSize)
	{
		rc = RC_SET( FERR_INVALID_CRC);
		goto Exit;
	}

	// Allocate space for the data

	if( RC_BAD( rc = f_alloc( uiDataSize, &pucData)))
	{
		goto Exit;
	}

	// Get the data

	if( RC_BAD( rc = flmGetNextHexPacketBytes( 
		pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, pucData, uiDataSize)))
	{
		goto Exit;
	}

	// Un-obfuscate the data

	for( uiLoop = 0; uiLoop < uiDataSize; uiLoop++)
	{
		pucData[ uiLoop] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64];
	}

	// Get the data CRC

	if( RC_BAD( rc = flmGetNextHexPacketBytes( 
		pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, ucTmp, 4)))
	{
		goto Exit;
	}

	// Verify the data CRC

	ui32Tmp = 0xFFFFFFFF;
	f_updateCRC( pucData, uiDataSize, &ui32Tmp);
	ui32Tmp = ~ui32Tmp;

	if( ui32Tmp != FB2UD( &ucTmp[ 0]))
	{
		rc = RC_SET( FERR_INVALID_CRC);
		goto Exit;
	}

	*ppucData = pucData;
	pucData = NULL;
	*puiDataSize = uiDataSize;

Exit:

	if( pucUsedMap)
	{
		f_free( &pucUsedMap);
	}

	if( pucData)
	{
		f_free( &pucData);
	}

	if( pucBinPacket)
	{
		f_free( &pucBinPacket);
	}
	
	if( pRandGen)
	{
		pRandGen->Release();
	}

	return( rc);
}

/****************************************************************************
Desc: Used by flmGenerateHexPacket to find an unused byte in the packet
*****************************************************************************/
FLMBOOL flmGetNextHexPacketSlot( 
	FLMBYTE *				pucUsedMap,
	FLMUINT					uiMapSize,
	IF_RandomGenerator *	pRandGen,
	FLMUINT *				puiSlot)
{
	FLMUINT		uiLoop;
	FLMUINT		uiSlot = 0;
	FLMBOOL		bFound = FALSE;

	for( uiLoop = 0; uiLoop < 100; uiLoop++)
	{
		uiSlot = ((FLMUINT)pRandGen->getUINT32()) % uiMapSize;
		if( !pucUsedMap[ uiSlot])
		{
			bFound = TRUE;
			goto Exit;
		}
	}

	// Scan the table from the top to find an empty slot

	for( uiSlot = 0; uiSlot < uiMapSize; uiSlot++)
	{
		if( !pucUsedMap[ uiSlot])
		{
			bFound = TRUE;
			goto Exit;
		}
	}

Exit:

	if( bFound)
	{
		flmAssert( uiSlot < uiMapSize);
		*puiSlot = uiSlot;
		pucUsedMap[ uiSlot] = 0xFF;
	}

	return( bFound);
}

/****************************************************************************
Desc: Used by flmExtractHexPacket to get the next N bytes of data from the
		packet.
*****************************************************************************/
RCODE flmGetNextHexPacketBytes( 
	FLMBYTE *				pucUsedMap,
	FLMUINT					uiMapSize,
	FLMBYTE *				pucPacket,
	IF_RandomGenerator *	pRandGen,
	FLMBYTE *				pucBuf,
	FLMUINT					uiCount)
{
	FLMUINT		uiSlot;
	FLMUINT		uiLoop;
	RCODE			rc = FERR_OK;

	for( uiLoop = 0; uiLoop < uiCount; uiLoop++)
	{
		if( !flmGetNextHexPacketSlot( pucUsedMap, uiMapSize, pRandGen, &uiSlot))
		{
			rc = RC_SET( FERR_INVALID_CRC);
			goto Exit;
		}

		pucBuf[ uiLoop] = pucPacket[ uiSlot];
	}

Exit:

	return( rc);
}
	
/****************************************************************************
Desc: Decodes a string containing %XX sequences and does it in place.
		Typically, this data comes from an HTML form.
****************************************************************************/
void fcsDecodeHttpString(
	char *		pszSrc)
{
	char *		pszDest;

	pszDest = pszSrc;
	while( *pszSrc)
	{
		if( *pszSrc == '%')
		{
			pszSrc++;
			if( f_isHexChar( (FLMBYTE)pszSrc[ 0]) && 
				 f_isHexChar( (FLMBYTE)pszSrc[ 1]))
			{
				*pszDest = (f_getHexVal( (FLMBYTE)pszSrc[ 0]) << 4) |
					f_getHexVal( (FLMBYTE)pszSrc[ 1]);

				pszSrc += 2;
				pszDest++;
				continue;
			}
		}
		else if( *pszSrc == '+')
		{
			*pszDest = ' ';
			pszSrc++;
			pszDest++;
			continue;
		}

		if( pszSrc != pszDest)
		{
			*pszDest = *pszSrc;
		}
		pszSrc++;
		pszDest++;
	}

	*pszDest = 0;
}

/****************************************************************************
Desc:
*****************************************************************************/
FCS_WIRE::FCS_WIRE( FCS_DIS * pDIStream, FCS_DOS * pDOStream)
{
	m_pool.poolInit( 2048);
	m_pPool = &m_pool;
	m_pDIStream = pDIStream;
	m_pDOStream = pDOStream;
	m_pRecord = NULL;
	m_pFromKey = NULL;
	m_pUntilKey = NULL;
	m_bSendGedcom = FALSE;
	(void)resetCommon();
}

/****************************************************************************
Desc:
*****************************************************************************/
FCS_WIRE::~FCS_WIRE( void)
{
	if( m_pRecord)
	{
		m_pRecord->Release();
		m_pRecord = NULL;
	}

	if( m_pFromKey)
	{
		m_pFromKey->Release();
		m_pFromKey = NULL;
	}

	if( m_pUntilKey)
	{
		m_pUntilKey->Release();
		m_pUntilKey = NULL;
	}

	m_pool.poolFree();
}

/****************************************************************************
Desc:	Resets all member variables to their default / initial values.
*****************************************************************************/
void FCS_WIRE::resetCommon( void)
{
	if( m_pRecord)
	{
		m_pRecord->Release();
		m_pRecord = NULL;
	}

	if( m_pFromKey)
	{
		m_pFromKey->Release();
		m_pFromKey = NULL;
	}

	if( m_pUntilKey)
	{
		m_pUntilKey->Release();
		m_pUntilKey = NULL;
	}

	m_uiClass = 0;
	m_uiOp = 0;
	m_uiRCode = 0;
	m_uiDrn = 0;
	m_uiTransType = FLM_READ_TRANS;
	m_ui64Count = 0;
	m_uiItemId = 0;
	m_uiIndexId = 0;
	m_puzItemName = NULL;
	m_pHTD = NULL;
	m_uiSessionId = FCS_INVALID_ID;
	m_uiSessionCookie = 0;
	m_uiContainer = FLM_DATA_CONTAINER;
	m_uiTransId = FCS_INVALID_ID;
	m_uiIteratorId = FCS_INVALID_ID;
	m_puzFilePath = NULL;
	m_puzFilePath2 = NULL;
	m_puzFilePath3 = NULL;
	m_pucBlock = NULL;
	m_pucSerialNum = NULL;
	m_uiBlockSize = 0;
	m_bIncludesAsync = FALSE;
	fcsInitCreateOpts( &m_CreateOpts);
	m_pPool->poolReset();
	m_bFlag = FALSE;
	m_ui64Number1 = 0;
	m_ui64Number2 = 0;
	m_ui64Number3 = 0;
	m_uiAddress = 0;
	m_uiFlags = 0;
	m_uiFlaimVersion = 0;
	m_i64SignedValue = 0;
	m_pCSContext = NULL;
	m_pDb = NULL;
}

/****************************************************************************
Desc:	Reads the class and opcode for a client request or server response.
*****************************************************************************/
RCODE FCS_WIRE::readOpcode( void)
{
	FLMBYTE	ucClass;
	FLMBYTE	ucOp;
	RCODE		rc = FERR_OK;

	if( RC_BAD( rc = m_pDIStream->read( &ucClass, 1, NULL)))
	{
		goto Exit;
	}
	m_uiClass = ucClass;

	if( RC_BAD( rc = m_pDIStream->read( &ucOp, 1, NULL)))
	{
		goto Exit;
	}
	m_uiOp = ucOp;

Exit:

	return( rc);
}

	
/****************************************************************************
Desc:	Reads a client request or server response and sets the appropriate
		member variable values.
*****************************************************************************/
RCODE FCS_WIRE::readCommon(
	FLMUINT *	puiTagRV,
	FLMBOOL *	pbEndRV)
{
	FLMUINT16	ui16Tmp;
	FLMUINT		uiTag = 0;
	RCODE			rc = FERR_OK;

	*pbEndRV = FALSE;

	// Read the tag.
	
	if( RC_BAD( rc = m_pDIStream->readUShort( &ui16Tmp)))
	{
		goto Exit;
	}
	uiTag = ui16Tmp;

	// Read the request / response values.
	
	switch( (uiTag & WIRE_VALUE_TAG_MASK))
	{
		case WIRE_VALUE_RCODE:
		{
			rc = readNumber( uiTag, &m_uiRCode);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_SESSION_ID:
		{
			rc = readNumber( uiTag, &m_uiSessionId);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_SESSION_COOKIE:
		{
			rc = readNumber( uiTag, &m_uiSessionCookie);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_CONTAINER_ID:
		{
			rc = readNumber( uiTag, &m_uiContainer);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_COUNT:
		{
			rc = readNumber( uiTag, NULL, NULL, &m_ui64Count);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_DRN:
		{
			rc = readNumber( uiTag, &m_uiDrn);
			uiTag = 0;
			break;
		}
		
		case WIRE_VALUE_INDEX_ID:
		{
			rc = readNumber( uiTag,	&m_uiIndexId);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_HTD:
		{
			rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pHTD, NULL);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_RECORD:
		{
			FlmRecord *		pRecord = m_pRecord;
			if( RC_OK( rc = receiveRecord( &pRecord)))
			{
				if( m_pRecord != pRecord)
				{
					if( m_pRecord)
					{
						m_pRecord->Release();
					}
					m_pRecord = pRecord;
				}
			}

			uiTag = 0;
			break;
		}

		case WIRE_VALUE_FROM_KEY:
		{
			FlmRecord *		pFromKey = m_pFromKey;
			if( RC_OK( rc = receiveRecord( &pFromKey)))
			{
				if( m_pFromKey != pFromKey)
				{
					if( m_pFromKey)
					{
						m_pFromKey->Release();
					}
					m_pFromKey = pFromKey;
				}
			}

			uiTag = 0;
			break;
		}

		case WIRE_VALUE_UNTIL_KEY:
		{
			FlmRecord *		pUntilKey = m_pUntilKey;
			if( RC_OK( rc = receiveRecord( &pUntilKey)))
			{
				if( m_pUntilKey != pUntilKey)
				{
					if( m_pUntilKey)
					{
						m_pUntilKey->Release();
					}
					m_pUntilKey = pUntilKey;
				}
			}

			uiTag = 0;
			break;
		}

		case WIRE_VALUE_CREATE_OPTS:
		{
			rc = receiveCreateOpts();
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_ITERATOR_ID:
		{
			rc = readNumber( uiTag, &m_uiIteratorId);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_TRANSACTION_TYPE:
		{
			rc = readNumber( uiTag, &m_uiTransType);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_TRANSACTION_ID:
		{
			rc = readNumber( uiTag, &m_uiTransId);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_ITEM_NAME:
		{
			rc = m_pDIStream->readUTF( m_pPool, &m_puzItemName);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_ITEM_ID:
		{
			rc = readNumber( uiTag, &m_uiItemId);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_BOOLEAN:
		{
			FLMUINT		uiTmp;

			if( RC_OK( rc = readNumber( uiTag, &uiTmp)))
			{
				m_bFlag = (FLMBOOL)((uiTmp) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE);
			}
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_NUMBER1:
		{
			rc = readNumber( uiTag, NULL, NULL, &m_ui64Number1);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_NUMBER2:
		{
			rc = readNumber( uiTag, NULL, NULL, &m_ui64Number2);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_NUMBER3:
		{
			rc = readNumber( uiTag, NULL, NULL, &m_ui64Number3);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_ADDRESS:
		{
			rc = readNumber( uiTag, &m_uiAddress);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_SIGNED_NUMBER:
		{
			rc = readNumber( uiTag, NULL, NULL, NULL, &m_i64SignedValue);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_FILE_PATH:
		{
			rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath);
			uiTag = 0;
			break;
		}
				
		case WIRE_VALUE_FILE_PATH_2:
		{
			rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath2);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_FILE_PATH_3:
		{
			rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath3);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_BLOCK:
		{
			rc = m_pDIStream->readLargeBinary( m_pPool, 
				&m_pucBlock, &m_uiBlockSize);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_SERIAL_NUM:
		{
			FLMUINT	uiSerialLen;

			if( RC_BAD( rc = m_pDIStream->readBinary( m_pPool,
				&m_pucSerialNum, &uiSerialLen)))
			{
				goto Exit;
			}
			
			if( uiSerialLen != F_SERIAL_NUM_SIZE)
			{
				rc = RC_SET( FERR_CONV_DEST_OVERFLOW);
				goto Exit;
			}

			uiTag = 0;
			break;
		}

		case WIRE_VALUE_START_ASYNC:
		{
			m_bIncludesAsync = TRUE;
			*pbEndRV = TRUE;
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_FLAGS:
		{
			rc = readNumber( uiTag, &m_uiFlags);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_FLAIM_VERSION:
		{
			rc = readNumber( uiTag, &m_uiFlaimVersion);
			uiTag = 0;
			break;
		}

		case WIRE_VALUE_TERMINATE:
		{
			rc = m_pDIStream->endMessage();
			*pbEndRV = TRUE;
			uiTag = 0;
			break;
		}

		default:
		{
			break;
		}
	}

Exit:

	*puiTagRV = uiTag;
	return( rc);
}

/****************************************************************************
Desc:	Copies the internal CREATE_OPTS structure into a user-supplied location
*****************************************************************************/
void FCS_WIRE::copyCreateOpts(
	CREATE_OPTS *		pCreateOptsRV)
{
	f_memcpy( pCreateOptsRV, &m_CreateOpts, sizeof( CREATE_OPTS));
}

/****************************************************************************
Desc:	Reads a numeric value from the specified data input stream.
*****************************************************************************/
RCODE FCS_WIRE::readNumber(
	FLMUINT			uiTag,
	FLMUINT *		puiNumber,
	FLMINT *			piNumber,
	FLMUINT64 *		pui64Number,
	FLMINT64 *		pi64Number)
{
	RCODE				rc = FERR_OK;

	flmAssert( !(puiNumber && piNumber));
	
	// Read the number of bytes specified by the
	// value's tag.

	switch( ((uiTag & WIRE_VALUE_TYPE_MASK) >> 
		WIRE_VALUE_TYPE_START_BIT))
	{
		case WIRE_VALUE_TYPE_GEN_0:
		{
			if( puiNumber)
			{
				*puiNumber = 0;
			}
			else if( piNumber)
			{
				*piNumber = 0;
			}
			else if( pui64Number)
			{
				*pui64Number = 0;
			}
			else if( pi64Number)
			{
				*pi64Number = 0;
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_1:
		{
			FLMBYTE	ucValue;

			if( RC_BAD( rc = m_pDIStream->read( &ucValue, 1, NULL)))
			{
				goto Exit;
			}

			if( puiNumber)
			{
				*puiNumber = (FLMUINT)ucValue;
			}
			else if( piNumber)
			{
				*piNumber = (FLMINT)*((FLMINT8 *)&ucValue);
			}
			else if( pui64Number)
			{
				*pui64Number = (FLMUINT64)ucValue;
			}
			else if( pi64Number)
			{
				*pi64Number = (FLMINT64)*((FLMINT8 *)&ucValue);
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_2:
		{
			if( puiNumber || pui64Number)
			{
				FLMUINT16	ui16Value;

				if( RC_BAD( rc = m_pDIStream->readUShort( &ui16Value)))
				{
					goto Exit;
				}

				if( puiNumber)
				{
					*puiNumber = (FLMUINT)ui16Value;
				}
				else if( pui64Number)
				{
					*pui64Number = (FLMUINT64)ui16Value;
				}
			}
			else if( piNumber || pi64Number)
			{
				FLMINT16		i16Value;

				if( RC_BAD( rc = m_pDIStream->readShort( &i16Value)))
				{
					goto Exit;
				}

				if( piNumber)
				{
					*piNumber = (FLMINT)i16Value;
				}
				else if( pi64Number)
				{
					*pi64Number = (FLMINT)i16Value;
				}
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_4:
		{
			if( puiNumber || pui64Number)
			{
				FLMUINT32	ui32Value;

				if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Value)))
				{
					goto Exit;
				}

				if( puiNumber)
				{
					*puiNumber = (FLMUINT)ui32Value;
				}
				else if( pui64Number)
				{
					*pui64Number = (FLMUINT64)ui32Value;
				}
			}
			else if( piNumber || pi64Number)
			{
				FLMINT32		i32Value;

				if( RC_BAD( rc = m_pDIStream->readInt( &i32Value)))
				{
					goto Exit;
				}

				if( piNumber)
				{
					*piNumber = (FLMINT)i32Value;
				}
				else if( pi64Number)
				{
					*pi64Number = (FLMINT64)i32Value;
				}
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_8:
		{
			if( puiNumber || piNumber)
			{
				rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
			}
			else
			{
				if( pui64Number)
				{
					if( RC_BAD( rc = m_pDIStream->readUInt64( pui64Number)))
					{
						goto Exit;
					}
				}
				else if( pi64Number)
				{
					if( RC_BAD( rc = m_pDIStream->readInt64( pi64Number)))
					{
						goto Exit;
					}
				}
				else
				{
					flmAssert( 0);
					rc = RC_SET( FERR_INVALID_PARM);
					goto Exit;
				}
			}
			break;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Writes an unsigned number to the specified data output stream.	
*****************************************************************************/
RCODE FCS_WIRE::writeUnsignedNumber(
	FLMUINT		uiTag,
	FLMUINT64	ui64Number)
{
	RCODE			rc = FERR_OK;

	if( ui64Number <= (FLMUINT64)0x000000FF)
	{
		uiTag |= WIRE_VALUE_TYPE_GEN_1 <<
			WIRE_VALUE_TYPE_START_BIT;

		if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = m_pDOStream->writeByte( (FLMBYTE)ui64Number)))
		{
			goto Exit;
		}
	}
	else if( ui64Number <= (FLMUINT64)0x0000FFFF)
	{
		uiTag |= WIRE_VALUE_TYPE_GEN_2 <<
			WIRE_VALUE_TYPE_START_BIT;

		if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)ui64Number)))
		{
			goto Exit;
		}
	}
	else if( ui64Number <= (FLMUINT64)0xFFFFFFFF)
	{
		uiTag |= WIRE_VALUE_TYPE_GEN_4 <<
			WIRE_VALUE_TYPE_START_BIT;

		if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = m_pDOStream->writeUInt32( (FLMUINT32)ui64Number)))
		{
			goto Exit;
		}
	}
	else
	{
		uiTag |= WIRE_VALUE_TYPE_GEN_8 <<
			WIRE_VALUE_TYPE_START_BIT;

		if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = m_pDOStream->writeUInt64( ui64Number)))
		{
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Writes a signed number to the specified data output stream.	
*****************************************************************************/
RCODE FCS_WIRE::writeSignedNumber(
	FLMUINT		uiTag,
	FLMINT64		i64Number)
{
	RCODE			rc = FERR_OK;

	if( RC_BAD( rc = writeUnsignedNumber( uiTag, (FLMUINT64)i64Number)))
	{
		goto Exit;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:	Skips a parameter or return value in the data stream
*****************************************************************************/
RCODE FCS_WIRE::skipValue( 
	FLMUINT		uiTag)
{
	RCODE			rc = FERR_OK;

	switch( ((uiTag & WIRE_VALUE_TYPE_MASK) >> 
		WIRE_VALUE_TYPE_START_BIT))
	{
		case WIRE_VALUE_TYPE_GEN_0:
		{
			break;
		}

		case WIRE_VALUE_TYPE_GEN_1:
		{
			if( RC_BAD( rc = m_pDIStream->skip( 1)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_2:
		{
			if( RC_BAD( rc = m_pDIStream->skip( 2)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_4:
		{
			if( RC_BAD( rc = m_pDIStream->skip( 4)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_GEN_8:
		{
			if( RC_BAD( rc = m_pDIStream->skip( 8)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_BINARY:
		{
			if( RC_BAD( rc = m_pDIStream->readBinary( NULL, NULL, NULL)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_LARGE_BINARY:
		{
			if( RC_BAD( rc = m_pDIStream->readLargeBinary( NULL, NULL, NULL)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_HTD:
		{
			if( RC_BAD( rc = m_pDIStream->readHTD( NULL, 0, 0, NULL, NULL)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_TYPE_RECORD:
		{
			if( RC_BAD( rc = receiveRecord( NULL)))
			{
				goto Exit;
			}
		}

		case WIRE_VALUE_TYPE_UTF:
		{
			if( RC_BAD( rc = m_pDIStream->readUTF( NULL, NULL)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
			rc = RC_SET( FERR_FAILURE);
			goto Exit;
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends an opcode to the client
*****************************************************************************/
RCODE FCS_WIRE::sendOpcode(
	FLMUINT		uiClass,
	FLMUINT		uiOp)
{
	FLMBYTE		ucClass = (FLMBYTE)uiClass;
	FLMBYTE		ucOp = (FLMBYTE)uiOp;
	RCODE			rc = FERR_OK;
	
	if( RC_BAD( rc = m_pDOStream->write( &ucClass, 1)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = m_pDOStream->write( &ucOp, 1)))
	{
		goto Exit;
	}

Exit:

	return( rc);
}


/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendTerminate( void)
{
	RCODE			rc = FERR_OK;
	
	if( RC_BAD( rc = m_pDOStream->writeUShort( 0)))
	{
		goto Exit;
	}

	if( RC_BAD( rc = m_pDOStream->endMessage()))
	{
		goto Exit;
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendNumber(
	FLMUINT			uiTag,
	FLMUINT64		ui64Value,
	FLMINT64			i64Value)
{
	RCODE				rc = FERR_OK;
	
	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_AREA_ID:
		case WIRE_VALUE_OP_SEQ_NUM:
		case WIRE_VALUE_FLAGS:
		case WIRE_VALUE_CLIENT_VERSION:
		case WIRE_VALUE_SESSION_ID:
		case WIRE_VALUE_SESSION_COOKIE:
		case WIRE_VALUE_CONTAINER_ID:
		case WIRE_VALUE_INDEX_ID:
		case WIRE_VALUE_ITEM_ID:
		case WIRE_VALUE_TRANSACTION_ID:
		case WIRE_VALUE_TRANSACTION_TYPE:
		case WIRE_VALUE_DRN:
		case WIRE_VALUE_COUNT:
		case WIRE_VALUE_AUTOTRANS:
		case WIRE_VALUE_MAX_LOCK_WAIT:
		case WIRE_VALUE_RECORD_COUNT:
		case WIRE_VALUE_BOOLEAN:
		case WIRE_VALUE_ITERATOR_ID:
		case WIRE_VALUE_SHARED_DICT_ID:
		case WIRE_VALUE_PARENT_DICT_ID:
		case WIRE_VALUE_TYPE:
		case WIRE_VALUE_NUMBER1:
		case WIRE_VALUE_NUMBER2:
		case WIRE_VALUE_NUMBER3:
		case WIRE_VALUE_ADDRESS:
		case WIRE_VALUE_FLAIM_VERSION:
		{
			if( RC_BAD( rc = writeUnsignedNumber( uiTag, ui64Value)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_SIGNED_NUMBER:
		{
			if( RC_BAD( rc = writeSignedNumber( uiTag, i64Value)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendBinary(
	FLMUINT			uiTag,
	FLMBYTE *		pData,
	FLMUINT			uiLength)
{
	RCODE				rc = FERR_OK;
	
	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_PASSWORD:
		case WIRE_VALUE_SERIAL_NUM:
		{
			uiTag |= WIRE_VALUE_TYPE_BINARY <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = m_pDOStream->writeBinary( pData, uiLength)))
			{
				goto Exit;
			}
			break;
		}

		case WIRE_VALUE_BLOCK:
		{
			uiTag |= WIRE_VALUE_TYPE_LARGE_BINARY <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = m_pDOStream->writeLargeBinary( pData, uiLength)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a record
*****************************************************************************/
RCODE FCS_WIRE::sendRecord(
	FLMUINT			uiTag,
	FlmRecord *		pRecord)
{
	RCODE				rc = FERR_OK;
#define RECORD_OUTPUT_BUFFER_SIZE	64
	FLMBYTE			pucBuffer[ RECORD_OUTPUT_BUFFER_SIZE];
	FLMBYTE *		pucBufPos;
	FLMBYTE			ucDescriptor;

	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_RECORD:
		case WIRE_VALUE_FROM_KEY:
		case WIRE_VALUE_UNTIL_KEY:
		{
			uiTag |= WIRE_VALUE_TYPE_RECORD <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			// The format of a record is (X = 1 bit):
			//
			// X				X					XXXXXX			0-64 bytes		HTD
			// RESERVED		HTD_FOLLOWS		ID_LENGTH		ID_VALUE			TREE (optional)
			//
			// This sequence can repeat, terminating with a 0 byte.

			ucDescriptor = 0;
			pucBufPos = pucBuffer;
			ucDescriptor |= (FLMBYTE)RECORD_HAS_HTD_FLAG;

			// Output the descriptor.

			ucDescriptor |= (FLMBYTE)RECORD_ID_SIZE;

			*pucBufPos = ucDescriptor;
			pucBufPos++;

			// Output the ID.  Current format of a record ID is:
			//
			//		4-byte container ID, 4-byte DRN

			f_UINT32ToBigEndian( (FLMUINT32)pRecord->getContainerID(), pucBufPos);
			pucBufPos += 4;

			f_UINT32ToBigEndian( (FLMUINT32)pRecord->getID(), pucBufPos);
			pucBufPos += 4;

			// Send the descriptor and record source.

			if( RC_BAD( rc = m_pDOStream->write( pucBuffer, 
				pucBufPos - pucBuffer)))
			{
				goto Exit;
			}

			// Send the record.

			if( RC_BAD( rc = m_pDOStream->writeHTD( NULL, pRecord, 
				FALSE, m_bSendGedcom)))
			{
				goto Exit;
			}
			
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendDrnList(
	FLMUINT			uiTag,
	FLMUINT *		puiList)
{
	FLMUINT		uiItemCount;
	FLMUINT		uiLoop;
	FLMUINT		uiBufSize = 0;
	FLMBYTE *	pucItemBuf = NULL;
	FLMBYTE *	pucItemPos;
	RCODE			rc = FERR_OK;
	
	if( !puiList)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}

	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_DRN_LIST:
		{
			uiTag |= WIRE_VALUE_TYPE_BINARY <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			// Count the entries in the list.  For now, support only a list of
			// 2048 elements.

			for( uiItemCount = 0; uiItemCount < 2048; uiItemCount++)
			{
				if( !puiList[ uiItemCount])
				{
					break;
				}
			}

			// Allocate a buffer for the list.

			uiBufSize = (FLMUINT)(((FLMUINT)sizeof( FLMUINT) * uiItemCount) + (FLMUINT)4);
			if( RC_BAD( rc = f_calloc( uiBufSize, &pucItemBuf)))
			{
				goto Exit;
			}
			pucItemPos = pucItemBuf;

			// Set the item count.

			UD2FBA( (FLMUINT32)uiItemCount, pucItemPos);
			pucItemPos += 4;

			// Put the items into the buffer.

			for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++)
			{
				UD2FBA( (FLMUINT32)puiList[ uiLoop], pucItemPos);
				pucItemPos += 4;
			}

			// Send the list.

			if( RC_BAD( rc = m_pDOStream->writeBinary(
				pucItemBuf, uiBufSize)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	if( pucItemBuf)
	{
		f_free( (void **)&pucItemBuf);
	}

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendString(
	FLMUINT			uiTag,
	FLMUNICODE *	puzString)
{
	RCODE				rc = FERR_OK;
	
	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_FILE_NAME:
		case WIRE_VALUE_FILE_PATH:
		case WIRE_VALUE_FILE_PATH_2:
		case WIRE_VALUE_FILE_PATH_3:
		case WIRE_VALUE_DICT_FILE_PATH:
		case WIRE_VALUE_ITEM_NAME:
		case WIRE_VALUE_DICT_BUFFER:
		{
			uiTag |= WIRE_VALUE_TYPE_UTF <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = m_pDOStream->writeUTF( puzString)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendHTD(
	FLMUINT			uiTag,
	NODE *			pHTD)
{
	RCODE				rc = FERR_OK;
	
	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_HTD:
		case WIRE_VALUE_ITERATOR_SELECT:
		case WIRE_VALUE_ITERATOR_FROM:
		case WIRE_VALUE_ITERATOR_WHERE:
		case WIRE_VALUE_ITERATOR_CONFIG:
		{
			uiTag |= WIRE_VALUE_TYPE_HTD <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = m_pDOStream->writeHTD( pHTD, NULL, TRUE, m_bSendGedcom)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendHTD(
	FLMUINT			uiTag,
	FlmRecord *		pRecord)
{
	RCODE				rc = FERR_OK;
	
	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_HTD:
		{
			uiTag |= WIRE_VALUE_TYPE_HTD <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = m_pDOStream->writeHTD( NULL, pRecord, 
				FALSE, m_bSendGedcom)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	return( rc);
}

/****************************************************************************
Desc:		Copies the current HTD tree to the application's pool
*****************************************************************************/
RCODE FCS_WIRE::getHTD( 
	F_Pool *		pPool,
	NODE **		ppTreeRV)
{
	RCODE			rc = FERR_OK;

	if( !m_pHTD)
	{
		*ppTreeRV = NULL;
		goto Exit;
	}

	if( (*ppTreeRV = GedCopy( pPool, GED_FOREST, m_pHTD)) == NULL)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}

Exit:

	return( rc);
}
	
/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendCreateOpts(
	FLMUINT			uiTag,
	CREATE_OPTS *	pCreateOpts)
{
	NODE *			pRootNd = NULL;
	void *			pvMark = m_pPool->poolMark();
	RCODE				rc = FERR_OK;
	FLMUINT			uiTmp;
	
	// If no create options, goto exit.

	if( !pCreateOpts)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}

	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_CREATE_OPTS:
		{
			uiTag |= WIRE_VALUE_TYPE_HTD << WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}

			// Build the root node of the CreateOpts tree.

			if( (pRootNd = GedNodeMake( m_pPool, FCS_COPT_CONTEXT, &rc)) == NULL)
			{
				goto Exit;
			}

			// Add fields to the tree.

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_BLOCK_SIZE, (void *)&pCreateOpts->uiBlockSize,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_MIN_RFL_FILE_SIZE, (void *)&pCreateOpts->uiMinRflFileSize,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_MAX_RFL_FILE_SIZE, (void *)&pCreateOpts->uiMaxRflFileSize,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			uiTmp = pCreateOpts->bKeepRflFiles ? 1 : 0;
			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_KEEP_RFL_FILES, (void *)&uiTmp,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			uiTmp = pCreateOpts->bLogAbortedTransToRfl ? 1 : 0;
			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_LOG_ABORTED_TRANS, (void *)&uiTmp,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_DEFAULT_LANG, (void *)&pCreateOpts->uiDefaultLanguage,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_VERSION, (void *)&pCreateOpts->uiVersionNum,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_APP_MAJOR_VER, (void *)&pCreateOpts->uiAppMajorVer,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			if( RC_BAD( rc = gedAddField( m_pPool, pRootNd,
				FCS_COPT_APP_MINOR_VER, (void *)&pCreateOpts->uiAppMinorVer,
				0, FLM_NUMBER_TYPE)))
			{
				goto Exit;
			}

			// Send the tree.

			if( RC_BAD( rc = m_pDOStream->writeHTD( pRootNd, NULL, 
				TRUE, m_bSendGedcom)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	m_pPool->poolReset( pvMark);
	return( rc);
}

/****************************************************************************
Desc:		Sends a value to the client
*****************************************************************************/
RCODE FCS_WIRE::sendNameTable(
	FLMUINT			uiTag,
	F_NameTable *	pNameTable)
{
	void *			pvMark = m_pPool->poolMark();
	NODE *			pRootNd;
	NODE *			pNd;
	NODE *			pItemIdNd;
	FLMUINT			uiMaxNameChars = 1024;
	FLMUNICODE *	puzItemName = NULL;
	FLMUINT			uiId;
	FLMUINT			uiType;
	FLMUINT			uiSubType;
	FLMUINT			uiNextPos;
	RCODE				rc = FERR_OK;
	
	// If the name table pointer is invalid, goto exit.

	if( !pNameTable)
	{
		rc = RC_SET( FERR_MEM);
		goto Exit;
	}

	// Allocate a temporary name buffer
	
	if( RC_BAD( rc = m_pPool->poolAlloc( uiMaxNameChars * sizeof( FLMUNICODE),
		(void **)&puzItemName)))
	{
		goto Exit;
	}

	// Send the parameter tag and value.

	switch( uiTag)
	{
		case WIRE_VALUE_NAME_TABLE:
		{
			uiTag |= WIRE_VALUE_TYPE_HTD <<
				WIRE_VALUE_TYPE_START_BIT;

			if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag)))
			{
				goto Exit;
			}


			// Build the root node of the name table tree.

			if( (pRootNd = GedNodeMake( m_pPool, 
				FCS_NAME_TABLE_CONTEXT, &rc)) == NULL)
			{
				goto Exit;
			}
				
			uiNextPos = 0;
			while( pNameTable->getNextTagNumOrder( &uiNextPos, puzItemName, 
				NULL, uiMaxNameChars * sizeof( FLMUNICODE), 
				&uiId, &uiType, &uiSubType))
			{
				if( (pItemIdNd = GedNodeMake( m_pPool, 
					FCS_NAME_TABLE_ITEM_ID, &rc)) == NULL)
				{
					goto Exit;
				}

				if( RC_BAD( rc = GedPutUINT( m_pPool, pItemIdNd, uiId)))
				{
					goto Exit;
				}

				if( (pNd = GedNodeMake( m_pPool, 
					FCS_NAME_TABLE_ITEM_NAME, &rc)) == NULL)
				{
					goto Exit;
				}

				if( RC_BAD( rc = GedPutUNICODE( m_pPool, pNd, puzItemName)))
				{
					goto Exit;
				}

				GedChildGraft( pItemIdNd, pNd, GED_LAST);

				if( (pNd = GedNodeMake( m_pPool, 
					FCS_NAME_TABLE_ITEM_TYPE, &rc)) == NULL)
				{
					goto Exit;
				}

				if( RC_BAD( rc = GedPutUINT( m_pPool, pNd, uiType)))
				{
					goto Exit;
				}

				GedChildGraft( pItemIdNd, pNd, GED_LAST);

				if( (pNd = GedNodeMake( m_pPool, 
					FCS_NAME_TABLE_ITEM_SUBTYPE, &rc)) == NULL)
				{
					goto Exit;
				}

				if( RC_BAD( rc = GedPutUINT( m_pPool, pNd, uiSubType)))
				{
					goto Exit;
				}

				GedChildGraft( pItemIdNd, pNd, GED_LAST);

				// Graft the item into the tree

				GedChildGraft( pRootNd, pItemIdNd, GED_LAST);

				// Release CPU to prevent CPU hog

				f_yieldCPU();
			}

			// Send the tree.

			if( RC_BAD( rc = m_pDOStream->writeHTD( pRootNd, 
				NULL, TRUE, m_bSendGedcom)))
			{
				goto Exit;
			}
			break;
		}

		default:
		{
#ifdef FLM_DEBUG
			flmAssert( 0);
#else
			rc = RC_SET( FERR_NOT_IMPLEMENTED);
			goto Exit;
#endif
		}
	}

Exit:

	m_pPool->poolReset( pvMark);
	return( rc);
}

/****************************************************************************
Desc:	Receives a record
*****************************************************************************/
RCODE FCS_WIRE::receiveRecord(
	FlmRecord **	ppRecord)
{
	FLMBYTE					ucDescriptor = 0;
	FLMUINT					uiIdLen = 0;
	FLMUINT32				ui32Container;
	FLMUINT32				ui32Drn;
	void *					pvMark = m_pPool->poolMark();
	FLMBOOL					bHasId = FALSE;
	RCODE						rc = FERR_OK;

	// Read the record.

	if( RC_BAD( rc = m_pDIStream->read( &ucDescriptor, 1, NULL)))
	{
		goto Exit;
	}

	uiIdLen = (FLMUINT)(ucDescriptor & RECORD_ID_SIZE_MASK);

	if( uiIdLen != RECORD_ID_SIZE)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}
	else if( uiIdLen)
	{
		bHasId = TRUE;
	}

	// Read the record ID.

	if( bHasId)
	{
		if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Container)))
		{
			goto Exit;
		}

		if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Drn)))
		{
			goto Exit;
		}
	}

	// Read the record.

	if( (ucDescriptor & RECORD_HAS_HTD_FLAG))
	{
		if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool,
			ui32Container, ui32Drn, NULL, ppRecord)))
		{
			goto Exit;
		}
	}

Exit:

	if( RC_BAD( rc) && ppRecord && *ppRecord)
	{
		(*ppRecord)->Release();
		*ppRecord = NULL;
	}

	m_pPool->poolReset( pvMark);
	return( rc);
}

/****************************************************************************
Desc:	Receives a CREATE_OPTS structure as an HTD tree.
*****************************************************************************/
RCODE FCS_WIRE::receiveCreateOpts( void)
{
	NODE *		pRootNd;
	NODE *		pTmpNd;
	void *		pPoolMark;
	FLMUINT		fieldPath[ 8];
	FLMUINT		uiTmp;
	RCODE			rc = FERR_OK;

	pPoolMark = m_pPool->poolMark();
  
	// Initialize the CREATE_OPTS structure to its default values.

	fcsInitCreateOpts( &m_CreateOpts);

	// Receive the tree.

	if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool,
		0, 0, &pRootNd, NULL)))
	{
		goto Exit;
	}

	// Parse the tree and extract the values.

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_BLOCK_SIZE;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiBlockSize);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_MIN_RFL_FILE_SIZE;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiMinRflFileSize);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_MAX_RFL_FILE_SIZE;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiMaxRflFileSize);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_KEEP_RFL_FILES;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		m_CreateOpts.bKeepRflFiles = (FLMBOOL)((uiTmp)
															? (FLMBOOL)TRUE
															: (FLMBOOL)FALSE);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_LOG_ABORTED_TRANS;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &uiTmp);
		m_CreateOpts.bLogAbortedTransToRfl = (FLMBOOL)((uiTmp)
																	  ? (FLMBOOL)TRUE
																	  : (FLMBOOL)FALSE);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_DEFAULT_LANG;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiDefaultLanguage);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_VERSION;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiVersionNum);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_APP_MAJOR_VER;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiAppMajorVer);
	}

	fieldPath[ 0] = FCS_COPT_CONTEXT;
	fieldPath[ 1] = FCS_COPT_APP_MINOR_VER;
	fieldPath[ 2] = 0;

	if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL)
	{
		(void) GedGetUINT( pTmpNd, &m_CreateOpts.uiAppMinorVer);
	}

Exit:

	m_pPool->poolReset( pPoolMark);
	return( rc);
}

/****************************************************************************
Desc:	Receives a name table.
*****************************************************************************/
RCODE FCS_WIRE::receiveNameTable(
	F_NameTable **		ppNameTable)
{
	NODE *			pRootNd;
	NODE *			pItemIdNd;
	NODE *			pNd = NULL;
	void *			pvMark = m_pPool->poolMark();
	FLMUINT			uiMaxNameChars = 1024;
	FLMUNICODE *	puzItemName;
	FLMUINT			uiItemId;
	FLMUINT			uiItemType;
	FLMUINT			uiItemSubType;
	F_NameTable *	pNameTable = NULL;
	FLMBOOL			bCreatedTable = FALSE;
	RCODE				rc = FERR_OK;

	// Allocate a temporary name buffer

	if( RC_BAD( rc = m_pPool->poolAlloc( uiMaxNameChars * sizeof( FLMUNICODE),
		(void **)&puzItemName)))
	{
		goto Exit;
	}
	
	// Initialize the name table.

	if( (pNameTable = *ppNameTable) == NULL)
	{
		if( (pNameTable = f_new F_NameTable) == NULL)
		{
			rc = RC_SET( FERR_MEM);
			goto Exit;
		}
		bCreatedTable = TRUE;
	}
	else
	{
		pNameTable->clearTable();
	}

	// Receive the tree.

	if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool,
		0, 0, &pRootNd, NULL)))
	{
		goto Exit;
	}

	// Parse the tree and extract the values.

	pItemIdNd = GedChild( pRootNd);
	while( pItemIdNd)
	{
		if( GedTagNum( pItemIdNd) == FCS_NAME_TABLE_ITEM_ID)
		{
			if( RC_BAD( rc = GedGetUINT( pItemIdNd, &uiItemId)))
			{
				goto Exit;
			}

			uiItemType = 0;
			uiItemSubType = 0;
			pNd = GedChild( pItemIdNd);
			while( pNd)
			{
				switch( GedTagNum( pNd))
				{
					case FCS_NAME_TABLE_ITEM_NAME:
					{
						FLMUINT		uiStrLen = uiMaxNameChars * sizeof( FLMUNICODE);

						if( RC_BAD( rc = GedGetUNICODE( pNd, puzItemName,
							&uiStrLen)))
						{
							goto Exit;
						}

						break;
					}

					case FCS_NAME_TABLE_ITEM_TYPE:
					{
						if( RC_BAD( rc = GedGetUINT( pNd, &uiItemType)))
						{
							goto Exit;
						}

						break;
					}

					case FCS_NAME_TABLE_ITEM_SUBTYPE:
					{
						if( RC_BAD( rc = GedGetUINT( pNd, &uiItemSubType)))
						{
							goto Exit;
						}

						break;
					}
				}

				pNd = GedSibNext( pNd);
			}

			if( puzItemName[ 0])
			{
				if( RC_BAD( rc = pNameTable->addTag( puzItemName, NULL, 
					uiItemId, uiItemType, uiItemSubType, FALSE)))
				{
					goto Exit;
				}
			}
		}

		pItemIdNd = GedSibNext( pItemIdNd);

		// Release CPU to prevent CPU hog

		f_yieldCPU();
	}

	pNameTable->sortTags();
	*ppNameTable = pNameTable;
	pNameTable = NULL;

Exit:

	if( pNameTable && bCreatedTable)
	{
		pNameTable->Release();
	}

	m_pPool->poolReset( pvMark);
	return( rc);
}

/****************************************************************************
Desc:
*****************************************************************************/
FCL_WIRE::FCL_WIRE( CS_CONTEXT * pCSContext, FDB * pDb) :
	FCS_WIRE( pCSContext != NULL ? pCSContext->pIDataStream : NULL,
			  pCSContext != NULL ? pCSContext->pODataStream : NULL)
{
	m_pCSContext = pCSContext;
	m_pDb = pDb;

	if( m_pCSContext)
	{
		m_bSendGedcom = m_pCSContext->bGedcomSupport;
	}
}

/****************************************************************************
Desc:	Sets the CS CONTEXT in FCL_WIRE and the I/O streams in FCS_WIRE
*****************************************************************************/
void FCL_WIRE::setContext(
	CS_CONTEXT *		pCSContext)
{
	m_pCSContext = pCSContext;
	m_bSendGedcom = pCSContext->bGedcomSupport;
	FCS_WIRE::setDIStream( pCSContext->pIDataStream);
	FCS_WIRE::setDOStream( pCSContext->pODataStream);
}

/****************************************************************************
Desc:	Send a client/server opcode with session id, and optionally the
		database id
****************************************************************************/
RCODE FCL_WIRE::sendOp(
	FLMUINT			uiClass,
	FLMUINT			uiOp)
{
	RCODE				rc = FERR_OK;

	if (!m_pCSContext->bConnectionGood)
	{
		rc = RC_SET( FERR_BAD_SERVER_CONNECTION);
		goto Exit;
	}

	// Send the class and opcode

	if (RC_BAD( rc = sendOpcode( (FLMBYTE)uiClass, (FLMBYTE)uiOp)))
	{
		goto Transmission_Error;
	}

	// Send session ID

	if (RC_BAD( rc = sendNumber(
		WIRE_VALUE_SESSION_ID, m_pCSContext->uiSessionId)))
	{
		goto Transmission_Error;
	}

	// Send session cookie

	if (RC_BAD( rc = sendNumber(
		WIRE_VALUE_SESSION_COOKIE, m_pCSContext->uiSessionCookie)))
	{
		goto Transmission_Error;
	}

	// Send operation sequence number

	m_pCSContext->uiOpSeqNum++;
	if (RC_BAD( rc = sendNumber( 
		WIRE_VALUE_OP_SEQ_NUM, m_pCSContext->uiOpSeqNum)))
	{
		goto Transmission_Error;
	}

Exit:

	return( rc);

Transmission_Error:
	m_pCSContext->bConnectionGood = FALSE;
	goto Exit;
}


/****************************************************************************
Desc:	This routine instructs the server to start or end a transaction
****************************************************************************/
RCODE FCL_WIRE::doTransOp(
	FLMUINT			uiOp,
	FLMUINT			uiTransType,
	FLMUINT			uiFlags,
	FLMUINT			uiMaxLockWait,
	FLMBYTE *		pszHeader,
	FLMBOOL			bForceCheckpoint)
{
	FLMUINT			uiTransFlags = 0;
	RCODE				rc = FERR_OK;

	// Send request to server

	if( RC_BAD( rc = sendOp( FCS_OPCLASS_TRANS, uiOp)))
	{
		goto Exit;
	}

	if( uiOp == FCS_OP_TRANSACTION_BEGIN)
	{
		if (RC_BAD( rc = sendNumber(
			WIRE_VALUE_TRANSACTION_TYPE, uiTransType)))
		{
			goto Transmission_Error;
		}

		if (RC_BAD( rc = sendNumber(
			WIRE_VALUE_MAX_LOCK_WAIT, uiMaxLockWait)))
		{
			goto Transmission_Error;
		}

		if( pszHeader)
		{
			uiTransFlags |= FCS_TRANS_FLAG_GET_HEADER;
		}

		if( uiFlags & FLM_DONT_KILL_TRANS)
		{
			uiTransFlags |= FCS_TRANS_FLAG_DONT_KILL;
		}

		if( uiFlags & FLM_DONT_POISON_CACHE)
		{
			uiTransFlags |= FCS_TRANS_FLAG_DONT_POISON;
		}
	}
	else if( uiOp == FCS_OP_TRANSACTION_COMMIT_EX)
	{
		if( pszHeader)
		{
			if( RC_BAD( rc = sendBinary(
				WIRE_VALUE_BLOCK, pszHeader, F_TRANS_HEADER_SIZE)))
			{
				goto Exit;
			}
		}

		if( bForceCheckpoint)
		{
			uiTransFlags |= FCS_TRANS_FORCE_CHECKPOINT;
		}
	}

	if( uiTransFlags)
	{
		if (RC_BAD( rc = sendNumber(
			WIRE_VALUE_FLAGS, uiTransFlags)))
		{
			goto Transmission_Error;
		}
	}

	if( RC_BAD( rc = sendTerminate()))
	{
		goto Transmission_Error;
	}

	// Read the response

	if( RC_BAD( rc = read()))
	{
		goto Transmission_Error;
	}

	if (RC_BAD( rc = getRCode()))
	{
		goto Exit;
	}

	if( pszHeader)
	{
		if( getBlockSize())
		{
			f_memcpy( pszHeader, getBlock(), getBlockSize());
		}
		else
		{
			f_memset( pszHeader, 0, 2048);
		}
	}

	if (!m_pDb)
	{
		m_pCSContext->bTransActive = (FLMBOOL)((uiOp == FCS_OP_TRANSACTION_BEGIN)
													  ? (FLMBOOL)TRUE
													  : (FLMBOOL)FALSE);
	}

Exit:

	return( rc);
Transmission_Error:
	m_pCSContext->bConnectionGood = FALSE;
	goto Exit;
}

/****************************************************************************
Desc:	Reads a server response for the client.
*****************************************************************************/
RCODE	FCL_WIRE::read( void)
{
	FLMUINT	uiTag;
	FLMUINT	uiCount = 0;
	FLMBOOL	bDone = FALSE;
	RCODE		rc = FERR_OK;

	// Read the opcode.

	if( RC_BAD( rc = readOpcode()))
	{
		goto Exit;
	}
	
	// Read the request / response values.
	
	for( ;;)
	{
		if (RC_BAD( rc = readCommon( &uiTag, &bDone)))
		{
			if( rc == FERR_EOF_HIT && !uiCount)
			{
				rc = FERR_OK;
			}
			goto Exit;
		}

		if( bDone)
		{
			goto Exit;
		}

		// uiTag will be non-zero if readCommon did not understand it.

		uiCount++;
		if( uiTag)
		{
			switch( (uiTag & WIRE_VALUE_TAG_MASK))
			{
				case WIRE_VALUE_NAME_TABLE:
				{
					if( RC_BAD( rc = receiveNameTable( &m_pNameTable)))
					{
						goto Exit;
					}
					break;
				}

				default:
				{
					if( RC_BAD( rc = skipValue( uiTag)))
					{
						goto Exit;
					}
					break;
				}
			}
		}
	}

Exit:

	if( rc == FERR_EOF_HIT)
	{
		rc = RC_SET( FERR_FAILURE);
		goto Exit;
	}

	return( rc);
}
