diff --git a/flaim/src/fcs.cpp b/flaim/src/fcs.cpp new file mode 100644 index 0000000..2b1045e --- /dev/null +++ b/flaim/src/fcs.cpp @@ -0,0 +1,8594 @@ +//------------------------------------------------------------------------- +// 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 $ +//------------------------------------------------------------------------- + +// These must be defined BEFORE any includes. Unfortunately, this +// also means that we can't use our FLM_HPUX define because it hasn't +// been set yet... + +#if defined( __hpux) || defined( hpux) + #define _XOPEN_SOURCE_EXTENDED 1 + #define _INCLUDE_HPUX_SOURCE +#endif + +#include "flaimsys.h" + +#if defined( FLM_NLM) && !defined ( __MWERKS__) + // Disable errors for "expression for 'while' is always false" + // Needed for FD_SET macro + #pragma warning 555 9 +#endif + +#ifdef FLM_WIN + // conditional expression is constant (from FD_SET()) + #pragma warning( disable : 4127) +#endif + +FSTATIC FLMBOOL flmGetNextHexPacketSlot( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + f_randomGenerator * pRandGen, + FLMUINT * puiSlot); + +FSTATIC RCODE flmGetNextHexPacketBytes( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + FLMBYTE * pucPacket, + f_randomGenerator * pRandGen, + FLMBYTE * pucBuf, + FLMUINT uiCount); + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_BIOS::FCS_BIOS( void) +{ + GedPoolInit( &m_pool, (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() +{ + GedPoolFree( &m_pool); +} + +/**************************************************************************** +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; + + GedPoolReset( &m_pool, NULL); + 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) + { + GedPoolReset( &m_pool, NULL); + 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; + m_pCurrWriteBlock = + (FCSBIOSBLOCK *)GedPoolCalloc( &m_pool, sizeof( FCSBIOSBLOCK)); + if( !m_pCurrWriteBlock) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + m_pCurrWriteBlock->pucBlock = + (FLMBYTE *)GedPoolAlloc( &m_pool, FCS_BIOS_BLOCK_SIZE); + + if( !m_pCurrWriteBlock->pucBlock) + { + rc = RC_SET( FERR_MEM); + 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) + { + GedPoolReset( &m_pool, NULL); + 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 = flmBigEndianToINT16( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUShort( + FLMUINT16 * pValue) +{ + RCODE rc; + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL))) + { + *pValue = flmBigEndianToUINT16( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readInt( + FLMINT32 * pValue) +{ + RCODE rc; + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) + { + *pValue = flmBigEndianToINT32( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUInt( + FLMUINT32 * pValue) +{ + RCODE rc; + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) + { + *pValue = flmBigEndianToUINT32( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readInt64( + FLMINT64 * pValue) +{ + RCODE rc; + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) + { + *pValue = flmBigEndianToINT64( (FLMBYTE *)pValue); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FCS_DIS::readUInt64( + FLMUINT64 * pValue) +{ + RCODE rc; + + if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) + { + *pValue = flmBigEndianToUINT64( (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) + { +#if defined( FLM_NLM) || defined( FLM_WIN) + if( uiCopySize == 1) + { + *pucPos = m_pucBuffer[ m_uiBOffset]; + } + else if( uiLength == 2) + { + *(FLMUINT16 *)pucPos = *((FLMUINT16 *)&m_pucBuffer[ m_uiBOffset]); + } + else if( uiLength == 4) + { + *(FLMUINT32 *)pucPos = *((FLMUINT32 *)&m_pucBuffer[ m_uiBOffset]); + } + else + { + f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize); + } +#else + f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize); +#endif + 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( + 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( (*ppValue = (FLMBYTE *)GedPoolAlloc( pPool, ui16DataSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + 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( (*ppValue = (FLMBYTE *)GedPoolAlloc( pPool, ui32DataSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + 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) + { + *ppValue = (FLMUNICODE *)GedPoolAlloc( pPool, + (FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * (FLMUINT)(ui16UTFLen + 1))); + } + 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( + 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 = GedPoolMark( pPool); + } + + 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_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_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) + { + GedPoolReset( pPool, pvMark); + } + } + +Exit: + + if( RC_OK( rc)) + { + if( ppNode) + { + *ppNode = pRoot; + } + } + else + { + if( ppRecord && *ppRecord) + { + (*ppRecord)->Release(); + } + } + + if( pPool && !ppNode) + { + GedPoolReset( pPool, pvMark); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FCS_DOS::FCS_DOS( void) +{ + m_pOStream = NULL; + m_uiBOffset = 0; + GedPoolInit( &m_tmpPool, 512); + m_bSetupCalled = FALSE; +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FCS_DOS::~FCS_DOS( void) +{ + if( m_bSetupCalled) + { + (void)close(); + } + GedPoolFree( &m_tmpPool); +} + +/**************************************************************************** +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) + { +#if defined( FLM_NLM) || defined( FLM_WIN) + if( uiLength == 1) + { + m_pucBuffer[ m_uiBOffset] = *pucData; + m_uiBOffset++; + } + else if( uiLength == 2) + { + *(FLMUINT16 *)&(m_pucBuffer[ m_uiBOffset]) = *((FLMUINT16 *)pucData); + m_uiBOffset += 2; + } + else if( uiLength == 4) + { + *(FLMUINT32 *)&(m_pucBuffer[ m_uiBOffset]) = *((FLMUINT32 *)pucData); + m_uiBOffset += 4; + } + else + { + f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength); + m_uiBOffset += uiLength; + } +#else + f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength); + m_uiBOffset += uiLength; +#endif + } + 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 = GedPoolMark( &m_tmpPool); + 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) + { + flmUINT16ToBigEndian( (FLMUINT16)GedTagNum( pCurNode), pucTmpBuf); + } + else if( pCurField) + { + flmUINT16ToBigEndian( (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)) + { + uiDescriptor |= HTD_TYPE_INT; + } + else + { + uiDescriptor |= HTD_TYPE_UINT; + } + 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); + + flmUINT16ToBigEndian( (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. + + GedPoolReset( &m_tmpPool, 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( (puzValue = (FLMUNICODE *)GedPoolAlloc( &m_tmpPool, + uiBufSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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. + + GedPoolReset( &m_tmpPool, pvMark); + + // Allocate the new buffer. + + if( (puzValue = (FLMUNICODE *)GedPoolAlloc( + &m_tmpPool, uiBufSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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_INT) + { + // Since the number is negative, extract and send it + // as a signed 32-bit value. + + FLMINT iValue; + + if( (pCurNode && RC_BAD( rc = GedGetINT( + pCurNode, &iValue))) || + (pCurField && RC_BAD( rc = pRecord->getINT( + pCurField, &iValue)))) + { + goto Exit; + } + + // Write the value. + + if( RC_BAD( rc = writeInt32( (FLMINT32)iValue))) + { + goto Exit; + } + } + else + { + // The number is non-negative + + FLMUINT uiValue; + + if( (pCurNode && RC_BAD( rc = GedGetUINT( + pCurNode, &uiValue))) || + (pCurField && RC_BAD( rc = pRecord->getUINT( + pCurField, &uiValue)))) + { + goto Exit; + } + + // Write the value. + + if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiValue))) + { + 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: + + GedPoolReset( &m_tmpPool, 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->Open( pszFilePath, + F_IO_RDONLY | F_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->Close(); + 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: Constructor +*****************************************************************************/ +FCS_IPIS::FCS_IPIS( FCS_TCP * pTcpObj) +{ + m_pTcpObj = pTcpObj; + m_pucBufPos = m_pucBuffer; + m_bStreamInvalid = FALSE; + m_bMessageActive = FALSE; + m_bGotLastPacket = FALSE; + m_uiPacketSize = 0; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_IPIS::~FCS_IPIS( void) +{ + (void)close(); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FCS_IPIS::isOpen( void) +{ + return( TRUE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FCS_IPIS::close( void) +{ + (void)endMessage(); + m_bStreamInvalid = FALSE; + return( FERR_OK); +} + +/**************************************************************************** +Desc: Reads the requested amount of data from the stream. +*****************************************************************************/ +RCODE FCS_IPIS::read( + FLMBYTE * pucData, + FLMUINT uiLength, + FLMUINT * puiBytesRead) +{ + FLMUINT uiBytesRead = 0; + FLMUINT uiMaxSize; + RCODE rc = FERR_OK; + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_bStreamInvalid) + { + while( uiLength) + { + uiMaxSize = m_uiPacketSize - (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; + } + } + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: Flushes any pending data. +*****************************************************************************/ +RCODE FCS_IPIS::flush( void) +{ + RCODE rc = FERR_OK; + + if( !m_bMessageActive) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = getNextPacket())) + { + if( rc == FERR_EOF_HIT) + { + rc = FERR_OK; + } + goto Exit; + } + } + +Exit: + + m_pucBufPos = m_pucBuffer; + return( rc); +} + + +/**************************************************************************** +Desc: Flushes any pending data. +*****************************************************************************/ +RCODE FCS_IPIS::endMessage( void) +{ + RCODE rc = FERR_OK; + + if( !m_bMessageActive) + { + goto Exit; + } + + if( RC_BAD( rc = flush())) + { + goto Exit; + } + +Exit: + + m_bMessageActive = FALSE; + m_bGotLastPacket = FALSE; + return( rc); +} + + +/**************************************************************************** +Desc: Reads the next packet off the wire. +*****************************************************************************/ +RCODE FCS_IPIS::getNextPacket( void) +{ + FLMBYTE pucDescriptor[ 2]; + FLMUINT uiDescriptor; + FLMUINT uiActualCnt = 0; + RCODE rc = FERR_OK; + + if( !m_bStreamInvalid) + { + if( !m_bMessageActive) + { + m_bMessageActive = TRUE; + } + + if( m_bGotLastPacket) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pTcpObj->readAll( pucDescriptor, + 2, &uiActualCnt))) + { + goto Exit; + } + + uiDescriptor = flmBigEndianToUINT16( pucDescriptor); + m_uiPacketSize = uiDescriptor & 0x7FFF; + + if( uiDescriptor & 0x8000) + { + m_bGotLastPacket = TRUE; + } + + if( m_uiPacketSize > FCS_IPIS_BUFFER_SIZE) + { + m_uiPacketSize = 0; + rc = RC_SET( FERR_READING_FILE); + goto Exit; + } + + if( m_uiPacketSize > 0) + { + if( RC_BAD( rc = m_pTcpObj->readAll( m_pucBuffer, + m_uiPacketSize, &uiActualCnt))) + { + goto Exit; + } + } + else + { + if( m_bGotLastPacket) + { + rc = RC_SET( FERR_EOF_HIT); + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + goto Exit; + } + + m_pucBufPos = m_pucBuffer; + } + else + { + rc = RC_SET( FERR_READING_FILE); + } + +Exit: + + if( RC_BAD( rc) && rc != FERR_EOF_HIT) + { + m_bStreamInvalid = TRUE; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FCS_IPOS::FCS_IPOS( FCS_TCP * pTcpObj) +{ + m_pTcpObj = pTcpObj; + m_pucBufPos = &(m_pucBuffer[ 2]); + m_bOpen = TRUE; + m_bMessageActive = FALSE; +} + +/**************************************************************************** +Desc: Flushes any pending data and closes the stream. +*****************************************************************************/ +RCODE FCS_IPOS::close( void) +{ + RCODE rc = FERR_OK; + + if( m_bOpen) + { + rc = endMessage(); + m_bOpen = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Writes the requested amount of data to the stream. +*****************************************************************************/ +RCODE FCS_IPOS::write( + FLMBYTE * pucData, + FLMUINT uiLength) +{ + FLMUINT uiMaxSize; + RCODE rc = FERR_OK; + + if( !uiLength) + { + goto Exit; + } + + if( m_bOpen) + { + while( uiLength) + { + uiMaxSize = + (FLMUINT)(FCS_IPOS_BUFFER_SIZE - (m_pucBufPos - m_pucBuffer)); + + if( !uiMaxSize) + { + if( RC_BAD( rc = flush())) + { + goto Exit; + } + } + else if( uiLength <= uiMaxSize) + { + f_memcpy( m_pucBufPos, pucData, uiLength); + m_pucBufPos += uiLength; + uiLength = 0; + } + else + { + f_memcpy( m_pucBufPos, pucData, uiMaxSize); + m_pucBufPos += uiMaxSize; + pucData += uiMaxSize; + uiLength -= uiMaxSize; + if( RC_BAD( rc = flush())) + { + goto Exit; + } + } + } + m_bMessageActive = TRUE; + } + else + { + rc = RC_SET( FERR_WRITING_FILE); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Flushes any pending data and optionally ends the current message. +*****************************************************************************/ +RCODE FCS_IPOS::_flush( + FLMBOOL bEndMessage) +{ + FLMUINT uiActualCnt; + FLMUINT uiLength; + FLMUINT uiDescriptor; + RCODE rc = FERR_OK; + + if( (uiLength = (FLMUINT)(m_pucBufPos - m_pucBuffer)) != 0) + { + uiDescriptor = uiLength - 2; + if( bEndMessage) + { + uiDescriptor |= 0x8000; + } + + if( uiDescriptor) + { + flmUINT16ToBigEndian( (FLMUINT16)uiDescriptor, m_pucBuffer); + + if( RC_BAD( rc = m_pTcpObj->write( m_pucBuffer, + uiLength, &uiActualCnt))) + { + goto Exit; + } + } + } + +Exit: + + m_pucBufPos = &(m_pucBuffer[ 2]); + return( rc); +} + +/**************************************************************************** +Desc: Terminates the current message +*****************************************************************************/ +RCODE FCS_IPOS::endMessage( void) +{ + RCODE rc = FERR_OK; + + + if( !m_bMessageActive) + { + goto Exit; + } + + if( RC_BAD( rc = _flush( TRUE))) + { + goto Exit; + } + +Exit: + + m_bMessageActive = FALSE; + return( rc); +} + +/******************************************************************** +Desc: Constructor +*********************************************************************/ +FCS_TCP::FCS_TCP( void) +{ + m_pszIp[ 0] = '\0'; + m_pszName[ 0] = '\0'; + m_pszPeerIp[ 0] = '\0'; + m_pszPeerName[ 0] = '\0'; + m_uiIOTimeout = 10; + m_iSocket = INVALID_SOCKET; + m_ulRemoteAddr = 0; + m_bInitialized = FALSE; + m_bConnected = FALSE; + +#ifndef FLM_UNIX + if( !WSAStartup( MAKEWORD(2, 0), &m_wsaData)) + { + m_bInitialized = TRUE; + } +#endif +} + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +FCS_TCP::~FCS_TCP( void ) +{ + if( m_bConnected) + { + close(); + } + +#ifndef FLM_UNIX + if( m_bInitialized) + { + WSACleanup(); + } +#endif +} + +/******************************************************************** +Desc: Gets information about the local host machine. +*********************************************************************/ +RCODE FCS_TCP::_GetLocalInfo( void) +{ + struct hostent * pHostEnt; + FLMUINT32 ui32IPAddr; + RCODE rc = FERR_OK; + + m_pszIp[ 0] = '\0'; + m_pszName[ 0] = '\0'; + + if( m_pszName[ 0] == '\0') + { + if( gethostname( m_pszName, (unsigned)sizeof( m_pszName))) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + } + + if( m_pszIp[ 0] == '\0' && + (pHostEnt = gethostbyname( m_pszName)) != NULL) + { + ui32IPAddr = (FLMUINT32)(*((unsigned long *)pHostEnt->h_addr)); + if( ui32IPAddr != (FLMUINT32)-1) + { + struct in_addr InAddr; + + InAddr.s_addr = ui32IPAddr; + f_strcpy( m_pszIp, inet_ntoa( InAddr)); + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Gets information about the remote machine. +*********************************************************************/ +RCODE FCS_TCP::_GetRemoteInfo( void) +{ + struct sockaddr_in SockAddrIn; + char * InetAddr = NULL; + struct hostent * HostsName; + RCODE rc = FERR_OK; + + m_pszPeerIp[ 0] = '\0'; + m_pszPeerName[ 0] = '\0'; + + SockAddrIn.sin_addr.s_addr = (unsigned)m_ulRemoteAddr; + + // inet_ntoa() - converts a 32-bit value in in_addr format into an ASCII + // string representing the address in dotted notation. + // VISIT: + // NetWare: Macro in arpa/inet.h. "Apps with multiple threads should use + // NWinet_ntoa instead of inet_ntoa. Then we can get rid of the semaphore! + + InetAddr = inet_ntoa( SockAddrIn.sin_addr ); + f_strcpy( m_pszPeerIp, InetAddr ); + + // Try to get the peer's host name by looking up his IP + // address. If found, copy IP Host name "BEVIS@NOVELL.COM" to TCPInfo + // otherwise, use his IP address as IP name. + // VISIT: + // Netware: "If your app has multiple threads, use either NWgethostbyaddr + // or NetDBgethostbyaddr(). This does the blocking? This may be done + // already in netdb.h - it is hard to tell. + + HostsName = gethostbyaddr( (char *)&SockAddrIn.sin_addr.s_addr, + (unsigned)sizeof( unsigned long), AF_INET ); + + if( HostsName != NULL) + { + f_strcpy( m_pszPeerName, (char*) HostsName->h_name ); + } + else + { + if (!InetAddr) + { + InetAddr = inet_ntoa( SockAddrIn.sin_addr); + } + f_strcpy( m_pszPeerName, InetAddr ); + } + + return( rc); +} + +/******************************************************************** +Desc: Tests for socket data readiness +*********************************************************************/ +RCODE FCS_TCP::_SocketPeek( + FLMINT iTimeoutVal, + FLMBOOL bPeekRead) +{ + struct timeval TimeOut; + int iMaxDescs; + fd_set GenDescriptors; + fd_set * DescrRead; + fd_set * DescrWrt; + RCODE rc = FERR_OK; + + if( m_iSocket != INVALID_SOCKET) + { + FD_ZERO( &GenDescriptors ); + FD_SET( m_iSocket, &GenDescriptors ); + + iMaxDescs = (int)(m_iSocket + 1); + DescrRead = bPeekRead ? &GenDescriptors : NULL; + DescrWrt = bPeekRead ? NULL : &GenDescriptors; + + TimeOut.tv_sec = (long)iTimeoutVal; + TimeOut.tv_usec = (long)0; + + if( select( iMaxDescs, DescrRead, DescrWrt, NULL, &TimeOut) < 0 ) + { + rc = RC_SET( FERR_SVR_SELECT_ERR); + goto Exit; + } + else + { + if( !FD_ISSET( m_iSocket, &GenDescriptors)) + { + rc = bPeekRead + ? RC_SET( FERR_SVR_READ_TIMEOUT) + : RC_SET( FERR_SVR_WRT_TIMEOUT); + } + } + } + else + { + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Writes data to the connection. +*********************************************************************/ +RCODE FCS_TCP::write( + FLMBYTE * pucDataBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiWrtCnt) +{ + FLMUINT uiPartialCnt; + FLMUINT uiToWrite; + FLMUINT uiHaveWritten = 0; + RCODE rc = FERR_OK; + + if( m_iSocket == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + + uiToWrite = uiDataCnt; + *puiWrtCnt = 0; + while( uiToWrite > 0) + { + // The internal write call checks the arguments + + if( RC_BAD( rc = _write( pucDataBuffer, + uiToWrite, &uiPartialCnt))) + { + goto Exit; + } + + pucDataBuffer += uiPartialCnt; + uiHaveWritten += uiPartialCnt; + uiToWrite = (FLMUINT)(uiDataCnt - uiHaveWritten); + *puiWrtCnt = uiHaveWritten; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE FCS_TCP::_write( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT *puiWrtCnt) +{ + FLMINT iRetryCount = 0; + FLMINT iWrtCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_iSocket != INVALID_SOCKET && pucBuffer && uiDataCnt); + +Retry: + + *puiWrtCnt = 0; + if( RC_OK( rc = _SocketPeek( m_uiIOTimeout, FALSE))) + { + iWrtCnt = send( m_iSocket, (char *)pucBuffer, (int)uiDataCnt, 0 ); + switch ( iWrtCnt ) + { + case -1: + { + *puiWrtCnt = 0; + rc = RC_SET( FERR_SVR_WRT_FAIL); + break; + } + + case 0: + { + rc = RC_SET( FERR_SVR_DISCONNECT); + break; + } + + default: + { + *puiWrtCnt = (FLMUINT)iWrtCnt; + break; + } + } + } + + if( RC_BAD( rc) && rc != FERR_SVR_WRT_TIMEOUT) + { +#ifndef FLM_UNIX + FLMINT iSockErr = WSAGetLastError(); +#else + FLMINT iSockErr = errno; +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAECONNABORTED) +#else + if( iSockErr == ECONNABORTED) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } +#if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK && iRetryCount < 5) +#else + else if( iSockErr == EWOULDBLOCK && iRetryCount < 5) +#endif + { + iRetryCount++; + f_sleep( (FLMUINT)(100 * iRetryCount)); + goto Retry; + } + } + + return( rc); +} + +/******************************************************************** +Desc: Reads data from the connection +*********************************************************************/ +RCODE FCS_TCP::read( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMINT iReadCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + if( RC_OK( rc = _SocketPeek( m_uiIOTimeout, TRUE))) + { + iReadCnt = (FLMINT)recv( m_iSocket, + (char *)pucBuffer, (int)uiDataCnt, 0); + switch ( iReadCnt) + { + case -1: + { + iReadCnt = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } + else + { + rc = RC_SET( FERR_SVR_READ_FAIL); + } + break; + } + + case 0: + { + rc = RC_SET( FERR_SVR_DISCONNECT); + break; + } + + default: + { + break; + } + } + } + + if( puiReadCnt) + { + *puiReadCnt = (FLMUINT)iReadCnt; + } + + return( rc); +} + +/******************************************************************** +Desc: Reads data from the connection - Timeout valkue is zero, no error + is generated if timeout occurs. +*********************************************************************/ +RCODE FCS_TCP::readNoWait( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMINT iReadCnt = 0; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + if( puiReadCnt) + { + *puiReadCnt = 0; + } + + if( RC_OK( rc = _SocketPeek( (FLMUINT)0, TRUE))) + { + iReadCnt = recv( m_iSocket, (char *)pucBuffer, (int)uiDataCnt, 0); + switch ( iReadCnt) + { + case -1: + { + *puiReadCnt = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( FERR_SVR_DISCONNECT); + } + else + { + rc = RC_SET( FERR_SVR_READ_FAIL); + } + goto Exit; + } + + case 0: + { + rc = RC_SET( FERR_SVR_DISCONNECT); + goto Exit; + } + + default: + { + break; + } + } + } + else if (rc == FERR_SVR_READ_TIMEOUT) + { + rc = FERR_OK; + } + + if( puiReadCnt) + { + *puiReadCnt = (FLMUINT)iReadCnt; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads data and does not return until all requested data has + been read or a timeout error has been encountered. +*********************************************************************/ +RCODE FCS_TCP::readAll( + FLMBYTE * pucBuffer, + FLMUINT uiDataCnt, + FLMUINT * puiReadCnt) +{ + FLMUINT uiToRead = 0; + FLMUINT uiHaveRead = 0; + FLMUINT uiPartialCnt; + RCODE rc = FERR_OK; + + flmAssert( m_bConnected && pucBuffer && uiDataCnt); + + uiToRead = uiDataCnt; + while( uiToRead) + { + if( RC_BAD( rc = read( pucBuffer, uiToRead, &uiPartialCnt))) + { + goto Exit; + } + + pucBuffer += uiPartialCnt; + uiHaveRead += uiPartialCnt; + uiToRead = (FLMUINT)(uiDataCnt - uiHaveRead); + + if( puiReadCnt) + { + *puiReadCnt = uiHaveRead; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Enables or disables Nagle's algorithm +*********************************************************************/ +RCODE FCS_TCP::setTcpDelay( + FLMBOOL bOn) +{ + RCODE rc = FERR_OK; + int iOn; + + if( m_iSocket != INVALID_SOCKET) + { + iOn = bOn ? 1 : 0; + + if( (setsockopt( m_iSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&iOn, + (unsigned)sizeof( iOn) )) < 0) + { + rc = RC_SET( FERR_SVR_SOCKOPT_FAIL); + goto Exit; + } + } + else + { + rc = RC_SET( FERR_SVR_ALREADY_CLOSED); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Closes any open connections +*********************************************************************/ +void FCS_TCP::close( + FLMBOOL bForce) +{ + if( m_iSocket == INVALID_SOCKET) + { + goto Exit; + } + +#ifdef FLM_NLM + F_UNREFERENCED_PARM( bForce); +#else + if( !bForce) + { + char ucTmpBuf[ 128]; + struct timeval tv; + fd_set fds; + fd_set fds_read; + fd_set fds_err; + + // Close our half of the connection + + shutdown( m_iSocket, 1); + + // Set up to wait for readable data on the socket + + FD_ZERO( &fds); + FD_SET( m_iSocket, &fds); + + tv.tv_sec = 10; + tv.tv_usec = 0; + + fds_read = fds; + fds_err = fds; + + // Wait for data or an error + + while( select( m_iSocket + 1, &fds_read, NULL, &fds_err, &tv) > 0) + { + if( recv( m_iSocket, ucTmpBuf, sizeof( ucTmpBuf), 0) <= 0) + { + break; + } + fds_read = fds; + fds_err = fds; + } + + shutdown( m_iSocket, 2); + } +#endif + +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + +Exit: + + m_iSocket = INVALID_SOCKET; + m_bConnected = FALSE; +} + +/******************************************************************** +Desc: Creates a client object +*********************************************************************/ +FCS_TCP_CLIENT::FCS_TCP_CLIENT( void) : FCS_TCP() +{ + m_bConnected = FALSE; +} + +/******************************************************************** +Desc: Closes any connections and frees client resources +*********************************************************************/ +FCS_TCP_CLIENT::~FCS_TCP_CLIENT( void ) +{ + (void)close(); +} + +/******************************************************************** +Desc: Opens a new connection +*********************************************************************/ +RCODE FCS_TCP_CLIENT::openConnection( + const char * pucHostName, + FLMUINT uiPort, + FLMUINT uiConnectTimeout, + FLMUINT uiDataTimeout) +{ + FLMINT iSockErr; + FLMINT iTries; + FLMINT iMaxTries = 5; + struct sockaddr_in address; + struct hostent * pHostEntry; + unsigned long ulIPAddr; + RCODE rc = FERR_OK; + + flmAssert( !m_bConnected); + m_iSocket = INVALID_SOCKET; + + if( pucHostName && pucHostName[ 0] != '\0') + { + ulIPAddr = inet_addr( (char *)pucHostName); + if( ulIPAddr == (unsigned long)INADDR_NONE) + { + pHostEntry = gethostbyname( (char *)pucHostName); + + if( !pHostEntry) + { + rc = RC_SET( FERR_SVR_NOIP_ADDR); + goto Exit; + } + else + { + ulIPAddr = *((unsigned long *)pHostEntry->h_addr); + } + + } + } + else + { + ulIPAddr = inet_addr( (char *)"127.0.0.1"); + } + + // Fill in the Socket structure with family type + + f_memset( (char*)&address, 0, sizeof( struct sockaddr_in)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = (unsigned)ulIPAddr; + address.sin_port = htons( (unsigned short)uiPort); + + // Allocate a socket, then attempt to connect to it! + + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + // Now attempt to connect with the specified partner host, + // time-out if connection doesn't complete within alloted time + +#ifdef FLM_WIN + + if( uiConnectTimeout) + { + if ( uiConnectTimeout < 5 ) + { + iMaxTries = (iMaxTries * uiConnectTimeout) / 5; + uiConnectTimeout = 5; + } + } + else + { + iMaxTries = 1; + } +#endif + + for( iTries = 0; iTries < iMaxTries; iTries++ ) + { + iSockErr = 0; + if( connect( m_iSocket, (struct sockaddr *)&address, + (unsigned)sizeof(struct sockaddr)) >= 0) + { + break; + } + + #ifndef FLM_UNIX + iSockErr = WSAGetLastError(); + #else + iSockErr = errno; + #endif + + #ifdef FLM_WIN + + // In WIN, we sometimes get WSAEINVAL when, if we keep + // trying, we will eventually connect. Therefore, + // here we'll treat WSAEINVAL as EINPROGRESS. + + if( iSockErr == WSAEINVAL) + { + #ifndef FLM_UNIX + closesocket( m_iSocket); + #else + ::close( m_iSocket); + #endif + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + iSockErr = WSAEINPROGRESS; + #else + iSockErr = EINPROGRESS; + #endif + continue; + } + #endif + + #if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAEISCONN ) + #else + if( iSockErr == EISCONN ) + #endif + { + break; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK) + #else + else if( iSockErr == EWOULDBLOCK) + #endif + { + // Let's wait a split second to give the connection + // request a chance. + + f_sleep( 100 ); + continue; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEINPROGRESS) + #else + else if( iSockErr == EINPROGRESS) + #endif + { + if( RC_OK( rc = _SocketPeek( uiConnectTimeout, FALSE))) + { + // Let's wait a split second to give the connection + // request a chance. + + f_sleep( 100 ); + continue; + } + } + + rc = RC_SET( FERR_SVR_CONNECT_FAIL); + } + + if( RC_BAD( rc)) + { + if( m_iSocket != INVALID_SOCKET) + { + #ifndef FLM_UNIX + closesocket( m_iSocket); + #else + ::close( m_iSocket); + #endif + m_iSocket = INVALID_SOCKET; + } + + goto Exit; + } + + m_uiIOTimeout = uiDataTimeout; + + setTcpDelay( TRUE); + m_bConnected = TRUE; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Constructor +*********************************************************************/ +FCS_TCP_SERVER::FCS_TCP_SERVER( void) : FCS_TCP() +{ + m_bBound = FALSE; +} + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +FCS_TCP_SERVER::~FCS_TCP_SERVER( void) +{ + if( m_bBound) + { + close( TRUE); + } +} + +/******************************************************************** +Desc: Bind to a port prior to listening for connections +*********************************************************************/ +RCODE FCS_TCP_SERVER::bind( + FLMUINT uiBindPort, + FLMBYTE * pucBindAddr) +{ + struct sockaddr_in address; + RCODE rc = FERR_OK; + + if( m_bBound) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_SOCK_FAIL); + goto Exit; + } + + f_memset( &address, 0, sizeof( address)); + address.sin_family = AF_INET; + if( !pucBindAddr) + { + address.sin_addr.s_addr = htonl( INADDR_ANY); + } + else + { + address.sin_addr.s_addr = inet_addr( (char *)pucBindAddr); + } + address.sin_port = htons( (unsigned short)uiBindPort); + + // Bind to the address+port + + if( ::bind( m_iSocket, (struct sockaddr *)&address, + (unsigned)sizeof( address)) != 0) + { + rc = RC_SET( FERR_SVR_BIND_FAIL); + goto Exit; + } + + // Bind succeeded! + // listen() prepares a socket to accept a connection and specifies a + // queue limit for incoming connections. The accept() accepts the connection. + // Listen returns immediatly. + +#ifdef FLM_NLM + if( listen( m_iSocket, 32 ) < 0) +#endif + { + if( listen( m_iSocket, 5 ) < 0) + { + rc = RC_SET( FERR_SVR_LISTEN_FAIL); + goto Exit; + } + } + + // Disable the packet send delay. + + setTcpDelay( TRUE); + m_bBound = TRUE; + +Exit: + + if( RC_BAD( rc) && m_iSocket != INVALID_SOCKET) + { +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + m_iSocket = INVALID_SOCKET; + } + + return( rc); +} + +/******************************************************************** +Desc: Wait for and accept a client connection +*********************************************************************/ +RCODE FCS_TCP_SERVER::connectClient( + FCS_TCP * pClient, + FLMINT uiConnectTimeout, + FLMINT uiDataTimeout) +{ + SOCKET iSocket; +#if defined( FLM_UNIX) + socklen_t iAddrLen; +#else + int iAddrLen; +#endif + struct sockaddr_in address; + RCODE rc = FERR_OK; + + if( !m_bBound) + { + rc = RC_SET( FERR_SVR_BIND_FAIL); + goto Exit; + } + + if( RC_BAD( rc = _SocketPeek( uiConnectTimeout, TRUE))) + { + goto Exit; + } + + iAddrLen = (int)sizeof( struct sockaddr); + if( (iSocket = accept( m_iSocket, + (struct sockaddr *)&address, &iAddrLen)) == INVALID_SOCKET) + { + rc = RC_SET( FERR_SVR_ACCEPT_FAIL); + goto Exit; + } + + pClient->m_ulRemoteAddr = address.sin_addr.s_addr; + pClient->m_iSocket = iSocket; + pClient->m_bConnected = TRUE; + pClient->m_uiIOTimeout = uiDataTimeout; + pClient->setTcpDelay( TRUE); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a UNICODE string consisting of 7-bit ASCII characters to + a native string. +*****************************************************************************/ +RCODE fcsConvertUnicodeToNative( + 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( (pucDest = (char *)GedPoolAlloc( pPool, + (FLMUINT)(uiCount + 1))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( + POOL * pPool, + const char * pszNative, + FLMUNICODE ** ppuzUnicode) +{ + RCODE rc = FERR_OK; + FLMUNICODE * puzDest; + FLMUINT uiCount; + + uiCount = f_strlen( pszNative); + + if( (puzDest = (FLMUNICODE *)GedPoolAlloc( pPool, + (FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * + (FLMUINT)(uiCount + 1)))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pRootNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + +/**************************************************************************** +Desc: Converts a LOCK_USER structure (or list of structures) to an HTD tree +*****************************************************************************/ +RCODE fcsBuildLockUser( + LOCK_USER * pLockUser, + FLMBOOL bList, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pRootNd = NULL; + NODE * pContextNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + +/**************************************************************************** +Desc: Converts an HTD tree to a LOCK_USER structure (or list of structures) +*****************************************************************************/ +RCODE fcsExtractLockUser( + NODE * pTree, + FLMBOOL bExtractAsList, + void * pvLockUser) +{ + NODE * pTmpNd; + FLMUINT uiItemCount = 0; + FLMUINT fieldPath[ 8]; + LOCK_USER * pLockUser = NULL; + FLMUINT uiLoop; + RCODE rc = FERR_OK; + + if( !pTree) + { + if( bExtractAsList) + { + *((LOCK_USER **)pvLockUser) = NULL; + } + else + { + f_memset( (LOCK_USER *)pvLockUser, 0, sizeof( 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( LOCK_USER) * (uiItemCount + 1), &pLockUser))) + { + goto Exit; + } + + *((LOCK_USER **)pvLockUser) = pLockUser; + } + else + { + pLockUser = (LOCK_USER *)pvLockUser; + f_memset( pLockUser, 0, sizeof( 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( 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, + POOL * pPool, + NODE ** ppTree) +{ + NODE * pContextNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, 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, + POOL * pPool, + NODE ** ppTree) +{ + FLMUINT uiTmp; + NODE * pContextNd = NULL; + NODE * pSubContext = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, 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( + POOL * pPool, + NODE ** ppTree) +{ + NODE * pContextNd = NULL; + NODE * pRootNd = NULL; + void * pMark = GedPoolMark( pPool); + 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)) + { + GedPoolReset( pPool, pMark); + } + + return( rc); +} + +/**************************************************************************** +Desc: Extracts a list of F_THREAD_INFO structure from an HTD tree. +*****************************************************************************/ +RCODE fcsExtractThreadInfo( + NODE * pTree, + POOL * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads) +{ + NODE * pTmpNd; + NODE * pContextNd; + void * pMark = GedPoolMark( pPool); + 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( (pThreadInfo = (F_THREAD_INFO *)GedPoolCalloc( pPool, + uiNumThreads * sizeof( F_THREAD_INFO))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pCurThread->pszThreadName = (char *)GedPoolAlloc( + pPool, uiTmp)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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( (pCurThread->pszThreadStatus = (char *)GedPoolAlloc( + pPool, uiTmp)) == NULL) + { + rc = RC_SET( FERR_MEM); + 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)) + { + GedPoolReset( pPool, 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) +{ + FLMUINT32 * pui32CRCTbl = NULL; + FLMBYTE * pucBinPacket = NULL; + FLMBYTE * pucHexPacket = NULL; + FLMBYTE * pucUsedMap = NULL; + FLMUINT32 ui32Tmp; + FLMUINT uiLoop; + FLMUINT uiSlot = 0; + FLMBYTE ucTmp[ 32]; + FLMUINT uiBinPacketSize; + FLMBOOL bTmp; + f_randomGenerator randGen; + RCODE rc = FERR_OK; + + // 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 CRC table. + + if( RC_BAD( rc = f_initCRCTable( &pui32CRCTbl))) + { + goto Exit; + } + + // Initialize the random number generator and seed with the current + // time. + + f_randomize( &randGen); + + // Fill the packet with random "noise" + + for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop += 4) + { + ui32Tmp = f_randomLong( &randGen); + UD2FBA( ui32Tmp, &pucBinPacket[ uiLoop]); + } + + for( uiLoop = 0; uiLoop < 512; uiLoop++) + { + ui32Tmp = f_randomLong( &randGen); + UD2FBA( ui32Tmp, &pucBinPacket[ f_randomChoice( + &randGen, 1, (int)(uiBinPacketSize / 4)) - 1]); + } + + // Determine a new random seed based on bytes in the + // packet header + + if( (ui32Tmp = (FLMUINT32)FB2UD( &pucBinPacket[ + f_randomChoice( &randGen, 1, 61) - 1])) == 0) + { + ui32Tmp = 1; + } + + f_randomSetSeed( &randGen, 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( pui32CRCTbl, 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, + &randGen, &uiSlot); + + flmAssert( bTmp); + pucBinPacket[ uiSlot] = ucTmp[ uiLoop]; + } + + // Encode the data size + + UD2FBA( uiDataSize, &ucTmp[ 0]); + for( uiLoop = 0; uiLoop < 4; uiLoop++) + { + bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, + &randGen, &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, + &randGen, &uiSlot); + + flmAssert( bTmp); + pucBinPacket[ uiSlot] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64]; + } + + // Calculate and encode the data CRC + + ui32Tmp = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, pucData, uiDataSize, &ui32Tmp); + ui32Tmp = ~ui32Tmp; + UD2FBA( ui32Tmp, &ucTmp[ 0]); + + for( uiLoop = 0; uiLoop < 4; uiLoop++) + { + bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, + &randGen, &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( pui32CRCTbl) + { + f_freeCRCTable( &pui32CRCTbl); + } + + if( pucUsedMap) + { + f_free( &pucUsedMap); + } + + if( pucBinPacket) + { + f_free( &pucBinPacket); + } + + if( pucHexPacket) + { + f_free( &pucHexPacket); + } + + return( rc); +} + +/**************************************************************************** +Desc: Extracts a data buffer from the passed-in hex-encoded, obfuscated + string. +*****************************************************************************/ +RCODE flmExtractHexPacketData( + FLMBYTE * pucPacket, + FLMBYTE ** ppucData, + FLMUINT * puiDataSize) +{ + FLMUINT32 * pui32CRCTbl = NULL; + 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; + f_randomGenerator randGen; + RCODE rc = FERR_OK; + + // 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); + + // Initialize the CRC table + + if( RC_BAD( rc = f_initCRCTable( &pui32CRCTbl))) + { + goto Exit; + } + + // Determine the CRC of the 1st 64-bytes + + ui32FirstCRC = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, 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]); + f_randomSetSeed( &randGen, ui32Tmp); + + if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, + pucBinPacket, &randGen, 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, &randGen, 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, &randGen, 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, &randGen, ucTmp, 4))) + { + goto Exit; + } + + // Verify the data CRC + + ui32Tmp = 0xFFFFFFFF; + f_updateCRC( pui32CRCTbl, 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( pui32CRCTbl) + { + f_freeCRCTable( &pui32CRCTbl); + } + + if( pucUsedMap) + { + f_free( &pucUsedMap); + } + + if( pucData) + { + f_free( &pucData); + } + + if( pucBinPacket) + { + f_free( &pucBinPacket); + } + + return( rc); +} + +/**************************************************************************** +Desc: Used by flmGenerateHexPacket to find an unused byte in the packet +*****************************************************************************/ +FLMBOOL flmGetNextHexPacketSlot( + FLMBYTE * pucUsedMap, + FLMUINT uiMapSize, + f_randomGenerator * pRandGen, + FLMUINT * puiSlot) +{ + FLMUINT uiLoop; + FLMUINT uiSlot = 0; + FLMBOOL bFound = FALSE; + + for( uiLoop = 0; uiLoop < 100; uiLoop++) + { + uiSlot = ((FLMUINT)f_randomLong( pRandGen)) % 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, + f_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( pszSrc[ 0]) && f_isHexChar( pszSrc[ 1])) + { + *pszDest = (f_getHexVal( pszSrc[ 0]) << 4) | + f_getHexVal( 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) +{ + GedPoolInit( &m_pool, 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; + } + + GedPoolFree( &m_pool); +} + +/**************************************************************************** +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); + GedPoolReset( m_pPool, NULL); + 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; + + /* + Read the opcode. + */ + + 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) +{ +#define RECORD_OUTPUT_BUFFER_SIZE 64 + FLMBYTE pucBuffer[ RECORD_OUTPUT_BUFFER_SIZE]; + FLMBYTE * pucBufPos; + FLMBYTE ucDescriptor; + RCODE rc = FERR_OK; + + // 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 + + flmUINT32ToBigEndian( pRecord->getContainerID(), pucBufPos); + pucBufPos += 4; + + flmUINT32ToBigEndian( 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( uiItemCount, pucItemPos); + pucItemPos += 4; + + // Put the items into the buffer. + + for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++) + { + UD2FBA( 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( + 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 = GedPoolMark( m_pPool); + 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: + + GedPoolReset( m_pPool, pvMark); + return( rc); +} + +/**************************************************************************** +Desc: Sends a value to the client +*****************************************************************************/ +RCODE FCS_WIRE::sendNameTable( + FLMUINT uiTag, + F_NameTable * pNameTable) +{ + void * pvMark = GedPoolMark( m_pPool); + 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( (puzItemName = (FLMUNICODE *)GedPoolAlloc( m_pPool, + uiMaxNameChars * sizeof( FLMUNICODE))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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: + + GedPoolReset( m_pPool, 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 = GedPoolMark( m_pPool); + 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; + } + + GedPoolReset( m_pPool, 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 = GedPoolMark( m_pPool); + + // 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: + + GedPoolReset( m_pPool, pPoolMark); + return( rc); +} + +/**************************************************************************** +Desc: Receives a name table. +*****************************************************************************/ +RCODE FCS_WIRE::receiveNameTable( + F_NameTable ** ppNameTable) +{ + NODE * pRootNd; + NODE * pItemIdNd; + NODE * pNd = NULL; + void * pvMark = GedPoolMark( m_pPool); + 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( (puzItemName = (FLMUNICODE *)GedPoolAlloc( m_pPool, + uiMaxNameChars * sizeof( FLMUNICODE))) == NULL) + { + rc = RC_SET( FERR_MEM); + 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(); + } + + GedPoolReset( m_pPool, 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); +} diff --git a/flaim/src/fdict.h b/flaim/src/fdict.h new file mode 100644 index 0000000..efc10c3 --- /dev/null +++ b/flaim/src/fdict.h @@ -0,0 +1,449 @@ +//------------------------------------------------------------------------- +// Desc: Typedefs for strucures needed to build pcode. +// Tabs: 3 +// +// Copyright (c) 1991-1992,1995-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$ +//------------------------------------------------------------------------- + +#ifndef FDICT_H +#define FDICT_H + +#include "fpackon.h" +// IMPORTANT NOTE: No other include files should follow this one except +// for fpackoff.h + +// Logical File Save Area Layout for 4.x files. + +#define LFH_LF_NUMBER_OFFSET 0 // Logical file number +#define LFH_TYPE_OFFSET 2 // Type of logical file +#define LFH_STATUS_OFFSET 3 // Contains status bits +#define LFH_ROOT_BLK_OFFSET 4 // B-TREE root block address +//#define LFH_FUTURE1 8 // Not necessarily zeroes - Code bases + // 31 and 40 put stuff here. +#define LFH_NEXT_DRN_OFFSET 12 // Next DRN for containers +#define LFH_MAX_FILL_OFFSET 16 // Max fill % after rightmost split. +#define LFH_MIN_FILL_OFFSET 17 // Min fill % in blk after normal delete +//#define LFH_FUTURE2 18 // Filled with zeros +#define LFH_SIZE 32 // Maximum size of LFH. + +#define FFILE_MIN_FILL 35 +#define FFILE_MAX_FILL 91 + +struct TDICT; +struct LFILE; + +RCODE fdictRebuild( + FDB * pDb); + +RCODE fdictBuildTables( + TDICT * pTDict, + FLMBOOL bRereadLFiles, + FLMBOOL bNewDict); + +RCODE fdictInitTDict( + FDB * pDb, + TDICT * pTDict); + +RCODE fdictCopySkeletonDict( + FDB * pDb); + +RCODE fdictCloneDict( + FDB * pDb); + +RCODE fdictFixupLFileTbl( + FDICT * pDict); + +RCODE fdictProcessAllDictRecs( + FDB * pDb, + TDICT * pTDict); + +RCODE fdictProcessRec( + TDICT * pTDict, + FlmRecord * pRecord, + FLMUINT uiDictRecNum); + +RCODE DDGetFieldType( + FlmRecord * pRecord, + void * pvField, + FLMUINT * puiFldInfo); + +RCODE DDGetEncType( + FlmRecord * pRecord, + void * pvField, + FLMUINT * puiFldInfo); + +RCODE fdictCreateNewDict( + FDB * pDb); + +RCODE fdictCreate( + FDB * pDb, + const char * pszDictPath, + const char * pDictBuf); + +RCODE flmAddRecordToDict( + FDB * pDb, + FlmRecord * pRecord, + FLMUINT uiDictId, + FLMBOOL bRereadLFiles); + +/**************************************************************************** +Desc: Structure for type, DRN and name for data dictionary entries +****************************************************************************/ +typedef struct DDENTRY +{ + DDENTRY * pNextEntry; + void * vpDef; + FLMUINT uiEntryNum; + FLMUINT uiType; +} DDENTRY; + +/**************************************************************************** +Desc: Temporary field info used during a database create or dictionary + modification. This field is pointed to by the DDEntry structure. +****************************************************************************/ +typedef struct TFIELD +{ + FLMUINT uiFldNum; + FLMUINT uiFldInfo; +} TFIELD; + +/**************************************************************************** +Desc: Temporary encryption definition info used during a database create or + dictionary modification. This field is pointed to by the + DDEntry structure. +****************************************************************************/ +typedef struct TENCDEF +{ + FLMUINT uiRecNum; + FLMUINT uiState; + FLMUINT uiAlgType; + FLMBYTE * pucKeyInfo; + FLMUINT uiLength; +} TENCDEF; + +/**************************************************************************** +Desc: Used as temporary storage for index definitions during a + database create or dictionary modification. This field is + pointed to by the DDEntry structure. +****************************************************************************/ +typedef struct TIFP +{ + TIFP * pNextTIfp; // Linked list of IFPs + FLMBOOL bFieldInThisDict; // Was field reference found in the + // dictionary we are updating? + FLMUINT uiFldNum; // Fixedup field ID value +} TIFP; + +/**************************************************************************** +Desc: Used as temporary storage for index definitions during a + database create or dictionary modification. This field is + pointed to by the DDEntry structure. +****************************************************************************/ +typedef struct TIFD +{ + TIFP * pTIfp; // Linked list of temporary field paths + TIFD * pNextTIfd; // Linked List + FLMUINT uiFlags; // Field type & processing flags + FLMUINT uiNextFixupPos; // Next fixup position + FLMUINT uiLimit; // Zero or limit of characters/bytes + FLMUINT uiCompoundPos; // Position of this field is in + // the compound key. Zero based number. +} TIFD; + +/**************************************************************************** +Desc: Used as temporary storage for index definitions during a + database create or dictionary modification. This field is + pointed to by the DDEntry structure. +****************************************************************************/ +typedef struct TIXD +{ + TIFD * pNextTIfd; // Linked list of TIFDs + FLMUINT uiFlags; // Index attributes + FLMUINT uiContainerNum; // Container number of data records + FLMUINT uiNumFlds; // Number of field definitions + FLMUINT uiLanguage; // Index language + FLMUINT uiEncId; // Encryption Definition +} TIXD; + +/**************************************************************************** +Desc: Contains the dictionary entries through parsing all of the dictionary + records. Used for expanding record definitions, checking index + definitions, building fixup position values and last of all + BUILDING THE PCODE. +****************************************************************************/ +typedef struct TDICT +{ + FDB * pDb; + POOL pool; // Pool for the DDENTRY allocations. + LFILE * pLFile; // Dictionary container LFile + FDICT * pDict; // Pointer to new dictionary. + FLMBOOL bWriteToDisk; // Flag indicating if PCODE should be + // written to disk after being generated. + + // Variables for building dictionaries + + FLMUINT uiCurPcodeAddr; // Current pcode block we are adding to + FLMUINT uiBlockSize; // PCODE Block size + + // Used in building the temporary structures + + FLMUINT uiVersionNum; // Version number of database. + DDENTRY * pFirstEntry; + DDENTRY * pLastEntry; + + FLMUINT uiNewIxds; + FLMUINT uiNewIfds; + FLMUINT uiNewFldPaths; + FLMUINT uiNewLFiles; + + FLMUINT uiTotalItts; + FLMUINT uiTotalIxds; + FLMUINT uiTotalIfds; + FLMUINT uiTotalFldPaths; + FLMUINT uiTotalLFiles; + + FLMUINT uiBadField; // Set to field number on most errors. + FLMUINT uiBadReference; // Same + + FLMUINT uiDefaultLanguage;// Default language to set in each index. +} TDICT; + +/**************************************************************************** +Desc: A Item Type consists of a byte that describes the type of item + like a field, index or container. + For fields a ITT will also indicate the fields delete status. +****************************************************************************/ +typedef struct ITT +{ + FLMUINT uiType; + void * pvItem; // Points to LFILE if index or container + // If field, is NULL or points to first IFD. +} ITT; + +// Bit values for uiType. The 4 low bits contain the field type. +// See FLM_XXXX_TYPE in FLAIM.H for lower four bits. + +#define ITT_FLD_GET_TYPE( pItt) (((pItt)->uiType) & 0x0F) +#define ITT_FLD_IS_INDEXED( pItt) (((pItt)->pvItem) ? TRUE : FALSE) +#define ITT_FLD_GET_STATE( pItt) (((pItt)->uiType) & 0x30) + +#define ITT_FLD_STATE_MASK 0x30 +#define ITT_FLD_STATE_ACTIVE 0x00 // Normal active field +#define ITT_FLD_STATE_CHECKING 0x10 // Field has been marked to be checked +#define ITT_FLD_STATE_UNUSED 0x30 // Field is not used. +#define ITT_FLD_STATE_PURGE 0x20 // Purge this field from the database. + // And delete the dictionary definition + +#define ITT_ENC_STATE_MASK 0x30 +#define ITT_ENC_STATE_ACTIVE 0x00 // Normal active field +#define ITT_ENC_STATE_CHECKING 0x10 // EncDef has been marked to be checked +#define ITT_ENC_STATE_UNUSED 0x30 // EncDef is not used. +#define ITT_ENC_STATE_PURGE 0x20 // EncDef record is being deleted. Decrypt the + // encrypted field as it can no longer be + // encrypted. + +#define ITT_ENCDEF_TYPE 0xAF // Encrypted Definition Record +#define ITT_INDEX_TYPE 0xBF +#define ITT_CONTAINER_TYPE 0xCF +#define ITT_EMPTY_SLOT 0xEF +#define ITT_INFO_MASK 0x0F + +#define ITT_IS_FIELD(pItt) (((pItt)->uiType & ITT_INFO_MASK) != ITT_INFO_MASK) +#define ITT_IS_CONTAINER(pItt) ((pItt)->uiType == ITT_CONTAINER_TYPE) +#define ITT_IS_INDEX(pItt) ((pItt)->uiType == ITT_INDEX_TYPE) +#define ITT_IS_ENCDEF(pItt) ((pItt)->uiType == ITT_ENCDEF_TYPE) + +/**************************************************************************** +Desc: This structure holds the information for an index definition. + There may be multiple IXDs for the same index number. +****************************************************************************/ +typedef struct IXD +{ + FLMUINT uiIndexNum; // Index number. + FLMUINT uiContainerNum; // Container number being indexed. + IFD * pFirstIfd; // Points to first IFD + FLMUINT uiNumFlds; // Number of index fields in the IFD. + FLMUINT uiFlags; + #define IXD_UNIQUE 0x00001 // Unique index + #define IXD_COUNT 0x00002 // Count keys and references + #define IXD_EACHWORD 0x00100 // FUTURE: FLAIMs fulltext indexing. + #define IXD_HAS_POST 0x01000 // Has post keys parts. + #define IXD_HAS_SUBSTRING 0x02000 + #define IXD_POSITIONING 0x04000 // The index has positioning counts. + #define IXD_OFFLINE 0x08000 + #define IXD_SUSPENDED 0x10000 + + FLMUINT uiLanguage; // WP.LRS language number (not code!) + #define US_LANG 0 + #define DEFAULT_LANG US_LANG + +#define TRANS_ID_OFFLINE TRANS_ID_HIGH_VALUE +#define TRANS_ID_ALWAYS_ONLINE TRANS_ID_LOW_VALUE + + FLMUINT uiLastContainerIndexed; // Last container indexed if index + // covers multiple containers. + FLMUINT uiLastDrnIndexed; // If value is not DRN_LAST_MARKER then + // update index with keys from a record + // update if drn of record is <= of + // this value. + FLMUINT uiEncId; // The ID / Drn of the Encryption record (if used) +} IXD; + +/**************************************************************************** +Desc: This structure contains an index field definition. +****************************************************************************/ +typedef struct IFD +{ + FLMUINT uiFldNum; // Field being indexed. + FLMUINT uiIndexNum; // Index number. + IXD * pIxd; // IXD corresponding to wIndexNum + FLMUINT uiFlags; // The first 4 bits contain field type + // Use FLM_XXXXX_TYPE definitions. + + IFD * pNextInChain; // Next IFD in the chain that has this + // field number and is used in another index. + FLMUINT * pFieldPathCToP; // Child to parent field path (zero term) + FLMUINT * pFieldPathPToC; // Parent to child field path (zero term) + + FLMUINT uiLimit; // Zero or # of characters/bytes to limit. +#define IFD_DEFAULT_LIMIT 256 +#define IFD_DEFAULT_SUBSTRING_LIMIT 48 + + FLMUINT uiCompoundPos; // Position of this field is in + // the compound key. Zero based number. +} IFD; + +#define IFD_GET_FIELD_TYPE(pIfd) ((pIfd)->uiFlags & 0x0F) +#define IFD_SET_FIELD_TYPE(pIfd,type) ((pIfd)->uiFlags = ((pIfd)->uiFlags & 0xFFFFFFF0) | (type)) +#define IFD_FIELD 0x00000010 // There must always be some value +#define IFD_VALUE 0x00000010 // Value agrees with parsing syntax + +#define IFD_EACHWORD 0x00000020 // Index each and every word in the field +#define IFD_CONTEXT 0x00000040 // Index the tag and NOT the value +#define IFD_COMPOUND 0x00000080 // Index multiple fields + +#define IFD_POST 0x00000100 // Place case info at end of compound key +#define IFD_UPPER 0x00000200 // Uppercase keys only +#define IFD_OPTIONAL 0x00000400 // This field is optional (compound) + // Phasing this value out. + +// Note: the unique flag is for future compatiblity. + +#define IFD_UNIQUE_PIECE 0x00000800 // Better name + +#define IFD_REQUIRED_PIECE 0x00001000 // Required piece (not optional) +#define IFD_REQUIRED_IN_SET 0x0002000 // Required within a set of fields. + +#define IFD_LAST 0x00008000 // Last IFD for this index definition + +#define IFD_SUBSTRING 0x00040000 // Index all substrings pieces +#define IFD_DRN 0x00080000 // index DRN value +#define IFD_FIELDID_PAIR 0x00200000 // Data | fieldID pair. +#define IFD_MIN_SPACES 0x00400000 // Removed leading/trailing spaces. + // Combine multiple spaces into 1 space. + // Minimize spaces +#define IFD_NO_SPACE 0x00800000 // Remove all spaces +#define IFD_NO_DASH 0x01000000 // Remove all dashes +#define IFD_NO_UNDERSCORE 0x02000000 // Change underscores to spaces, + // Must be applied before nospace/minspace +#define IFD_ESC_CHAR 0x04000000 // Placehold so that a query can parse the input + // string and find a literal '*' or '\\'. + +#define IFD_IS_POST_TEXT(pIfd) (((pIfd)->uiFlags & IFD_POST) && \ + (IFD_GET_FIELD_TYPE(pIfd) == FLM_TEXT_TYPE)) +#define IFD_DEFAULT_LIMIT 256 +#define IFD_DEFAULT_SUBSTRING_LIMIT 48 + +/************************************************************************** +Desc: This structure is a header for a FLAIM dictionary. All of + the information in this structure is static. +**************************************************************************/ +typedef struct FDICT +{ + FDICT * pNext; // Pointer to next FDICT structure in the list, + // if any. All versions of a dictionary that + // are currently in use are linked together. + // Usually, there will be only one local + // dictionary in the list. + FDICT * pPrev; // Previous FDICT structure in the list. + FFILE * pFile; // File this dictionary is associated with. + // A null value means it is not yet linked + // to a file. + FLMUINT uiDictSeq; // This is the sequence number of the dictionary + + // Local Dictionary Tables. + + LFILE * pLFileTbl; // Logical file (index or container) + FLMUINT uiLFileCnt; +#define LFILE_DATA_CONTAINER_OFFSET 0 +#define LFILE_DICT_CONTAINER_OFFSET 1 +#define LFILE_DICT_INDEX_OFFSET 2 +#define LFILE_TRACKER_CONTAINER_OFFSET 3 + + ITT * pIttTbl; + FLMUINT uiIttCnt; + + IXD * pIxdTbl; + FLMUINT uiIxdCnt; + + IFD * pIfdTbl; + FLMUINT uiIfdCnt; + + FLMUINT * pFldPathsTbl; + FLMUINT uiFldPathsCnt; + + FLMUINT uiUseCount; // Number of FDB structures currently + // pointing to this dictionary. +} FDICT; + +/**************************************************************************** +Desc: This is a temporary structure that is used when building compound + keys. +****************************************************************************/ +typedef struct CDL +{ + void * pField; // Field to be included in a compound key + void * pRootContext; // Points to root context of field path + CDL * pNext; // Pointer to the next CDL entry. +} CDL; + +/**************************************************************************** +Desc: This keeps track of the logical file information for an index or + a container. +****************************************************************************/ +typedef struct LFILE +{ + FLMUINT uiRootBlk; // Address of root block. + FLMUINT uiNextDrn; // Next DRN - only use when root is null + FLMUINT uiBlkAddress; // Block address of LFile entry. + FLMUINT uiOffsetInBlk; // Offset within block of entry. + FLMUINT uiLfNum; // Index number or container number. + FLMUINT uiLfType; // Type of logical file. */ + FLMBOOL bMakeFieldIdTable; // Boolean that indicates whether or not + // for this container when we create + // records in cache we should create a + // field id table for the level-1 fields. + IXD * pIxd; // If an index, points to the IXD. + +} LFILE; + +#include "fpackoff.h" + +#endif diff --git a/flaim/src/fgedcom.cpp b/flaim/src/fgedcom.cpp new file mode 100644 index 0000000..0c40f47 --- /dev/null +++ b/flaim/src/fgedcom.cpp @@ -0,0 +1,3234 @@ +//------------------------------------------------------------------------- +// Desc: GEDCOM routines +// Tabs: 3 +// +// Copyright (c) 1990-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$ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +extern FLMBYTE arr[]; +extern FLMBYTE ucMaxBcdINT32[]; +extern FLMBYTE ucMinBcdINT32[]; +extern FLMBYTE ucMaxBcdUINT32[]; + +#define BINARY_GED_HEADER_LEN 8 + +static FLMBYTE FlmBinaryGedHeader[BINARY_GED_HEADER_LEN] = +{ + 0xFF, + 'F', + 'L', + 'M', + 'D', + 'I', + 'C', + 'T' +}; + +static FLMBYTE FlmBinaryRecHeader[BINARY_GED_HEADER_LEN] = +{ + 0xFF, + 'F', + 'L', + 'M', + 'R', + 'E', + 'C', + 'S' +}; + +#define NODE_DRN_POS 0 +#define NODE_CONTAINER_POS ( NODE_DRN_POS + sizeof(FLMUINT)) +#define NODE_DB_POS ( NODE_CONTAINER_POS + sizeof(FLMUINT)) + +#define f_isdigit(c) \ + ((c) < 60 ? ((((FLMBYTE) (arr[(c) >> 3])) << ((c) & 0x07)) & 0x80) : 0) + +FSTATIC RCODE expWrite( + EXP_IMP_INFO * pExpImpInfo, + const FLMBYTE * pData, + FLMUINT uiDataLen); + +FSTATIC RCODE impRead( + EXP_IMP_INFO * pExpImpInfo, + FLMBYTE * pData, + FLMUINT uiDataLen, + FLMUINT * puiBytesReadRV); + +FSTATIC RCODE tagValLenType( + POOL * pPool, + GED_STREAM * x, + NODE ** ppNode, + F_NameTable * pNameTable); + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetBINARY( + NODE * pNode, + void * buffer, + FLMUINT * bufLenRV) +{ + RCODE rc = FERR_OK; + FLMBYTE * ptr; + FLMUINT valLength; + FLMUINT outputData; + FLMUINT uiNodeType; + + // Check for a null node + + if (!pNode) + { + rc = RC_SET( FERR_CONV_NULL_SRC); + goto Exit; + } + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + // If the node is not a BINARY node, return an error + + uiNodeType = GedValType( pNode); + if (uiNodeType != FLM_BINARY_TYPE) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + ptr = (FLMBYTE *) GedValPtr( pNode); + valLength = GedValLen( pNode); + + // At this point we know the node is a BINARY node + + outputData = ((buffer != NULL) && (*bufLenRV)); + if ((outputData) && (valLength)) + { + if (valLength > *bufLenRV) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + f_memcpy( buffer, ptr, valLength); + } + + *bufLenRV = valLength; + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutBINARY( + POOL * pPool, + NODE * pNode, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + RCODE rc = FERR_OK; + FLMBYTE * outPtr; + + // Check for a null node being passed in + + if (!pNode) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + // If data pointer is NULL or length is zero, call GedAllocSpace with + // a length of zero to set the node length to zero and node type to + // FLM_BINARY_TYPE. + + if (pvData == NULL || !uiDataLen) + { + (void) GedAllocSpace( pPool, pNode, FLM_BINARY_TYPE, + 0, uiEncId, uiEncSize); + goto Exit; + } + + // Allocate space in the node for the binary data + + if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_BINARY_TYPE, + uiDataLen, uiEncId, uiEncSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Set the node type and copy the data into the node + + f_memcpy( outPtr, pvData, uiDataLen); + + if (pNode->ui32EncId) + { + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedToTree( + POOL * pPool, + F_FileHdl * pFileHdl, + char ** pBuf, + FLMUINT uiBufSize, + NODE ** ppRoot, + F_NameTable * pNameTable) +{ + RCODE rc = FERR_OK; + GED_STREAM gedStream; + FLMUINT level; + FLMUINT levelBase = 0; + FLMUINT levelPrior = 0; + FLMBYTE nextChar; + NODE * nd = NULL; + NODE * ndPrior = NULL; + FLMUINT startPos; + + gedStream.pFileHdl = pFileHdl; + gedStream.pThis = gedStream.pBuf = *pBuf; + gedStream.uiBufSize = uiBufSize; + + if (pFileHdl) + { + + // Find 1st starting file position + + if (RC_OK( pFileHdl->Seek( 0L, F_IO_SEEK_CUR, &gedStream.uiFilePos))) + { + gedStream.pLast = gedStream.pBuf; + gedReadChar( &gedStream, gedStream.uiFilePos); + } + else + { + return (RC_SET( FERR_FILE_ER)); + } + } + else + { + gedStream.errorIO = 0; + gedStream.uiFilePos = 0; + gedStream.pLast = gedStream.pBuf + (uiBufSize - 1); + gedStream.thisC = f_toascii( *gedStream.pBuf); + } + + for (;;) + { + gedSkipBlankLines( &gedStream); + startPos = gedStream.uiFilePos; + + if (f_isdigit( gedStream.thisC)) + { + level = 0; + do + { + level = gedStream.thisC - ASCII_ZERO + (level * 10); + nextChar = (FLMBYTE) (gedNextChar( &gedStream)); + } while (f_isdigit( nextChar)); + + if (!f_iswhitespace( gedStream.thisC)) + { + rc = RC_SET( FERR_BAD_FIELD_LEVEL); + break; + } + + if (level > GED_MAXLVLNUM) + { + rc = RC_SET( FERR_GED_MAXLVLNUM); + break; + } + + if (ndPrior) + { + if (levelBase >= level) + { + goto successful; + } + else if ((levelPrior < level) && ((levelPrior + 1) != level)) + { + rc = RC_SET( FERR_GED_SKIP_LEVEL); + break; + } + } + else + { + levelBase = level; + } + + levelPrior = level; + + if( RC_OK( rc = tagValLenType( pPool, &gedStream, &nd, pNameTable))) + { + if (ndPrior) + { + ndPrior->next = nd; + } + else + { + *ppRoot = nd; + } + + nd->prior = ndPrior; + GedNodeLevelSet( nd, level - levelBase); + ndPrior = nd; + continue; + } + } + else if (gedStream.thisC == '\0' || gedStream.thisC == ASCII_CTRLZ) + { + if (gedStream.errorIO) + { + rc = RC_SET( FERR_FILE_ER); + } + else if (ndPrior) + { +successful: + + ndPrior->next = NULL; + if (pFileHdl == NULL) + { + *pBuf = gedStream.pThis + + (FLMINT32) (startPos - gedStream.uiFilePos); + } + + gedStream.uiFilePos = startPos; + rc = FERR_OK; + } + else + { + rc = RC_SET( FERR_END); + } + } + else + { + rc = RC_SET( FERR_BAD_FIELD_LEVEL); + } + + break; + } + + if (RC_BAD( rc)) + { + *ppRoot = NULL; + + if (pFileHdl == NULL) + { + *pBuf = gedStream.pThis; + } + } + + if (pFileHdl) + { + pFileHdl->Seek( gedStream.uiFilePos, F_IO_SEEK_SET, &gedStream.uiFilePos); + } + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE tagValLenType( + POOL * pPool, + GED_STREAM * pGedStream, + NODE ** newNode, + F_NameTable * pNameTable) +{ + FLMUINT startPos; + RCODE rc = FERR_OK; + NODE * nd; + FLMUINT drn = 0; + FLMUINT uiTagNum; + char tagBuf[ GED_MAXTAGLEN + 1]; + + gedSkipWhiteSpaces( pGedStream); + + startPos = pGedStream->uiFilePos; + if (pGedStream->thisC == ASCII_AT) + { + int badDRN; + + for (badDRN = 0, gedNextChar( pGedStream); + pGedStream->thisC != ASCII_AT; + gedNextChar( pGedStream)) + { + FLMUINT priorDrn = drn; + + if (!badDRN) + { + if (f_isdigit( pGedStream->thisC)) + { + drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO; + badDRN = priorDrn != (drn / 10); + } + else + { + badDRN = 1; + } + } + } + + if (badDRN) + { + drn = 0; + } + + gedNextChar( pGedStream); + if (f_iswhitespace( pGedStream->thisC)) + { + gedSkipWhiteSpaces( pGedStream); + } + else + { + rc = RC_SET( FERR_GED_BAD_RECID); + goto Exit; + } + } + + // Determine the Tag Number and Build the NODE + + startPos = pGedStream->uiFilePos; + + if (!gedCopyTag( pGedStream, tagBuf)) + { + return (RC_SET( FERR_INVALID_TAG)); + } + + if (!pNameTable->getFromTagTypeAndName( NULL, tagBuf, FLM_FIELD_TAG, + &uiTagNum)) + { + + // See if tag is the reserved tag with the number following + + if (tagBuf[0] == f_toascii( 'T') && + tagBuf[1] == f_toascii( 'A') && + tagBuf[2] == f_toascii( 'G') && + tagBuf[3] == f_toascii( '_')) + { + uiTagNum = f_atoi( &tagBuf[4]); + } + else + { + return (RC_SET( FERR_NOT_FOUND)); + } + } + + if ((*newNode = nd = GedNodeCreate( pPool, uiTagNum, drn, &rc)) == NULL) + { + goto Exit; + } + + gedSkipWhiteSpaces( pGedStream); + + startPos = pGedStream->uiFilePos; + if (pGedStream->thisC == ASCII_AT) + { + for (drn = 0; gedNextChar( pGedStream) != ASCII_AT;) + { + FLMUINT priorDrn = drn; + + if (f_isdigit( pGedStream->thisC)) + { + drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO; + if (priorDrn == (drn / 10)) + { + continue; + } + } + + rc = RC_SET( FERR_GED_BAD_VALUE); + goto Exit; + } + + gedNextChar( pGedStream); + GedPutRecPtr( pPool, nd, drn); + if (gedCopyValue( pGedStream, NULL)) + { + rc = RC_SET( FERR_GED_BAD_VALUE); + goto Exit; + } + } + else + { + FLMINT valLength; + FLMUINT tempPos = pGedStream->uiFilePos; + + if ((valLength = gedCopyValue( pGedStream, NULL)) > 0) + { + char * vp = (char *) GedAllocSpace( pPool, nd, FLM_TEXT_TYPE, + valLength); + + if (vp) + { + gedReadChar( pGedStream, tempPos); + gedCopyValue( pGedStream, vp); + } + else + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + } + + startPos = pGedStream->uiFilePos; + +Exit: + + gedReadChar( pGedStream, startPos); + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedNodeCopy( + POOL * pPool, + NODE * pNode, + NODE * childList, + NODE * sibList) +{ + NODE * newNd; + FLMUINT bias; + FLMBYTE * vp; + RCODE rc; + HFDB hDb; + FLMUINT uiContainer; + FLMUINT uiRecId; + + // If the node has source information, we need to copy it + + if (RC_OK( GedGetRecSource( pNode, &hDb, &uiContainer, &uiRecId))) + { + + // The passed in node contains record source information, so create + // a GEDCOM record source node + + if (RC_BAD( gedCreateSourceNode( pPool, GedTagNum( pNode), hDb, + uiContainer, uiRecId, &newNd))) + { + return (NULL); + } + } + else + { + + // Create a normal (non-source) GEDCOM node + + if ((newNd = GedNodeMake( pPool, GedTagNum( pNode), &rc)) == NULL) + { + return (NULL); + } + } + + newNd->prior = NULL; + newNd->next = childList; + GedNodeLevelSet( newNd, 0); + + if ((vp = (FLMBYTE *) GedAllocSpace( pPool, newNd, GedValType( pNode), + GedValLen( pNode), pNode->ui32EncId, GedEncLen( pNode))) != NULL) + { + f_memcpy( vp, GedValPtr( pNode), GedValLen( pNode)); + + if (pNode->ui32EncFlags & FLD_HAVE_ENCRYPTED_DATA) + { + f_memcpy( GedEncPtr( newNd), GedEncPtr( pNode), GedEncLen( pNode)); + } + + newNd->ui32EncFlags = pNode->ui32EncFlags; + } + else + { + return (NULL); + } + + if (childList) + { + childList->prior = newNd; + for (bias = GedNodeLevel( childList) - 1; + childList->next; + GedNodeLevelSub( childList, bias), childList = childList->next); + + GedNodeLevelSub( childList, bias); + childList->next = sibList; + } + else + { + childList = newNd; + } + + if (sibList) + { + sibList->prior = childList; + childList->next = sibList; + for (bias = GedNodeLevel( sibList); sibList->next; + GedNodeLevelSub( sibList, bias), sibList = sibList->next); + GedNodeLevelSub( sibList, bias); + } + + return (newNd); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedCopy( + POOL * pPool, + FLMUINT cnt, + NODE * tree) +{ + NODE * oldNd; + NODE * newNd; + NODE * newRoot; + FLMUINT baseLevel; + + if (tree) + { + newRoot = newNd = GedNodeCopy( pPool, tree, NULL, NULL); + if (newRoot) + { + for (baseLevel = GedNodeLevel( tree); + (tree = tree->next) != NULL && (GedNodeLevel( tree) > baseLevel || + (GedNodeLevel( tree) == baseLevel && --cnt));) + { + oldNd = newNd; + if ((newNd = GedNodeCopy( pPool, tree, NULL, NULL)) != NULL) + { + oldNd->next = newNd; + newNd->prior = oldNd; + GedNodeLevelSet( newNd, GedNodeLevel( tree) - baseLevel); + } + else + { + return (NULL); + } + } + } + + return (newRoot); + } + + return (NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedClip( + FLMUINT treeCnt, + NODE * self) +{ + NODE * next; + + if (self) + { + FLMUINT oldLevel = GedNodeLevel( self); + + GedNodeLevelSet( self, 0); + + for (next = self->next; next && + (GedNodeLevel( next) > oldLevel || + (GedNodeLevel( next) == oldLevel && --treeCnt)); + GedNodeLevelSub( next, oldLevel), next = next->next) + { + ; + } + + if (self->prior) + { + self->prior->next = next; + } + + if (next) + { + next->prior->next = NULL; + next->prior = self->prior; + } + + self->prior = NULL; + } + + return (self); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedFind( + FLMUINT treeCnt, + NODE * nd, + FLMUINT tnum, + FLMINT nth) +{ + if (nd) + { + FLMUINT strtLvl = GedNodeLevel( nd); + do + { + if ((tnum == GedTagNum( nd)) && (--nth < 1)) + { + return (nd); + } + } while( (nd = nd->next) != NULL && + (GedNodeLevel( nd) > strtLvl || + (--treeCnt && GedNodeLevel( nd) == strtLvl))); + } + + return (NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedPathFind( + FLMUINT treeCnt, + NODE * nd, + FLMUINT * puiPathArray, + FLMINT nth) +{ + NODE * pNode = nd; + NODE * savenode; + FLMUINT * path; + + if (nd && puiPathArray) + { + FLMUINT uiLevel = GedNodeLevel( nd); + + for (;;) + { + path = puiPathArray + (GedNodeLevel( pNode) - uiLevel); + savenode = pNode; + if (*path == GedTagNum( pNode)) + { + if (*(path + 1) == 0 && (--nth < 1)) + { + return (pNode); + } + + if ((pNode = GedChild( pNode)) != NULL) + { + continue; + } + + pNode = savenode; + } + + do + { + pNode = pNode->next; + } while (pNode != NULL && GedNodeLevel( pNode) > GedNodeLevel( savenode)); + + // find next sibling/uncle/end + + if (!pNode || GedNodeLevel( pNode) < uiLevel || + (GedNodeLevel( pNode) == uiLevel && !(--treeCnt))) + { + break; + } + } + } + + return (NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedChildGraft( + NODE * parent, + NODE * child, + FLMINT nth) +{ + NODE * lastChildNode; + + if (parent && child) + { + FLMINT level = GedNodeLevel( parent) + 1; + + if (GedChild( parent)) + { + GedSibGraft( GedChild( parent), child, + (FLMINT) (nth == GED_FIRST ? GED_FIRST : nth - 1)); + } + else + { + for (lastChildNode = child; + lastChildNode->next; + GedNodeLevelAdd( lastChildNode, level), + lastChildNode = lastChildNode->next); + + child->prior = parent; + GedNodeLevelAdd( lastChildNode, level); + lastChildNode->next = parent->next; + if (parent->next) + { + parent->next->prior = lastChildNode; + } + + parent->next = child; + } + } + + return (parent); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedSibGraft( + NODE * self, + NODE * sib, + FLMINT nth) +{ + NODE * lastSibNode; + NODE * returnNode; + FLMINT deltaLevel; + FLMUINT level; + FLMUINT linkAt = TRUE; + + if (!sib) + { + return (self); + } + + if (!self) + { + return (sib); + } + + for( level = GedNodeLevel( self), + deltaLevel = (FLMINT) (level - GedNodeLevel( sib)), + lastSibNode = sib; lastSibNode->next; + GedNodeLevelAdd( lastSibNode, deltaLevel), + lastSibNode = lastSibNode->next); + + GedNodeLevelAdd( lastSibNode, deltaLevel); + if (nth != GED_LAST) + { + nth++; + } + + if (nth <= 0) + { + returnNode = sib; + while (nth) + { + if (self->prior) + { + self = self->prior; + if (GedNodeLevel( self) > level) + { + continue; + } + else if (GedNodeLevel( self) == level) + { + nth++; + continue; + } + + self = self->next; + } + break; + } + } + else + { + returnNode = self; + while (nth) + { + if (self->next) + { + self = self->next; + if (GedNodeLevel( self) > level) + { + continue; + } + else if (GedNodeLevel( self) == level) + { + nth--; + continue; + } + + self = self->prior; + } + + linkAt = FALSE; + break; + } + } + + if (linkAt) + { + + // Link the sib tree AT the current self location - link before self + + sib->prior = self->prior; + lastSibNode->next = self; + if (self->prior) + { + self->prior->next = sib; + } + + self->prior = lastSibNode; + } + else + { + + // Link the sib tree AFTER the current self location + + sib->prior = self; + lastSibNode->next = self->next; + if (self->next) + { + self->next->prior = lastSibNode; + } + + self->next = sib; + } + + return (returnNode); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedNodeCreate( + POOL * pPool, + FLMUINT tagNum, + FLMUINT id, + RCODE * rc) +{ + NODE * nd; + + if ((nd = (NODE *) GedPoolAlloc( pPool, (sizeof(NODE) + + (id ? sizeof(id) : 0)))) == NULL) + { + *rc = RC_SET( FERR_MEM); + } + else + { + f_memset( nd, '\0', sizeof(NODE)); + + GedValTypeSet( nd, FLM_CONTEXT_TYPE); + GedTagNumSet( nd, tagNum); + + if (id) + { + FLMBYTE * ptr; + GedValTypeSetFlag( nd, HAS_REC_ID); + ptr = ((FLMBYTE *) nd) + sizeof(NODE); + *((FLMUINT *) (ptr + NODE_DRN_POS)) = id; + } + + *rc = FERR_OK; + } + + return (nd); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +void * GedAllocSpace( + POOL * pPool, + NODE * pNode, + FLMUINT valType, + FLMUINT size, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + FLMBYTE * rPtr; + FLMUINT uiAllocSize = size; + + if (valType == FLM_TEXT_TYPE) + { + uiAllocSize++; + } + + if (uiAllocSize <= sizeof(void *)) + { + + // If the size is less than sizeof (void *), we use the space right + // inside value pointer itself. + + rPtr = (FLMBYTE *) &pNode->value; + } + else if (size <= GedValLen( pNode)) + { + + // If there is already allocated space, just re-use it + + rPtr = (FLMBYTE *) GedValPtr( pNode); + } + else + { + if ((pNode->value = rPtr = (FLMBYTE *) GedPoolAlloc( + pPool, uiAllocSize)) == NULL) + { + pNode->ui32Length = 0; + pNode->value = NULL; + return (NULL); + } + } + + if (valType == FLM_TEXT_TYPE) + { + rPtr[size] = '\0'; + } + + // Now set the size and the data type + + pNode->ui32Length = (FLMUINT32) size; + GedSetType( pNode, valType); + + // If passed-in enc id is zero, use the node's enc id. + + if (!uiEncId) + { + flmAssert( !uiEncSize); + if (size) + { + uiEncId = pNode->ui32EncId; + uiEncSize = size + (16 - (size % 16)); + } + } + else + { + + // We only should have an encryption ID if size is non-zero. If + // size is non-zero, encryption size must also be non-zero. + + flmAssert( size); + flmAssert( uiEncSize); + } + + if (uiEncId) + { + if (uiEncSize > GedEncLen( pNode)) + { + if ((pNode->pucEncValue = (FLMBYTE *) GedPoolAlloc( + pPool, uiEncSize)) == NULL) + { + pNode->ui32EncLength = 0; + pNode->pucEncValue = NULL; + return (NULL); + } + } + + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA | FLD_HAVE_ENCRYPTED_DATA; + pNode->ui32EncId = (FLMUINT32) uiEncId; + pNode->ui32EncLength = (FLMUINT32) uiEncSize; + } + + return (rPtr); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +void * GedValPtr( + NODE * nd) +{ + return( nd && nd->ui32Length + ? GedValType( nd) == FLM_TEXT_TYPE + ? nd->ui32Length < sizeof(void *) + ? (void *) &nd->value + : (void *) nd->value + : nd->ui32Length > sizeof(void *) + ? (void *) nd->value + : (void *) &nd->value + : NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +void * GedEncPtr( + NODE * nd) +{ + return( nd && nd->ui32EncLength + ? (void *) nd->pucEncValue + : (void *) NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutRecId( + POOL * pPool, + NODE ** ppNd, + FLMUINT uiId) +{ + NODE * pNewNd; + NODE * pOldNd = *ppNd; + FLMBYTE * ptr; + + if ((pNewNd = (NODE *) GedPoolAlloc( pPool, + sizeof(NODE) + sizeof(uiId))) == NULL) + { + *ppNd = NULL; + return (RC_SET( FERR_MEM)); + } + + // Copy the contents of the existing node + + pNewNd->prior = pOldNd->prior; + pNewNd->next = pOldNd->next; + pNewNd->value = pOldNd->value; + pNewNd->ui32Length = pOldNd->ui32Length; + pNewNd->ui32EncId = pOldNd->ui32EncId; + pNewNd->ui32EncLength = pOldNd->ui32EncLength; + pNewNd->ui32EncFlags = pOldNd->ui32EncFlags; + pNewNd->pucEncValue = pOldNd->pucEncValue; + GedTagNumSet( pNewNd, GedTagNum( pOldNd)); + GedNodeLevelSet( pNewNd, GedNodeLevel( pOldNd)); + GedNodeTypeSet( pNewNd, (GedNodeType( pOldNd) | HAS_REC_ID)); + + // Link in new node to parent and children/siblings + + if (pNewNd->prior) + { + pNewNd->prior->next = pNewNd; + } + + if (pNewNd->next) + { + pNewNd->next->prior = pNewNd; + } + + // Set the Ids value + + ptr = (FLMBYTE *) GedIdPtr( pNewNd); + *((FLMUINT *) (ptr + NODE_DRN_POS)) = uiId; + *ppNd = pNewNd; + + return (FERR_OK); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +void gedSetRecSource( + NODE * pNode, + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + FLMBYTE * pucPtr; + + pucPtr = ((FLMBYTE *) pNode) + sizeof(NODE); + if (uiDrn) + { + GedValTypeSetFlag( pNode, HAS_REC_ID); + *((FLMUINT *) (pucPtr + NODE_DRN_POS)) = uiDrn; + } + + if (uiContainer) + { + GedValTypeSetFlag( pNode, HAS_REC_SOURCE); + *((FLMUINT *) (pucPtr + NODE_CONTAINER_POS)) = uiContainer; + } + + if (hDb) + { + GedValTypeSetFlag( pNode, HAS_REC_SOURCE); + *((HFDB *) (pucPtr + NODE_DB_POS)) = hDb; + } +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetRecSource( + NODE * pNode, + HFDB * phDb, + FLMUINT * puiContainer, + FLMUINT * puiRecId) +{ + RCODE rc = FERR_OK; + FLMBYTE * ptr = ((FLMBYTE *) pNode) + sizeof(NODE); + + if (GedNodeType( pNode) & HAS_REC_SOURCE) + { + if (phDb) + { + *phDb = *((HFDB *) (ptr + NODE_DB_POS)); + } + + if (puiContainer) + { + *puiContainer = *((FLMUINT *) (ptr + NODE_CONTAINER_POS)); + } + + if (puiRecId) + { + *puiRecId = *((FLMUINT *) (ptr + NODE_DRN_POS)); + } + } + else if (GedNodeType( pNode) & HAS_REC_ID) + { + if (phDb) + { + *phDb = NULL; + } + + if (puiContainer) + { + *puiContainer = 0; + } + + if (puiRecId) + { + *puiRecId = *((FLMUINT *) (ptr + NODE_DRN_POS)); + } + } + else + { + // The record contains no record source, because the user may ignore + // the return code lets make sure everything is set to null. + + if (phDb) + { + *phDb = NULL; + } + + if (puiContainer) + { + *puiContainer = 0; + } + + if (puiRecId) + { + *puiRecId = 0; + } + + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutRecPtr( + POOL * pPool, + NODE * nd, + FLMUINT drn, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + void * ptr; + RCODE rc = FERR_OK; + + // Check for a null node being passed in + + if (nd == NULL) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + if ((ptr = GedAllocSpace( pPool, nd, FLM_CONTEXT_TYPE, sizeof(FLMUINT32), + uiEncId, uiEncSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + UD2FBA( (FLMUINT32) drn, ptr); + + if (nd->ui32EncId) + { + nd->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetRecPtr( + NODE * nd, + FLMUINT * drnRV) +{ + RCODE rc = FERR_OK; + + *drnRV = (FLMUINT) 0xFFFFFFFF; + + if (nd == NULL) + { + rc = RC_SET( FERR_CONV_NULL_SRC); + goto Exit; + } + + if (nd->ui32EncId) + { + if (!(nd->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (GedValType( nd) != FLM_CONTEXT_TYPE) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + if (GedValLen( nd) == sizeof(FLMUINT32)) + { + *drnRV = (FLMUINT) (FB2UD( (FLMBYTE *) GedValPtr( nd))); + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedWalk( + FLMUINT treeCnt, + NODE * pNode, + GEDWALK_FUNC_p func, + void * arg) +{ + RCODE rc; + + if (pNode) + { + FLMUINT baseLevel = GedNodeLevel( pNode); + do + { + rc = (*func)((GedNodeLevel( pNode) - baseLevel), pNode, arg); + } while( RC_OK( rc) && (pNode = pNode->next) != NULL && + (GedNodeLevel( pNode) > baseLevel || + (GedNodeLevel( pNode) == baseLevel && --treeCnt))); + } + else + { + rc = FERR_OK; + } + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedSibNext( + NODE * pNode) +{ + FLMUINT lev; + + if (pNode) + { + lev = GedNodeLevel( pNode); + while( ((pNode = pNode->next) != NULL) && (GedNodeLevel( pNode) > lev)); + } + + return ((pNode && (GedNodeLevel( pNode) == lev)) ? pNode : NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedParent( + NODE * pNode) +{ + if (pNode) + { + FLMUINT lev = GedNodeLevel( pNode); + while( ((pNode = pNode->prior) != NULL) && (GedNodeLevel( pNode) >= lev)); + } + + return (pNode); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedChild( + NODE * pNode) +{ + return( pNode && pNode->next && + (GedNodeLevel( pNode->next) > GedNodeLevel( pNode)) + ? pNode->next + : NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +NODE * GedSibPrev( + NODE * pNode) +{ + FLMUINT lev; + + if (pNode) + { + lev = GedNodeLevel( pNode); + while( ((pNode = pNode->prior) != NULL) && (GedNodeLevel( pNode) > lev)); + } + + return ((pNode && (GedNodeLevel( pNode) == lev)) ? pNode : NULL); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutNATIVE( + POOL * pPool, + NODE * pNode, + const char * nativeString, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + RCODE rc = FERR_OK; + FLMUINT allocLength; + FLMBYTE * outPtr; + + // Check for a null node being passed in + + if (!pNode) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + // If the string is NULL or empty, call GedAllocSpace with a length + // of zero to set the node length to zero and node type to + // FLM_TEXT_TYPE. + + if ((!nativeString) || (!(*nativeString))) + { + (void) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, 0, uiEncId, uiEncSize); + goto Exit; + } + + // Determine the size of the buffer needed to store the string + + if (RC_BAD( rc = FlmNative2Storage( nativeString, 0, &allocLength, NULL))) + { + goto Exit; + } + + if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, + allocLength, uiEncId, uiEncSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Convert the string + + if (RC_BAD( rc = FlmNative2Storage( nativeString, 0, &allocLength, outPtr))) + { + goto Exit; + } + + // Encrypted fields - only have decrypetd data at this point. + + if (pNode->ui32EncId) + { + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetNATIVE( + NODE * pNode, + char * pszBuffer, + FLMUINT * bufLenRV) +{ + RCODE rc = FERR_OK; + FLMBYTE * ptr; + FLMUINT valLength; + FLMUINT uiNodeType; + + if (!pNode) + { + rc = RC_SET( FERR_CONV_NULL_SRC); + goto Exit; + } + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + // If the node is not a TEXT or a NUMBER node, return an error for now + + uiNodeType = (FLMBYTE) GedValType( pNode); + if ((uiNodeType == FLM_BINARY_TYPE) || (uiNodeType == FLM_CONTEXT_TYPE)) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + ptr = (FLMBYTE *) GedValPtr( pNode); + valLength = GedValLen( pNode); + + rc = FlmStorage2Native( uiNodeType, valLength, + (const FLMBYTE *) ptr, bufLenRV, pszBuffer); + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutUINT( + POOL * pPool, + NODE * pNode, + FLMUINT uiNum, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPtr; + FLMBYTE ucNibStk[F_MAX_NUM_BUF + 1]; + FLMBYTE * pucNibStk; + + if (pNode == NULL) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + // push spare (undefined) nibble for possible half-used terminating + // byte + + pucNibStk = &ucNibStk[1]; + + // push terminator nibble -- popped last + + *pucNibStk++ = 0x0F; + + // push digits; + // do 32 bit division until we get down to 16 bits + + while (uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE) (uiNum % 10); // push BCD nibbles in reverse + // order + uiNum /= 10; + } + + *pucNibStk++ = (FLMBYTE) uiNum; // push last nibble of number + + // Determine number of bytes required for BCD number & allocate space + + if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, + ((pucNibStk - ucNibStk) >> 1), uiEncId, uiEncSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Pop stack and pack nibbles into byte stream a pair at a time + + do + { + *pucPtr++ = (FLMBYTE) ((pucNibStk[-1] << 4) | pucNibStk[-2]); + } while ((pucNibStk -= 2) > &ucNibStk[1]); + + // spare stack byte stops seg wrap + + if (pNode->ui32EncId) + { + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutINT( + POOL * pPool, + NODE * pNode, + FLMINT iNum, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + RCODE rc = FERR_OK; + FLMUINT uiNum; + FLMBYTE * pucPtr; + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; + FLMBYTE * pucNibStk; + FLMINT iNegFlag; + + if (!pNode) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + pucNibStk = &ucNibStk[1]; + *pucNibStk++ = 0x0F; + uiNum = ((iNegFlag = iNum < 0) != 0) ? -iNum : iNum; + + while (uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE) (uiNum % 10); + uiNum /= 10; + } + + *pucNibStk++ = (FLMBYTE) uiNum; + + if (iNegFlag) + { + *pucNibStk++ = 0x0B; + } + + // Determine number of bytes required for BCD number & allocate space + + if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, + ((pucNibStk - ucNibStk) >> 1), uiEncId, uiEncSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Pop stack and pack nibbles into byte stream a pair at a time + + do + { + *pucPtr++ = (FLMBYTE) ((pucNibStk[-1] << 4) | pucNibStk[-2]); + } while ((pucNibStk -= 2) > &ucNibStk[1]); + + // spare stack byte stops seg wrap + + if (pNode->ui32EncId) + { + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetINT( + NODE * pNode, + FLMINT * piNum) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_BAD( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + goto Exit; + } + + if (bcd.bNegFlag) + { + *piNum = -((FLMINT) bcd.uiNum); + rc = (bcd.uiNibCnt < 11) || + (bcd.uiNibCnt == 11 && (!bcd.pucPtr || + (f_memcmp( bcd.pucPtr, ucMinBcdINT32, 6) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else + { + *piNum = (FLMINT) bcd.uiNum; + rc = (bcd.uiNibCnt < 10) || + (bcd.uiNibCnt == 10 && (!bcd.pucPtr || + (f_memcmp( bcd.pucPtr, ucMaxBcdINT32, 5) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetINT32( + NODE * pNode, + FLMINT32 * pi32Num) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + if (bcd.bNegFlag) + { + *pi32Num = -((FLMINT32) bcd.uiNum); + rc = (bcd.uiNibCnt < 11) || + (bcd.uiNibCnt == 11 && (!bcd.pucPtr || + (f_memcmp( bcd.pucPtr, ucMinBcdINT32, 6) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else + { + *pi32Num = (FLMINT32) bcd.uiNum; + rc = (bcd.uiNibCnt < 10) || + (bcd.uiNibCnt == 10 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdINT32, 5) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetINT16( + NODE * pNode, + FLMINT16 * pi16Num) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + if (bcd.bNegFlag) + { + *pi16Num = -((FLMINT16) (bcd.uiNum)); + rc = (bcd.uiNibCnt < 6) || (bcd.uiNibCnt == 6 && bcd.uiNum <= FLM_MAX_INT16) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else + { + *pi16Num = (FLMINT16) bcd.uiNum; + rc = (bcd.uiNibCnt < 5) || (bcd.uiNibCnt == 5 && bcd.uiNum < FLM_MAX_INT16) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetUINT( + NODE * pNode, + FLMUINT * puiNum) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + *puiNum = bcd.uiNum; + + if (bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if (bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if (bcd.uiNibCnt == 10) + { + rc = ( !bcd.pucPtr || + (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetUINT8( + NODE * pNode, + FLMUINT8 * pui8Num) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + *pui8Num = (FLMUINT8) bcd.uiNum; + rc = bcd.bNegFlag + ? RC_SET( FERR_CONV_NUM_UNDERFLOW) + : (bcd.uiNibCnt < 3) || + (bcd.uiNibCnt == 3 && bcd.uiNum < FLM_MAX_UINT8) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetUINT32( + NODE * pNode, + FLMUINT32 * pui32Num) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd))) + { + *pui32Num = (FLMUINT32) bcd.uiNum; + + if (bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if (bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if (bcd.uiNibCnt == 10) + { + rc = (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedGetUINT16( + NODE * pNode, + FLMUINT16 * pui16Num) +{ + BCD_TYPE bcd; + RCODE rc = FERR_OK; + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), &bcd)) == FERR_OK) + { + *pui16Num = (FLMUINT16) bcd.uiNum; + rc = bcd.bNegFlag + ? RC_SET( FERR_CONV_NUM_UNDERFLOW) + : (bcd.uiNibCnt < 5) || + (bcd.uiNibCnt == 5 && bcd.uiNum < FLM_MAX_UINT16) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedNumToText( + const FLMBYTE * num, + FLMBYTE * buffer, + FLMUINT * bufLenRV) +{ + FLMBYTE * outPtr; + FLMBYTE c; + FLMBYTE c1 = 0; + FLMUINT bytesOutput; + FLMUINT outputData; + FLMUINT maxOutLen; + FLMBYTE done; + FLMBYTE firstNibble; + FLMBYTE lastChar; + const FLMBYTE * pExp = NULL; + FLMBYTE parseExponent = 0; + FLMBYTE firstNibbleAtExp = 0; + FLMBYTE firstDigit = 0; + + maxOutLen = *bufLenRV; + outputData = ((buffer != NULL) && (maxOutLen)); + bytesOutput = 0; + outPtr = buffer; + + // Parse through the string outputting data to the buffer + // as we go. + + done = (num == NULL); // Sets to TRUE if NULL else FALSE + firstNibble = 1; + lastChar = 0xFF; + + while (!done) + { +continue_loop: + + if (firstNibble) // Rather not do a ? : here because + { // of the num++ in the : portion + c = (FLMBYTE) (*num >> 4); + } + else + { + c = (FLMBYTE) (*num++ &0x0F); + } + + firstNibble = !firstNibble; + + if (c <= 9) // Check common case before switch + { + if (parseExponent) + { // Exponent number? + firstDigit++; + } + + c1 = (FLMBYTE) (ASCII_ZERO + c); // Normal decimal value + } + else + { + switch (c) + { + case 0x0A: + c1 = ASCII_DOT; + break; + case 0x0B: + c1 = ASCII_DASH; + break; + case 0x0C: // Ignore for now - imaginary + // numbers not implemented + c1 = 0; // Set c1 to zero if no output + break; + case 0x0D: + c1 = ASCII_SLASH; + break; + case 0x0E: + + // For real numbers the exponent appears first ; + // This was done to make it easier for building keys + + if (!parseExponent) + { + parseExponent++; // 1=need to output 1st digit + pExp = num; // Set state to reparse exponent + if (firstNibble) + { + pExp--; + } + + firstNibbleAtExp = (FLMBYTE) (firstNibble ^ 1); + + // Parse to the end of the exponent area - most 5 nibbles + + for (;;) + { + if (firstNibble) + { + if ((*num >> 4) == 0x0F) + { + break; + } + } + else + { + if ((*num++ &0x0F) == 0x0F) + { + break; + } + } + + firstNibble = !firstNibble; + } + + firstNibble = !firstNibble; // Don't forget this! + goto continue_loop; // 'continue' is vauge - use + ///* a goto + } + else + { + c1 = ASCII_UPPER_E; + parseExponent = 0; // Clear flag + } + break; + case 0x0F: + c1 = 0; // Set c1 to zero if no output + if (!parseExponent) + { // Done if no exponent or done /w exp + done = TRUE; + } + break; + } + } + + // If we got a character, put into output buffer (or just count) + + if (c1) + { + + // If the last character was an exponent and the current ; + // character is not a minus sign, insert a plus (+) + + if ((lastChar == ASCII_UPPER_E) && (c1 != ASCII_MINUS)) + { + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = ASCII_PLUS; + } + else + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + } + + bytesOutput++; + } + + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = c1; + } + else + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + } + + bytesOutput++; + + // If exponent (real) number output decimal place + + if (firstDigit == 1) + { + firstDigit++; // Set to != 1 + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = ASCII_DOT; + } + else + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + } + + bytesOutput++; + } + + lastChar = c1; + } + else if (parseExponent) // Hit last trailing 'F' in num + { + num = pExp; // Restore state + firstNibble = firstNibbleAtExp; + + // Go again parsing the exponent + + } + } + + *bufLenRV = bytesOutput; + return (FERR_OK); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedTextToNum( + FLMBYTE * textStr, // Pointer to buffer containing TEXT + FLMUINT textLen, // Length of text (in bytes) + FLMBYTE * buffer, // Pointer to buffer where number data is to be + // returned + FLMUINT * bufLenRV) // Return length -- on input contains buffer size +{ + FLMBYTE * outPtr; + FLMBYTE c; + FLMUINT bytesProcessed; + FLMUINT bytesOutput; + FLMUINT outputData; + FLMUINT maxOutLen; + FLMUINT objType; + FLMUINT objLength; + FLMBOOL firstNibble; + FLMBOOL have1Num; + FLMBOOL insideNum; + FLMBOOL haveSign; + + maxOutLen = *bufLenRV; + outputData = ((buffer != NULL) && (maxOutLen)); + bytesProcessed = bytesOutput = 0; + outPtr = buffer; + + // Parse through the string outputting data to the buffer + // as we go. + + haveSign = have1Num = insideNum = 0; + firstNibble = 1; + if (textStr == NULL) + { + textLen = 0; + } + + for (; + bytesProcessed < textLen; + textStr += objLength, bytesProcessed += objLength) + { + + // Determine what we are pointing at + + c = *textStr; + objType = (FLMBYTE) flmTextObjType( c); + + if (objType == ASCII_CHAR_CODE) + { + objLength = 1; + if ((c == ASCII_SPACE) || + (c == ASCII_TAB) || + (c == ASCII_NEWLINE) || + (c == ASCII_CR)) + { + if (insideNum) + { + have1Num = 1; + } + break; + } + + // Code below was a break - now skips leading zeros + + if ((c == ASCII_ZERO) && (!insideNum)) + { // Ignore leading zeroes + continue; + } + + if ((c >= ASCII_ZERO) && (c <= ASCII_NINE)) + { + if (!insideNum) + { + insideNum = 1; + haveSign = 1; + } + + c -= ASCII_ZERO; + } + + // Handle sign characters ('+', '-') + + else if (((c == ASCII_PLUS) || (c == ASCII_MINUS)) && + (!haveSign) && + (!insideNum)) + { + haveSign = 1; + if (c == ASCII_MINUS) + { + c = 0x0B; + } + } + else + { + return (RC_SET( FERR_CONV_BAD_DIGIT)); + } + + if (outputData) + { + if ((firstNibble) && (bytesOutput == maxOutLen)) + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + + if (firstNibble) + { + c <<= 4; + *outPtr = c; + } + else + { + *outPtr = (FLMBYTE) (*outPtr + c); + outPtr++; + } + } + + if (firstNibble) + { + bytesOutput++; + } + + firstNibble = !firstNibble; + } + else + { + switch (objType) + { + case WHITE_SPACE_CODE: + objLength = 1; + break; + + // Skip the unkown codes for now + + case UNK_GT_255_CODE: + objLength = (1 + sizeof(FLMUINT16) + FB2UW( textStr + 1)); + break; + case UNK_LE_255_CODE: + objLength = (2 + (FLMUINT16) * (textStr + 1)); + break; + case UNK_EQ_1_CODE: + objLength = 2; + break; + case CHAR_SET_CODE: + case EXT_CHAR_CODE: + case OEM_CODE: + case UNICODE_CODE: + + // Should not hit default. + + default: + return (RC_SET( FERR_CONV_BAD_DIGIT)); + } + } + } + + // Interpret empty number or all zeroes as single zero + + if ((!insideNum) && (!have1Num)) + { + if (outputData) + { + if ((firstNibble) && (bytesOutput == maxOutLen)) + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + + if (firstNibble) + { + *outPtr = 0x00; + } + else + { + outPtr++; + } + } + + if (firstNibble) + { + bytesOutput++; + } + + firstNibble = !firstNibble; + } + + // Add Terminator code to the end of the number + + if (outputData) + { + if ((firstNibble) && (bytesOutput == maxOutLen)) + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + + if (firstNibble) + { + *outPtr = 0xFF; + } + else + { + *outPtr = (FLMBYTE) (*outPtr + 0x0F); + } + } + + if (firstNibble) + { + bytesOutput++; + } + + *bufLenRV = bytesOutput; + return (FERR_OK); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE GedPutUNICODE( + POOL * pPool, + NODE * pNode, + const FLMUNICODE * puzString, + FLMUINT uiEncId, + FLMUINT uiEncSize) +{ + FLMUINT allocLength = 0; + FLMBYTE * outPtr; + RCODE rc = FERR_OK; + + // Check for a null node being passed in + + if (pNode == NULL) + { + rc = RC_SET( FERR_CONV_NULL_DEST); + goto Exit; + } + + // If the string is NULL or empty, call GedAllocSpace with a length of + // zero to set the node length to zero and node type to FLM_TEXT_TYPE. + + if ((puzString == NULL) || (*puzString == 0)) + { + GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, 0, uiEncId, uiEncSize); + return (FERR_OK); + } + + // Two passes are needed on the data. The first pass is to determine + // the storage length The second pass is to store the string into + // FLAIMs internal text format + + allocLength = FlmGetUnicodeStorageLength( puzString); + + if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, + allocLength, uiEncId, uiEncSize)) == NULL) + { + return (RC_SET( FERR_MEM)); + } + + if (RC_BAD( rc = FlmUnicode2Storage( puzString, &allocLength, outPtr))) + { + goto Exit; + } + + if (pNode->ui32EncId) + { + pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: Get Unicode data from a GEDCOM node. +*****************************************************************************/ +RCODE GedGetUNICODE( + NODE * pNode, + FLMUNICODE * uniBuf, + FLMUINT * bufLenRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiNodeType; + + // Check for a null node + + if( !pNode) + { + rc = RC_SET( FERR_CONV_NULL_SRC); + goto Exit; + } + + if (pNode->ui32EncId) + { + if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) + { + rc = RC_SET( FERR_FLD_NOT_DECRYPTED); + goto Exit; + } + } + + // If the node is not a TEXT or a NUMBER node, return an error for now. + + uiNodeType = GedValType( pNode); + + if (uiNodeType == FLM_BINARY_TYPE || uiNodeType == FLM_CONTEXT_TYPE) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + if( RC_BAD( rc = FlmStorage2Unicode( uiNodeType, GedValLen( pNode), + (const FLMBYTE *) GedValPtr( pNode), + bufLenRV, uniBuf))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE gedCreateSourceNode( + POOL * pPool, + FLMUINT uiFieldNum, + HFDB hDb, + FLMUINT uiContainer, + FLMUINT uiRecId, + NODE ** ppNode) +{ + NODE * nd; + RCODE rc = FERR_OK; + + *ppNode = nd = (NODE *) GedPoolCalloc( pPool, (sizeof(NODE) + + sizeof(FLMUINT) + sizeof(FLMUINT) + sizeof(HFDB))); + + if (nd != NULL) + { + GedValTypeSet( nd, FLM_CONTEXT_TYPE); + GedTagNumSet( nd, uiFieldNum); + gedSetRecSource( nd, hDb, uiContainer, uiRecId); + } + else + { + rc = RC_SET( FERR_MEM); + } + + return (rc); +} + + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE expImpInit( + F_FileHdl * pFileHdl, + FLMUINT uiFlag, + EXP_IMP_INFO * pExpImpInfoRV) +{ + RCODE rc = FERR_OK; + + f_memset( pExpImpInfoRV, 0, sizeof(EXP_IMP_INFO)); + pExpImpInfoRV->pFileHdl = pFileHdl; + pExpImpInfoRV->bDictRecords = (uiFlag == EXPIMP_IMPORT_EXPORT_GEDCOM) + ? FALSE + : TRUE; + + // Allocate a buffer for reading or writing. + + pExpImpInfoRV->uiBufSize = (uiFlag == EXPIMP_IMPORT_EXPORT_GEDCOM) + ? (FLMUINT) 2048 + : (FLMUINT) 32768; + for (;;) + { + if (RC_BAD( rc = f_alloc( pExpImpInfoRV->uiBufSize, &pExpImpInfoRV->pBuf))) + { + pExpImpInfoRV->uiBufSize -= 512; + if (pExpImpInfoRV->uiBufSize < 1024) + { + pExpImpInfoRV->uiBufSize = 0; + goto Exit; + } + } + else + { + break; + } + } + + // If writing, output the header data. If reading, seek past it. + + if (uiFlag == EXPIMP_EXPORT_DICTIONARY) + { + + // Write out the header data. + + rc = expWrite( pExpImpInfoRV, FlmBinaryGedHeader, BINARY_GED_HEADER_LEN); + } + else if (uiFlag == EXPIMP_IMPORT_DICTIONARY) + { + rc = pFileHdl->Seek( (FLMUINT) BINARY_GED_HEADER_LEN, F_IO_SEEK_SET, + &pExpImpInfoRV->uiFilePos); + } + else + { + rc = expWrite( pExpImpInfoRV, FlmBinaryRecHeader, BINARY_GED_HEADER_LEN); + } + +Exit: + + if (RC_BAD( rc)) + { + expImpFree( pExpImpInfoRV); + } + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +void expImpFree( + EXP_IMP_INFO * pExpImpInfo) +{ + if (pExpImpInfo->pBuf) + { + f_free( &pExpImpInfo->pBuf); + } + + f_memset( pExpImpInfo, 0, sizeof(EXP_IMP_INFO)); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE expFlush( + EXP_IMP_INFO * pExpImpInfo) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + + if ((pExpImpInfo->uiBufUsed) && (pExpImpInfo->bBufDirty)) + { + if (RC_BAD( rc = pExpImpInfo->pFileHdl->Write( pExpImpInfo->uiFilePos, + pExpImpInfo->uiBufUsed, pExpImpInfo->pBuf, &uiBytesWritten))) + { + goto Exit; + } + + if (uiBytesWritten < pExpImpInfo->uiBufUsed) + { + rc = RC_SET( FERR_IO_DISK_FULL); + goto Exit; + } + + pExpImpInfo->uiFilePos += uiBytesWritten; + pExpImpInfo->uiCurrBuffOffset = pExpImpInfo->uiBufUsed = 0; + pExpImpInfo->bBufDirty = FALSE; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE expImpSeek( + EXP_IMP_INFO * pExpImpInfo, + FLMUINT uiSeekPos) +{ + RCODE rc = FERR_OK; + + if ((uiSeekPos >= pExpImpInfo->uiFilePos) && + (uiSeekPos < pExpImpInfo->uiFilePos + (FLMUINT) pExpImpInfo->uiBufUsed)) + { + pExpImpInfo->uiCurrBuffOffset = (FLMUINT) (uiSeekPos - pExpImpInfo->uiFilePos); + } + else + { + if (pExpImpInfo->bBufDirty) + { + if (RC_BAD( rc = expFlush( pExpImpInfo))) + { + goto Exit; + } + } + + pExpImpInfo->uiFilePos = uiSeekPos; + pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE expWrite( + EXP_IMP_INFO * pExpImpInfo, + const FLMBYTE * pData, + FLMUINT uiDataLen) +{ + RCODE rc = FERR_OK; + FLMUINT uiCopyLen; + + while (uiDataLen) + { + if ((uiCopyLen = (pExpImpInfo->uiBufSize - + pExpImpInfo->uiCurrBuffOffset)) > uiDataLen) + { + uiCopyLen = uiDataLen; + } + + f_memcpy( &pExpImpInfo->pBuf[pExpImpInfo->uiCurrBuffOffset], pData, + uiCopyLen); + + pExpImpInfo->bBufDirty = TRUE; + uiDataLen -= uiCopyLen; + pData += uiCopyLen; + pExpImpInfo->uiCurrBuffOffset += uiCopyLen; + + if (pExpImpInfo->uiCurrBuffOffset > pExpImpInfo->uiBufUsed) + { + pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset; + } + + if (pExpImpInfo->uiCurrBuffOffset == pExpImpInfo->uiBufSize) + { + if (RC_BAD( rc = expFlush( pExpImpInfo))) + { + goto Exit; + } + } + } + +Exit: + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE expWriteRec( + EXP_IMP_INFO * pExpImpInfo, + FlmRecord * pRecord, + FLMUINT uiDrn) +{ + RCODE rc = FERR_OK; + FLMBYTE TBuf[ 24]; + FLMUINT uiLen; + FLMUINT uiTagNum; + FLMUINT uiInitLevel; + FLMBOOL bOutputtingRecInfo; + FLMBOOL bRootNode; + FLMUINT uiTmpLen; + FlmRecord * pRec = NULL; + FlmRecord * pRecInfoRec = NULL; + void * pvField; + + if (pExpImpInfo->bDictRecords) + { + + // Create a record for the RECINFO information + + if ((pRecInfoRec = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pRecInfoRec->insertLast( 0, FLM_RECINFO_TAG, + FLM_NUMBER_TYPE, &pvField))) + { + goto Exit; + } + + // Add the record's DRN to the RECINFO information. + + if (RC_BAD( rc = flmAddField( pRecInfoRec, FLM_DRN_TAG, (void *) &uiDrn, + 4, FLM_NUMBER_TYPE))) + { + goto Exit; + } + + bOutputtingRecInfo = TRUE; + + // Output both the REC_INFO GEDCOM tree and the record's GEDCOM + // tree. + + bRootNode = FALSE; + pRec = pRecInfoRec; + } + else + { + + // Output only the GEDCOM tree. + + bOutputtingRecInfo = FALSE; + bRootNode = TRUE; + pRec = pRecord; + } + + for (;;) + { + + // Output each node in the record. + + pvField = pRec->root(); + uiInitLevel = pRec->getLevel( pvField); + do + { + uiTagNum = pRec->getFieldID( pvField); + uiLen = pRec->getDataLength( pvField); + UW2FBA( (FLMUINT16) uiTagNum, TBuf); + UW2FBA( (FLMUINT16) uiLen, &TBuf[2]); + TBuf[4] = (FLMBYTE) (pRec->getLevel( pvField) - uiInitLevel); + TBuf[5] = (FLMBYTE) (pRec->getDataType( pvField)); + + // Add on the record source information for the root node. + + uiTmpLen = 6; + if (bRootNode) + { + UW2FBA( (FLMUINT16) pRec->getContainerID(), &TBuf[14]); + UD2FBA( pRec->getID(), &TBuf[16]); + uiTmpLen = 20; + + bRootNode = FALSE; + } + + if (RC_BAD( rc = expWrite( pExpImpInfo, TBuf, uiTmpLen))) + { + goto Exit; + } + + if (uiLen) + { + const FLMBYTE * pvData = pRec->getDataPtr( pvField); + + if (RC_BAD( rc = expWrite( pExpImpInfo, pvData, uiLen))) + { + goto Exit; + } + } + + pvField = pRec->next( pvField); + } while (pvField && (pRec->getLevel( pvField) > uiInitLevel)); + + // Output a zero tag number to indicate end of GEDCOM record. + + UW2FBA( 0, TBuf); + if (RC_BAD( rc = expWrite( pExpImpInfo, TBuf, 2))) + { + goto Exit; + } + + // Set things up to output the record after the REC_INFO. + + if (!bOutputtingRecInfo) + { + break; + } + + bOutputtingRecInfo = FALSE; + bRootNode = TRUE; + pRec = pRecord; + } + +Exit: + + if (pRecInfoRec) + { + pRecInfoRec->Release(); + } + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE impRead( + EXP_IMP_INFO * pExpImpInfo, // Export/Import information. + FLMBYTE * pData, // Buffer where data is to be read into. + FLMUINT uiDataLen, // Length of data to be read in. + FLMUINT * puiBytesReadRV) // Returns amount of data read in. +{ + RCODE rc = FERR_OK; + FLMUINT uiCopyLen; + FLMUINT uiBytesRead = 0; + + while (uiDataLen) + { + + // See if we need to read some more data into the import buffer. + + if (pExpImpInfo->uiCurrBuffOffset == pExpImpInfo->uiBufUsed) + { + + // If we have a dirty buffer, flush it out first. + + if (pExpImpInfo->bBufDirty) + { + if (RC_BAD( rc = expFlush( pExpImpInfo))) + { + goto Exit; + } + } + else + { + pExpImpInfo->uiFilePos += (FLMUINT) pExpImpInfo->uiBufUsed; + pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; + } + + if (RC_BAD( rc = pExpImpInfo->pFileHdl->Read( pExpImpInfo->uiFilePos, + pExpImpInfo->uiBufSize, pExpImpInfo->pBuf, + &pExpImpInfo->uiBufUsed))) + { + if ((rc == FERR_IO_END_OF_FILE) && (pExpImpInfo->uiBufUsed)) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + + // Copy from the import buffer to the data buffer. + + if (( + uiCopyLen = + ( + pExpImpInfo->uiBufUsed - + pExpImpInfo->uiCurrBuffOffset + ) + ) > uiDataLen) + { + uiCopyLen = uiDataLen; + } + + f_memcpy( pData, &pExpImpInfo->pBuf[pExpImpInfo->uiCurrBuffOffset], + uiCopyLen); + uiDataLen -= uiCopyLen; + uiBytesRead += uiCopyLen; + pData += uiCopyLen; + pExpImpInfo->uiCurrBuffOffset += uiCopyLen; + } + +Exit: + + *puiBytesReadRV = uiBytesRead; + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE impReadRec( + EXP_IMP_INFO * pExpImpInfo, // Export/Import information. + FlmRecord ** ppRecordRV) // Returns record that was read in. +{ + RCODE rc = FERR_OK; + FLMBYTE TBuf[24]; + FLMUINT uiLen; + FLMUINT uiTagNum; + FLMUINT uiRecInfoDrn = 0; + FLMUINT uiDictID; + FLMBOOL bHaveRecInfo = FALSE; + FLMBOOL bHaveDictID = FALSE; + FLMUINT uiLevel; + FLMUINT uiType; + FLMBOOL bGettingRecInfo; + FLMUINT uiBytesRead; + FLMUINT uiTmpLen; + FlmRecord * pRecord = NULL; + void * pvField; + + bGettingRecInfo = (pExpImpInfo->bDictRecords) ? TRUE : FALSE; + + // Read each node in the REC_INFO (if dictionary) and then the record. + + for (;;) + { + if (RC_BAD( rc = impRead( pExpImpInfo, TBuf, 2, &uiBytesRead))) + { + if ((rc == FERR_IO_END_OF_FILE) && + (uiBytesRead == 0) && + ((!bGettingRecInfo) || (!bHaveRecInfo))) + { + rc = RC_SET( FERR_END); + } + + goto Exit; + } + + // A tag number of zero means we are at the end of the record. + + uiTagNum = FB2UW( TBuf); + if (!uiTagNum) + { + if (bGettingRecInfo) + { + bGettingRecInfo = FALSE; + continue; + } + else + { + break; + } + } + + uiTmpLen = ((!bGettingRecInfo) && (!pRecord)) ? 18 : 4; + if (RC_BAD( rc = impRead( pExpImpInfo, TBuf, uiTmpLen, &uiBytesRead))) + { + goto Exit; + } + + uiLen = FB2UW( TBuf); + uiLevel = TBuf[2]; + uiType = TBuf[3]; + + if (!pRecord) + { + if ((pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecord->setContainerID( FB2UW( &TBuf[12])); + pRecord->setID( FB2UD( &TBuf[14])); + } + + if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum, + uiType, &pvField))) + { + goto Exit; + } + + if (uiLen) + { + FLMBYTE * pValue; + + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiType, + uiLen, 0, 0, 0, &pValue, NULL))) + { + goto Exit; + } + + if (RC_BAD( rc = impRead( pExpImpInfo, pValue, uiLen, &uiBytesRead))) + { + goto Exit; + } + } + + // Link the node into the tree. + + if (bGettingRecInfo) + { + switch (uiTagNum) + { + case FLM_RECINFO_TAG: + { + bHaveRecInfo = TRUE; + break; + } + + case FLM_DRN_TAG: + { + if (RC_BAD( rc = pRecord->getUINT( pvField, &uiRecInfoDrn))) + { + goto Exit; + } + break; + } + + case FLM_DICT_SEQ_TAG: + { + if (RC_BAD( rc = pRecord->getUINT( pvField, &uiDictID))) + { + goto Exit; + } + + bHaveDictID = TRUE; + break; + } + } + } + } + +Exit: + + if (RC_OK( rc)) + { + *ppRecordRV = pRecord; + } + else + { + if (pRecord) + { + pRecord->Release(); + } + + *ppRecordRV = NULL; + } + + return (rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE impFileIsExpImp( + F_FileHdl * pFileHdl, + FLMBOOL * pbFileIsBinaryRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiCurrPos; + FLMUINT uiIgnore; + FLMBYTE byHeader[ BINARY_GED_HEADER_LEN]; + FLMUINT uiBytesRead; + + *pbFileIsBinaryRV = FALSE; + + // Save current position so we can return to it. + + if (RC_BAD( rc = pFileHdl->Seek( (FLMUINT) 0, F_IO_SEEK_CUR, &uiCurrPos))) + { + goto Exit; + } + + // Read the file's header information. + + if (RC_BAD( rc = pFileHdl->Read( (FLMUINT) 0, BINARY_GED_HEADER_LEN, + byHeader, &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + uiBytesRead = 0; + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + if ((uiBytesRead == BINARY_GED_HEADER_LEN) && + ((f_memcmp( byHeader, FlmBinaryGedHeader, BINARY_GED_HEADER_LEN) == 0) || + (f_memcmp( byHeader, FlmBinaryRecHeader, BINARY_GED_HEADER_LEN) == 0))) + { + *pbFileIsBinaryRV = TRUE; + } + + // Reset the file position to where it was before. + + rc = pFileHdl->Seek( uiCurrPos, F_IO_SEEK_SET, &uiIgnore); + +Exit: + + return (rc); +} + + +/**************************************************************************** +Desc: This routine adds a field to a GEDCOM tree +****************************************************************************/ +RCODE gedAddField( + POOL * pPool, + NODE * pRecord, + FLMUINT uiTagNum, + const void * pvData, + FLMUINT uiDataLen, + FLMUINT uiDataType) +{ + RCODE rc = FERR_OK; + NODE * pChildNode; + FLMUINT uiNum; + + if ((pChildNode = GedNodeMake( pPool, uiTagNum, &rc)) == NULL) + { + goto Exit; + } + + switch( uiDataType) + { + case FLM_TEXT_TYPE: + { + rc = GedPutNATIVE( pPool, pChildNode, (const char *)pvData); + break; + } + + case FLM_NUMBER_TYPE: + { + switch (uiDataLen) + { + case 0: + uiNum = (FLMUINT)(*((FLMUINT *)(pvData))); + break; + case 1: + uiNum = (FLMUINT)(*((FLMBYTE *)(pvData))); + break; + case 2: + uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData))); + break; + case 4: + uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData))); + break; + default: + flmAssert( 0); + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + + rc = GedPutUINT( pPool, pChildNode, uiNum); + break; + } + + case FLM_BINARY_TYPE: + { + rc = GedPutBINARY( pPool, pChildNode, pvData, uiDataLen); + break; + } + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + GedChildGraft( pRecord, pChildNode, GED_LAST); + +Exit: + + return( rc); +} diff --git a/flaim/src/fnumber.cpp b/flaim/src/fnumber.cpp new file mode 100644 index 0000000..0e4fd3e --- /dev/null +++ b/flaim/src/fnumber.cpp @@ -0,0 +1,387 @@ +//------------------------------------------------------------------------- +// Desc: Routines to handle numbers. +// Tabs: 3 +// +// Copyright (c) 1999-2001,2003-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$ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +// Static Data + +FLMBYTE ucMaxBcdINT32[] = +{ + 0x21, + 0x47, + 0x48, + 0x36, + 0x47 +}; + +FLMBYTE ucMinBcdINT32[] = +{ + 0xB2, + 0x14, + 0x74, + 0x83, + 0x64, + 0x8F +}; + +FLMBYTE ucMaxBcdUINT32[] = +{ + 0x42, + 0x94, + 0x96, + 0x72, + 0x95 +}; + +/**************************************************************************** +Desc: Given an unsigned number create the matching FLAIM-specific BCD + number. +Note: If terminating byte is half-full, low-nibble value is + undefined. Example: -125 creates B1-25-FX +Method: Using a MOD algorithm, stack BCD values -- popping to + destination reverses the order for correct final sequence +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmUINT2Storage( + FLMUINT uiNum, + FLMUINT * puiBufLength, + FLMBYTE * pBuf) +{ + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; + FLMBYTE * pucNibStk; + + flmAssert( *puiBufLength >= F_MAX_NUM_BUF); + + // push spare (undefined) nibble for possible half-used terminating byte + + pucNibStk = &ucNibStk[ 1]; + + // push terminator nibble -- popped last + + *pucNibStk++ = 0x0F; + + // push digits + // do 32 bit division until we get down to 16 bits + + while( uiNum >= 10) + { + // push BCD nibbles in reverse order + + *pucNibStk++ = (FLMBYTE)(uiNum % 10); + uiNum /= 10; + } + + // push last nibble of number + + *pucNibStk++ = (FLMBYTE)uiNum; + + // count: nibbleCount / 2 and truncate + + *puiBufLength = ((pucNibStk - ucNibStk) >> 1); + + // Pop stack and pack nibbles into byte stream a pair at a time + + do + { + *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); + } + while( (pucNibStk -= 2) > &ucNibStk[ 1]); + + return( FERR_OK); +} + +/**************************************************************************** +Desc: Given an signed number create the matching FLAIM-specific BCD + number. +Note: If terminating byte is half-full, low-nibble value is + undefined. Example: -125 creates B1-25-FX +Method: Using a MOD algorithm, stack BCD values -- popping to + destination reverses the order for correct final sequence +WARNING: -2,147,483,648 may yield different results on different platforms +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmINT2Storage( + FLMINT iNum, + FLMUINT * puiBufLength, + FLMBYTE * pBuf) +{ + FLMUINT uiNum; + FLMBYTE ucNibStk[ F_MAX_NUM_BUF + 1]; + FLMBYTE * pucNibStk; + FLMINT iNegFlag; + + flmAssert( *puiBufLength >= F_MAX_NUM_BUF); + + pucNibStk = &ucNibStk[ 1]; + *pucNibStk++ = 0x0F; + + uiNum = ((iNegFlag = iNum < 0) != 0) + ? -iNum + : iNum; + + while( uiNum >= 10) + { + *pucNibStk++ = (FLMBYTE)(uiNum % 10); + uiNum /= 10; + } + + *pucNibStk++ = (FLMBYTE)uiNum; + + if( iNegFlag) + { + *pucNibStk++ = 0x0B; + } + + *puiBufLength = ((pucNibStk - ucNibStk) >> 1); + + do + { + *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); + } + while( (pucNibStk -= 2) > &ucNibStk[ 1]); + + return( FERR_OK); +} + +/**************************************************************************** +Desc: Returns a signed value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmStorage2INT( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + FLMINT * piNum) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd = {0}; + + if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + if( bcd.bNegFlag) + { + *piNum = -((FLMINT)bcd.uiNum); + return( (bcd.uiNibCnt < 11) || + (bcd.uiNibCnt == 11 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMinBcdINT32, 6) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_UNDERFLOW)); + } + else + { + *piNum = (FLMINT)bcd.uiNum; + return( (bcd.uiNibCnt < 10) || + (bcd.uiNibCnt == 10 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdINT32, 5) <= 0))) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW)); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a unsigned value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmStorage2UINT( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + FLMUINT * puiNum) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd = {0}; + + if( RC_OK( rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + *puiNum = bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if( bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if( bcd.uiNibCnt == 10) + { + rc = (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a unsigned value from a BCD value. + The data may be a number type, or context type. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmStorage2UINT32( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + FLMUINT32 * pui32Num) +{ + RCODE rc = FERR_OK; + BCD_TYPE bcd = {0}; + + if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &bcd))) + { + *pui32Num = (FLMUINT32)bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); + } + else if( bcd.uiNibCnt < 10) + { + rc = FERR_OK; + } + else if( bcd.uiNibCnt == 10) + { + rc = (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, ucMaxBcdUINT32, 5) <= 0)) + ? FERR_OK + : RC_SET( FERR_CONV_NUM_OVERFLOW); + } + else + { + rc = RC_SET( FERR_CONV_NUM_OVERFLOW); + } + } + + return( rc); +} + + +/**************************************************************************** +Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a number +****************************************************************************/ +RCODE flmBcd2Num( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + BCD_TYPE * bcd) +{ + if( pucValue == NULL) + { + return( RC_SET( FERR_CONV_NULL_SRC)); + } + + switch( uiValueType) + { + case FLM_NUMBER_TYPE: + { + FLMUINT uiTotalNum = 0; + FLMUINT uiByte; + FLMUINT uiNibCnt; + + bcd->pucPtr = pucValue; + + // Get each nibble and use to create the number + + for( bcd->bNegFlag = (FLMBOOL)(uiNibCnt = ((*pucValue & 0xF0) == 0xB0) + ? 1 + : 0); + uiNibCnt <= FLM_MAX_NIB_CNT; + uiNibCnt++ ) + { + + uiByte = (uiNibCnt & 0x01) + ? (FLMUINT)(0x0F & *pucValue++) + : (FLMUINT)(*pucValue >> 4); + + if( uiByte == 0x0F) + { + break; + } + + uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1) + uiByte; + } + + bcd->uiNibCnt = uiNibCnt; + bcd->uiNum = uiTotalNum; + break; + } + + case FLM_TEXT_TYPE : + { + FLMUINT uiNumber = 0; + + while( uiValueLength--) + { + if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE) + { + break; + } + + uiNumber = (uiNumber * 10) + (*pucValue - ASCII_ZERO); + pucValue++; + } + + bcd->uiNum = uiNumber; + bcd->uiNibCnt = 0; + bcd->bNegFlag = FALSE; + break; + } + + case FLM_CONTEXT_TYPE : + { + if( uiValueLength == sizeof( FLMUINT32)) + { + bcd->uiNum = (FLMUINT)( FB2UD( pucValue)); + + bcd->bNegFlag = 0; + if( bcd->uiNum < FLM_MAX_UINT8) + { + bcd->uiNibCnt = 3; + } + else if( bcd->uiNum < FLM_MAX_UINT16) + { + bcd->uiNibCnt = 5; + } + else + { + bcd->uiNibCnt = 9; + } + } + + break; + } + + default: + { + flmAssert( 0); + return( RC_SET( FERR_CONV_ILLEGAL)); + } + } + + return( FERR_OK); +} diff --git a/flaim/src/frebuild.cpp b/flaim/src/frebuild.cpp new file mode 100644 index 0000000..78a5aec --- /dev/null +++ b/flaim/src/frebuild.cpp @@ -0,0 +1,2823 @@ +//------------------------------------------------------------------------- +// Desc: Rebuild corrupted database. +// Tabs: 3 +// +// Copyright (c) 1991-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: flblddb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +typedef struct Container_Info +{ + FLMUINT uiHighetstDrnFound; + FLMUINT uiHighestNextDrnFound; + FLMUINT uiNumNextDrnsFound; + FLMBOOL bCountEstimated; +} CONTAINER_INFO, * CONTAINER_INFO_p; + +typedef struct RECOV_DICT_REC +{ + FlmRecord * pRec; + FLMUINT uiBlkAddress; + FLMUINT uiElmOffset; + FLMBOOL bAdded; + FLMBOOL bGotFromDataRec; + RECOV_DICT_REC * pNext; +} RECOV_DICT_REC; + +typedef struct +{ + RECOV_DICT_REC * pRecovRecs; + POOL pool; +} RECOV_DICT_INFO; + +FSTATIC RCODE bldAdjustNextDrn( + FDB * pDb, + LFILE * pLFile, + CONTAINER_INFO * pContInfo); + +FSTATIC RCODE bldRecovData( + FDB * pDb, + REBUILD_STATE * pRebuildState, + CONTAINER_INFO * pContainerInfo, + FLMBOOL bRecovDictRecs, + FLMBOOL * pbStartedTransRV); + +FSTATIC RCODE bldCheckBlock( + STATE_INFO * pStateInfo, + HDR_INFO * pHdrInfo, + FLMUINT uiBlkAddress, + FLMUINT uiPrevBlkAddress, + eCorruptionType * peCorruptionCode); + +FSTATIC RCODE bldExtractRecs( + FDB * pDb, + REBUILD_STATE * pRebuildState, + LFILE * pLFile, + CONTAINER_INFO * pContInfo, + FLMUINT uiBlkAddress, + LF_HDR * pLogicalFile, + FLMBOOL bRecovDictRecs, + RECOV_DICT_INFO ** ppRecovDictInfoRV); + +FSTATIC RCODE bldGetNextElm( + REBUILD_STATE * pRebuildState, + STATE_INFO * pStateInfo, + FLMBOOL * pbGotNextElmRV, + FLMBOOL * pbGotNewBlockRV); + +FSTATIC RCODE bldGetOneRec( + FDB * pDb, + REBUILD_STATE * pRebuildState, + STATE_INFO * pStateInfo, + FLMBOOL bRecovDictRecs, + FLMBOOL * pbGotNewBlockRV, + FLMBOOL * pbGotRecord); + +FSTATIC RCODE bldSaveRecovDictRec( + FDB * pDb, + RECOV_DICT_INFO ** ppRecovDictInfoRV, + FlmRecord * pRecord, + FLMUINT uiDrn, + FLMBOOL bGotFromDataRec, + FLMUINT uiBlkAddress, + FLMUINT uiElmOffset); + +FSTATIC void bldFreeRecovDictInfo( + RECOV_DICT_INFO * pRecovDictInfo); + +FSTATIC RCODE bldDoDict( + FDB * pDb, + REBUILD_STATE * pRebuildState, + RECOV_DICT_INFO * pDictToDo, + FLMBOOL * pbStartedTransRV); + +FSTATIC RCODE bldDetermineBlkSize( + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxFileSize, + FLMUINT * puiBlkSizeRV, + STATUS_HOOK fnStatusFunc, + REBUILD_INFO * pCallbackData, + void * AppArg); + +/*************************************************************************** +Desc: This routine adds all of the recovered dictionary records to their + appropriate dictionaries. +****************************************************************************/ +FINLINE RCODE bldAddRecovDictRecs( + FDB * pDb, + REBUILD_STATE * pRebuildState, + RECOV_DICT_INFO ** ppDictListRV, + FLMBOOL * pbStartedTransRV) +{ + RECOV_DICT_INFO * pDict; + + if( (pDict = *ppDictListRV) != NULL) + { + *ppDictListRV = NULL; + return bldDoDict( pDb, pRebuildState, pDict, pbStartedTransRV); + } + + return FERR_OK; +} + +/*************************************************************************** +Desc: Setup corrupt info structure prior to calling status callback +****************************************************************************/ +FINLINE RCODE bldReportReason( + REBUILD_STATE * pRebuildState, + eCorruptionType eCorruption, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT uiErrDrn, + FLMUINT uiErrElmRecOffset, + FLMUINT uiErrFieldNum) +{ + RCODE rc = FERR_OK; + + if( pRebuildState->fnStatusFunc) + { + pRebuildState->CorruptInfo.eCorruption = eCorruption; + pRebuildState->CorruptInfo.uiErrBlkAddress = uiErrBlkAddress; + pRebuildState->CorruptInfo.uiErrElmOffset = uiErrElmOffset; + pRebuildState->CorruptInfo.uiErrDrn = uiErrDrn; + pRebuildState->CorruptInfo.uiErrElmRecOffset = uiErrElmRecOffset; + pRebuildState->CorruptInfo.uiErrFieldNum = uiErrFieldNum; + rc = (*pRebuildState->fnStatusFunc)( FLM_PROBLEM_STATUS, + (void *)&pRebuildState->CorruptInfo, + (void *)0, pRebuildState->AppArg); + pRebuildState->CorruptInfo.eCorruption = FLM_NO_CORRUPTION; + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine determines whether or not a container should be done. +****************************************************************************/ +FINLINE FLMBOOL bldDoContainer( + FLMUINT uiContainerNum, + FLMBOOL bDoDictContainers) +{ + if (!bDoDictContainers) + { + switch (uiContainerNum) + { + case FLM_DICT_CONTAINER: + return( FALSE); + case FLM_DATA_CONTAINER: + return( TRUE); + default: + if (uiContainerNum < FLM_RESERVED_TAG_NUMS) + { + return( TRUE); + } + else + { + return( FALSE); + } + } + } + + return( TRUE); +} + +/*************************************************************************** +Desc: Reads through a database, extracts data records from all containers + and puts them into the database specified by hDb. It is + assumed that the new database has the same containers as the old + database. +****************************************************************************/ +RCODE flmDbRebuildFile( + REBUILD_STATE * pRebuildState, // Rebuild state information. + FLMBOOL bBadHeader // Was file's header or log header + // information bad? + ) +{ + RCODE rc = FERR_OK; + FLMUINT uiCurrLf; + FLMBOOL bFdbInitialized = FALSE; + FLMBOOL bStartedTrans = FALSE; + LFILE * pLFile; + FLMUINT uiTemp; + FLMUINT uiContainerNum; + CONTAINER_INFO * pContainerInfo = NULL; + CONTAINER_INFO * pContInfo; + FDB * pDb = (FDB *)pRebuildState->hDb; + + pRebuildState->CorruptInfo.eErrLocale = LOCALE_B_TREE; + pRebuildState->CorruptInfo.uiErrLfType = LF_CONTAINER; + + bFdbInitialized = TRUE; + if (RC_BAD( fdbInit( pDb, FLM_UPDATE_TRANS, + FDB_DONT_RESET_DIAG, + FLM_AUTO_TRANS | 5, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( pRebuildState->pHdrInfo->FileHdr.uiBlockSize, + &pRebuildState->pBlk))) + { + goto Exit; + } + + // Do a first pass to recover any dictionary items that may not have + // been added from the dictionary file that was passed into the rebuild + // function. + + if( RC_BAD( rc = bldRecovData( pDb, pRebuildState, pContainerInfo, TRUE, + &bStartedTrans))) + { + goto Exit; + } + + // Reset records recovered to zero after dictionary pass. + + pRebuildState->CallbackData.uiRecsRecov = 0; + + // Allocate an array of structures to keep information on each + // container. + + if( RC_BAD( rc = f_calloc( + (FLMUINT)( sizeof( CONTAINER_INFO) * pDb->pDict->uiLFileCnt), + &pContainerInfo))) + { + goto Exit; + } + + if( RC_BAD( rc = bldRecovData( pDb, pRebuildState, pContainerInfo, FALSE, + &bStartedTrans))) + { + goto Exit; + } + + // Adjust the next DRN for all containers so that they are at least as high + // as the next DRN in the containers we were rebuilding from. + + for( uiCurrLf = 0, + pContInfo = pContainerInfo, + pLFile = (LFILE *)pDb->pDict->pLFileTbl; + uiCurrLf < pDb->pDict->uiLFileCnt; + uiCurrLf++, pLFile++, pContInfo++) + { + if (pLFile->uiLfType == LF_CONTAINER) + { + uiContainerNum = pLFile->uiLfNum; + if (bldDoContainer( uiContainerNum, FALSE)) + { + if (RC_BAD( rc = bldAdjustNextDrn( pDb, pLFile, pContInfo))) + { + goto Exit; + } + } + } + } + + // Preserve other things in the log header that we ought + // to try and preserve. + + if( !bBadHeader) + { + FLMBYTE * pucLogHdr = &pDb->pFile->ucUncommittedLogHdr [0]; + + // Set the commit count one less than the old database's + // This is because it will be incremented if the transaction + // successfully commits - which will make it exactly right. + + uiTemp = (FLMUINT)FB2UD( &pRebuildState->pLogHdr [LOG_COMMIT_COUNT]) - 1; + if ((FLMUINT)FB2UD( &pucLogHdr [LOG_COMMIT_COUNT]) < uiTemp) + { + UD2FBA( (FLMUINT32)uiTemp, &pucLogHdr [LOG_COMMIT_COUNT]); + } + } + + // Signal the we are finished the rebuild + + if (pRebuildState->fnStatusFunc) + { + pRebuildState->CallbackData.iDoingFlag = REBUILD_FINISHED; + + pRebuildState->CallbackData.bStartFlag = TRUE; + + if (RC_BAD( rc = (*pRebuildState->fnStatusFunc)( FLM_REBUILD_STATUS, + (void *)&pRebuildState->CallbackData, + (void *)0, + pRebuildState->AppArg))) + { + goto Exit; + } + pRebuildState->CallbackData.bStartFlag = FALSE; + } + +Exit: + + if (pContainerInfo) + { + f_free( &pContainerInfo); + } + + if (pRebuildState->pBlk) + { + f_free( &pRebuildState->pBlk); + } + + if (bStartedTrans) + { + if (rc == FERR_OK) + { + rc = flmCommitDbTrans( pDb, 0, TRUE); + } + else + { + (void)flmAbortDbTrans( pDb); + } + } + + if (bFdbInitialized) + { + fdbExit( pDb); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine adjusts the next DRN for a container so that it + is at least as high as the DRN in the file we are rebuilding + from. +****************************************************************************/ +FSTATIC RCODE bldAdjustNextDrn( + FDB * pDb, + LFILE * pLFile, + CONTAINER_INFO * pContInfo) +{ + RCODE rc; + FLMUINT uiNextDrn; + BTSK StackBuf [BH_MAX_LEVELS]; + FLMBOOL bUsedStack = FALSE; + + // First see what the next DRN is currently set to + + uiNextDrn = 0; + if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiNextDrn))) + { + goto Exit; + } + + // Adjust the next DRN to be at least as high as the highest DRN + // in the old databaseor the highest next DRN found int the + // old database. + + if( uiNextDrn < pContInfo->uiHighetstDrnFound) + { + uiNextDrn = pContInfo->uiHighetstDrnFound + 1; + } + + if( uiNextDrn < pContInfo->uiHighestNextDrnFound) + { + uiNextDrn = pContInfo->uiHighestNextDrnFound; + } + + // Add either 100 or 1000 to next record number - just in case + // things weren't as accurate as they should have been in the + // old database. We want to make sure the next record + // number is high enough to avoid accidentally reusing any + // records which may have been used in the old database. + + uiNextDrn += ((pContInfo->uiNumNextDrnsFound != 1) ? 1000 : 100); + + // If there is no root block, the next DRN is stored inside the + // logical file header. Otherwise, it is stored in the rightmost + // element of the B-Tree - the one with a DRN of DRN_LAST_MARKER. + + if( pLFile->uiRootBlk == BT_END) + { + // LFILE is up to date from previously calling FSGetNxtDrn + + pLFile->uiNextDrn = uiNextDrn; + if (RC_BAD( rc = flmLFileWrite( pDb, pLFile))) + { + goto Exit; + } + } + else + { + BTSK * pStack = StackBuf; + FLMBYTE KeyBuf [DIN_KEY_SIZ + 4]; + FLMBYTE DrnMarker [DIN_KEY_SIZ]; + FLMBYTE * pNextDrnBuf; + + // Set up the stack + + FSInitStackCache( &StackBuf [0], BH_MAX_LEVELS); + bUsedStack = TRUE; + pStack->pKeyBuf = KeyBuf; + + // Find the element whose DRN is DRN_LAST_MARKER + + flmUINT32ToBigEndian( DRN_LAST_MARKER, DrnMarker); + if( RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, DrnMarker, + DIN_KEY_SIZ, 0))) + { + goto Exit; + } + + // Log the block before modifying it + + if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) + { + goto Exit; + } + + pNextDrnBuf = CURRENT_ELM( pStack); + pNextDrnBuf += BBE_GETR_KL( pNextDrnBuf ) + BBE_KEY; + + // Update with the next DRN value and dirty the block + + UD2FBA( (FLMUINT32)uiNextDrn, pNextDrnBuf ); + } + +Exit: + + if( bUsedStack) + { + FSReleaseStackCache( StackBuf, BH_MAX_LEVELS, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine recovers all records in the database for all + containers. +*****************************************************************************/ +FSTATIC RCODE bldRecovData( + FDB * pDb, + REBUILD_STATE * pRebuildState, // Pointer to rebuild state information. + // This structure has the container + // number being recovered. + CONTAINER_INFO * pContainerInfo, // Information being remembered for + // ALL containers. + FLMBOOL bRecovDictRecs, // Flag indicating whether we are to + // doing a pass to recover dictionary + // data or regular data. + + FLMBOOL * pbStartedTransRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBlkAddress; + FLMUINT uiBytesRead; + F_SuperFileHdl * pSFileHdl = pRebuildState->pSFileHdl; + HDR_INFO * pHdrInfo = pRebuildState->pHdrInfo; + LF_HDR LogicalFile; + LF_STATS LfStats; + FLMBYTE * pucBlk = pRebuildState->pBlk; + STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; + REBUILD_INFO * pCallbackData = &pRebuildState->CallbackData; + void * AppArg = pRebuildState->AppArg; + FLMUINT uiBlockSize = pHdrInfo->FileHdr.uiBlockSize; + FLMUINT uiCurrContainerNum = 0; + LFILE * pCurrLFile = NULL; + CONTAINER_INFO * pCurrContInfo = NULL; + FLMUINT uiBlkContainerNum; + LFILE * pBlkLFile; + RECOV_DICT_INFO * pRecovDictInfo = NULL; + FLMUINT uiFileNumber = 0; + FLMUINT uiOffset = 0; + F_FileHdlImp * pFileHdl = NULL; + FLMUINT uiMaxFileSize = pRebuildState->uiMaxFileSize; + FLMUINT uiDbVersion = + pRebuildState->pHdrInfo->FileHdr.uiVersionNum; + + // Read through all blocks in the file -- looking for leaf blocks + // of containers. Read until we get an error or run out of file. + + LogicalFile.pLfStats = &LfStats; + fnStatusFunc = pRebuildState->fnStatusFunc; + pCallbackData->iDoingFlag = (FLMINT)((bRecovDictRecs) + ? (FLMINT)REBUILD_RECOVER_DICT + : (FLMINT)REBUILD_RECOVER_DATA); + pCallbackData->bStartFlag = TRUE; + pCallbackData->ui64BytesExamined = 0; + for (;;) + { + if (uiOffset >= uiMaxFileSize || !uiFileNumber) + { + uiOffset = 0; + uiFileNumber++; + if (uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)) + { + break; + } + + if (RC_BAD( rc = pSFileHdl->GetFileHdl( + uiFileNumber, FALSE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + break; + } + goto Exit; + } + } + + // Read the block into memory. + + if (RC_BAD( rc = pFileHdl->SectorRead( uiOffset, uiBlockSize, + pucBlk, &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + if (!uiBytesRead) + { + + // Set uiOffset so we will go to the next file. + + uiOffset = uiMaxFileSize; + continue; + } + else + { + rc = FERR_OK; + } + } + else + { + goto Exit; + } + } + + if (fnStatusFunc) + { + pCallbackData->ui64BytesExamined += (FLMUINT64)uiBlockSize; + if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, + (void *)pCallbackData, + (void *)0, + AppArg))) + { + goto Exit; + } + pCallbackData->bStartFlag = FALSE; + } + + f_yieldCPU(); + + uiBlkAddress = (FLMUINT)GET_BH_ADDR( pucBlk); + if ((FSGetFileOffset( uiBlkAddress) == uiOffset) && + (BH_GET_TYPE( pucBlk) == BHT_LEAF) && + (pucBlk [BH_LEVEL] == 0) && + ((uiBlkContainerNum = (FLMUINT)FB2UW( &pucBlk [BH_LOG_FILE_NUM])) != 0) && + (bldDoContainer( uiBlkContainerNum, bRecovDictRecs))) + { + if (uiBlkContainerNum != uiCurrContainerNum) + { + if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiBlkContainerNum, + &pBlkLFile))) + { + if (rc != FERR_BAD_CONTAINER) + goto Exit; + rc = FERR_OK; + goto Do_Next_Block; + } + else + { + uiCurrContainerNum = uiBlkContainerNum; + f_memset( &LogicalFile, 0, sizeof( LF_HDR)); + f_memset( &LfStats, 0, sizeof( LF_STATS)); + LogicalFile.pLfStats = &LfStats; + LogicalFile.pLFile = pCurrLFile = pBlkLFile; + if (bRecovDictRecs) + { + pCurrContInfo = NULL; + } + else + { + pCurrContInfo = + &pContainerInfo [pCurrLFile - + ((LFILE *)pDb->pDict->pLFileTbl)]; + } + pRebuildState->CorruptInfo.uiErrLfNumber = uiBlkContainerNum; + } + } + + // Estimate the number of records in the block if we did't have + // a count of records in the container. The loop ignores the + // possibility that the block may be corrupted -- we are trying + // to estimate what might have been in the block. It loops through + // the elements in the block, looking for those which are marked + // as FIRST elements. When it encounters one of these, it will + // increment the counter. + + if (pCurrContInfo && !pCurrContInfo->bCountEstimated) + { + FLMUINT uiBlkOffset; + FLMUINT uiEndOfBlock; + FLMBYTE * pucElm; + FLMUINT uiElmLen; + FLMUINT uiElmKeyLen; + FLMUINT uiElmPKCLen; + FLMUINT uiNxtBlkAddr; + FLMBOOL bIncremented; + + uiEndOfBlock = (FLMUINT)FB2UW( &pucBlk [BH_ELM_END]); + uiNxtBlkAddr = (FLMUINT)FB2UD( &pucBlk [BH_NEXT_BLK]); + + // If uiEndOfBlock is too big, adjust it down so we can + // estimate. + + if (uiEndOfBlock > uiBlockSize) + { + uiEndOfBlock = uiBlockSize; + } + uiBlkOffset = BH_OVHD; + bIncremented = FALSE; + while (uiBlkOffset < uiEndOfBlock) + { + pucElm = &pucBlk [uiBlkOffset]; + uiElmLen = (FLMUINT)(BBE_LEN( pucElm)); + uiElmKeyLen = (FLMUINT)(BBE_GET_KL( pucElm)); + uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( pucElm)); + + // If it is a FIRST element, and it is NOT the LEM + // element, increment the count. + + if ((BBE_IS_FIRST( pucElm)) && + ((uiElmLen != BBE_LEM_LEN) || + (uiElmKeyLen > 0) || + (uiElmPKCLen > 0) || + (uiBlkOffset + uiElmLen != uiEndOfBlock) || + (uiNxtBlkAddr != BT_END))) + { + pCallbackData->uiTotRecs++; + bIncremented = TRUE; + } + uiBlkOffset += uiElmLen; + } + + // Decrement the estimated count by one if it is a last + // block - one of the elements in a last block should + // always be a DRN_LAST_MARKER element. + + if ((bIncremented) && (uiNxtBlkAddr == BT_END)) + { + pCallbackData->uiTotRecs--; + } + } + + // See if we can now extract any records from the block + + uiBlkAddress = FSBlkAddress( uiFileNumber, uiOffset); + if( RC_BAD( rc = bldExtractRecs( pDb, pRebuildState, pCurrLFile, + pCurrContInfo, uiBlkAddress, + &LogicalFile, + bRecovDictRecs, + &pRecovDictInfo))) + { + goto Exit; + } + } + +Do_Next_Block: + + uiOffset += uiBlockSize; + } + + // If we are recovering dictionary records, we need to now add them + // into the appropriate dictionaries. + + if( bRecovDictRecs) + { + if( RC_BAD( rc = bldAddRecovDictRecs( pDb, pRebuildState, + &pRecovDictInfo, pbStartedTransRV))) + { + goto Exit; + } + } + +Exit: + + bldFreeRecovDictInfo( pRecovDictInfo); + return( rc); +} + +/*************************************************************************** +Desc: This routine checks a few things in the block header and then + attempts to decrypt the block so we can read through its elements. +Ret: 0 if block is OK, error code otherwise +*****************************************************************************/ +FSTATIC RCODE bldCheckBlock( + STATE_INFO * pStateInfo, + HDR_INFO * pHdrInfo, + FLMUINT uiBlkAddress, + FLMUINT uiPrevBlkAddress, + eCorruptionType * peCorruptionCode) +{ + RCODE rc = FERR_OK; + + // Determine where the end of block is -- make sure it is a legal value + + pStateInfo->uiBlkAddress = uiBlkAddress; + + // Must force the block address to be correct so this check will not + // fail. We already have previously verified that the offset matches, and + // we know that we got it from the right block file. However, the low + // byte of the block header will not be correct because until we do a + // block checksum calculation, it will hold the low checksum byte. + // We don't do a block checksum calculation during rebuild, so at this + // point, it still holds the low checksum byte. + + SET_BH_ADDR( pStateInfo->pBlk, uiBlkAddress); + if ((*peCorruptionCode = flmVerifyBlockHeader( pStateInfo, NULL, + pHdrInfo->FileHdr.uiBlockSize, + 0, uiPrevBlkAddress, FALSE, TRUE)) != FLM_NO_CORRUPTION) + { + goto Exit; + } + + pStateInfo->uiElmOffset = BH_OVHD; + + // Decrypt the block if necessary to check the elements. + // If encryption is not enabled for the database, or the block + // is already decrypted, do nothing. + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine traverses all elements within a block, extracting + whatever records it can from the block. +*****************************************************************************/ +FSTATIC RCODE bldExtractRecs( + FDB * pDb, + REBUILD_STATE * pRebuildState, + LFILE * pLFile, + CONTAINER_INFO * pContInfo, + FLMUINT uiBlkAddress, + LF_HDR * pLogicalFile, + FLMBOOL bRecovDictRecs, + RECOV_DICT_INFO ** ppRecovDictInfoRV) +{ + RCODE rc = FERR_OK; + eCorruptionType eCorruptionCode; + FLMBOOL bGotNewBlock; + FLMUINT uiSaveElmOffset; + STATE_INFO * pStateInfo = pRebuildState->pStateInfo; + FLMBOOL bStateInitialized = TRUE; + STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; + void * AppArg = pRebuildState->AppArg; + FLMBOOL bGotRecord; + + // Setup the STATE variable for processing through the block + + flmInitReadState( pStateInfo, &bStateInitialized, + pRebuildState->pHdrInfo->FileHdr.uiVersionNum, + pDb, pLogicalFile, 0xFF, BHT_LEAF, + pRebuildState->pKeyBuffer); + pStateInfo->pBlk = pRebuildState->pBlk; + + if( (RC_BAD( rc = bldCheckBlock( pStateInfo, pRebuildState->pHdrInfo, + uiBlkAddress, 0, &eCorruptionCode))) || (eCorruptionCode != FLM_NO_CORRUPTION)) + { + if( eCorruptionCode != FLM_NO_CORRUPTION) + { + (void)bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, + 0, 0, 0xFFFF, 0); + } + goto Exit; + } + + // Go through each element in the block, extracting whatever data + // we can. The loop quits if it finds an inconsistency in the block. + + bGotNewBlock = FALSE; + while ((pStateInfo->uiElmOffset < pStateInfo->uiEndOfBlock) && + (!bGotNewBlock)) + { + bGotRecord = FALSE; + if (pRebuildState->pRecord) + { + pRebuildState->pRecord->clear(); + } + uiSaveElmOffset = pStateInfo->uiElmOffset; + + // Get the element and check it + + if( (eCorruptionCode = flmVerifyElement( pStateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) + { + if( RC_BAD( rc = bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, + pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, 0xFFFF, 0))) + { + goto Exit; + } + } + + // Skip continuation elements -- at this point, it should only + // be continuation elements which are at the first of the block. + // This is because the bldGetOneRec routine will traverse through + // continuation elements for a record. + + else if ((BBE_IS_FIRST( pStateInfo->pElm)) && (pStateInfo->uiCurKeyLen)) + { + if (pStateInfo->uiElmDrn == DRN_LAST_MARKER) + { + + if (pStateInfo->uiElmRecLen == 4) + { + FLMUINT uiNxtDrn = (FLMUINT)FB2UD( pStateInfo->pElmRec); + + if (pContInfo) + { + pContInfo->uiNumNextDrnsFound++; + if (uiNxtDrn > pContInfo->uiHighestNextDrnFound) + { + pContInfo->uiHighestNextDrnFound = uiNxtDrn; + } + } + } + } + + // If the element is the FIRST element in a record, + // see if we can extract a record from it. + + else if( RC_BAD( rc = bldGetOneRec( pDb, pRebuildState, pStateInfo, + bRecovDictRecs, &bGotNewBlock, &bGotRecord))) + { + // If we didn't have enough memory to retrieve the record, just + // skip it. + + if( rc == FERR_MEM) + { + bGotRecord = FALSE; + if (pRebuildState->pRecord) + { + pRebuildState->pRecord->clear(); + } + rc = FERR_OK; + } + else + { + goto Exit; + } + } + } + + // If we didn't get a data record, there was some inconsistency + // we encountered, so we continue to the next element in the block. + + if( bGotRecord) + { + f_yieldCPU(); + + if( pContInfo) + { + if( pStateInfo->uiElmDrn > pContInfo->uiHighetstDrnFound) + { + pContInfo->uiHighetstDrnFound = pStateInfo->uiElmDrn; + } + } + + // Add the record to the database + + if( bRecovDictRecs) + { + FlmRecord * pDictRec; + FLMUINT uiDictDrn = 0; + FLMBOOL bGotFromDataRec; + CHK_RECORD ChkRec; + + f_memset( &ChkRec, 0, sizeof( ChkRec)); + + // If this is not a dictionary container record, need to do the + // callback to see if there is dictionary information in this + // record. + + if( pLFile->uiLfNum == FLM_DICT_CONTAINER) + { + pDictRec = pRebuildState->pRecord; + uiDictDrn = pStateInfo->uiElmDrn; + bGotFromDataRec = FALSE; + } + else + { + if( !fnStatusFunc) + { + pDictRec = NULL; + } + else + { + ChkRec.pRecord = pRebuildState->pRecord; + ChkRec.uiContainer = pLFile->uiLfNum; + ChkRec.uiDrn = pStateInfo->uiElmDrn; + if( RC_BAD( rc = (*fnStatusFunc)( FLM_CHECK_RECORD_STATUS, + (void *)&ChkRec, + (void *)0, + AppArg))) + { + if( ChkRec.pDictRecSet) + { + ChkRec.pDictRecSet->Release(); + ChkRec.pDictRecSet = NULL; + } + goto Exit; + } + + if( ChkRec.pDictRecSet) + { + if( (pDictRec = ChkRec.pDictRecSet->first()) != NULL) + { + uiDictDrn = pDictRec->getID(); + } + } + else + { + pDictRec = NULL; + } + bGotFromDataRec = TRUE; + } + } + + if( !pDictRec) + { + rc = FERR_OK; + } + else + { + for (;;) + { + rc = bldSaveRecovDictRec( pDb, + ppRecovDictInfoRV, pDictRec, + uiDictDrn, bGotFromDataRec, uiBlkAddress, + uiSaveElmOffset); + + if( RC_BAD( rc) || !bGotFromDataRec) + { + break; + } + + // If bGotFromDataRec is TRUE, there may be more than + // one that was returned. + + if( (pDictRec = ChkRec.pDictRecSet->next()) == NULL) + { + break; + } + + uiDictDrn = pDictRec->getID(); + } + } + + if( ChkRec.pDictRecSet) + { + ChkRec.pDictRecSet->Release(); + ChkRec.pDictRecSet = NULL; + } + } + else if( pLFile->uiLfNum == FLM_TRACKER_CONTAINER) + { + rc = FSRecUpdate( pDb, pLFile, + pRebuildState->pRecord, pStateInfo->uiElmDrn, + REC_UPD_ADD); + } + else + { + rc = flmAddRecord( pDb, pLFile, &pStateInfo->uiElmDrn, + pRebuildState->pRecord, TRUE, + FALSE, FALSE, FALSE, NULL); + + if( RC_OK(rc) && fnStatusFunc ) + { + CHK_RECORD ChkRec; + + f_memset( &ChkRec, 0, sizeof( ChkRec)); + ChkRec.pRecord = pRebuildState->pRecord; + ChkRec.uiContainer = pLFile->uiLfNum; + ChkRec.uiDrn = pStateInfo->uiElmDrn; + rc = (*fnStatusFunc)( FLM_EXAMINE_RECORD_STATUS, + (void *)&ChkRec, + (void *)0, + AppArg); + if (ChkRec.pDictRecSet) + { + ChkRec.pDictRecSet->Release(); + ChkRec.pDictRecSet = NULL; + } + } + + if( pRebuildState->pRecord->isReadOnly()) + { + pRebuildState->pRecord->Release(); + pRebuildState->pRecord = NULL; + } + } + + if( RC_BAD( rc)) + { + if( (rc == FERR_EXISTS) || (rc == FERR_NOT_UNIQUE)) + { + eCorruptionCode = (rc == FERR_EXISTS) + ? FLM_REBUILD_REC_EXISTS + : FLM_REBUILD_KEY_NOT_UNIQUE; + if (RC_BAD( rc = bldReportReason( pRebuildState, + eCorruptionCode, uiBlkAddress, + uiSaveElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + // Make sure the tempory memory is freed. + // Eats up the memory during a rebuild. + + GedPoolReset( &pDb->TempPool, NULL); + if (!bRecovDictRecs) + { + pRebuildState->CallbackData.uiRecsRecov++; + } + } + } + + pStateInfo->uiElmOffset += pStateInfo->uiElmLen; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine gets the next element for a record. If necessary, it + will try to go to the next block. +Ret: TRUE if we got the next element, FALSE otherwise. +*****************************************************************************/ +FSTATIC RCODE bldGetNextElm( + REBUILD_STATE * pRebuildState, + STATE_INFO * pStateInfo, + FLMBOOL * pbGotNextElmRV, + FLMBOOL * pbGotNewBlockRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiSaveDrn; + FLMUINT uiBytesRead; + FLMUINT uiBlkAddress; + FLMBYTE * pBlk = pStateInfo->pBlk; + HDR_INFO * pHdrInfo = pRebuildState->pHdrInfo; + eCorruptionType eCorruptionCode; + FLMUINT uiSaveBlkAddress = pStateInfo->uiBlkAddress; + + *pbGotNextElmRV = FALSE; + uiSaveDrn = pStateInfo->uiElmDrn; + + // See if we need to go to the next block to get the element + + pStateInfo->uiElmOffset += pStateInfo->uiElmLen; + if (pStateInfo->uiElmOffset >= pStateInfo->uiEndOfBlock) + { + // Get the next block + + *pbGotNewBlockRV = TRUE; + uiBlkAddress = (FLMUINT)FB2UD( &pBlk [BH_NEXT_BLK]); + rc = pRebuildState->pSFileHdl->ReadBlock( uiBlkAddress, + pHdrInfo->FileHdr.uiBlockSize, + pBlk, &uiBytesRead); + if( uiBytesRead < pHdrInfo->FileHdr.uiBlockSize) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + } + + if( RC_BAD( rc)) + { + RCODE TempRc; + + if( rc == FERR_IO_END_OF_FILE || + rc == FERR_IO_PATH_NOT_FOUND || + rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + + if( RC_BAD( TempRc = bldReportReason( pRebuildState, + FLM_BAD_BLK_HDR_NEXT, uiSaveBlkAddress, + 0, 0, 0xFFFF, 0))) + { + if( RC_OK( rc)) + { + rc = TempRc; + } + } + + goto Exit; + } + + // Make sure it is the right type of block + + else if( (RC_BAD( rc = bldCheckBlock( pStateInfo, pRebuildState->pHdrInfo, + uiBlkAddress, uiSaveBlkAddress, &eCorruptionCode))) || (eCorruptionCode != FLM_NO_CORRUPTION)) + { + if (eCorruptionCode != FLM_NO_CORRUPTION) + { + (void)bldReportReason( pRebuildState, eCorruptionCode, + uiBlkAddress, 0, 0, + 0xFFFF, 0); + } + + goto Exit; + } + } + + // Verify other things about the element + + if( (eCorruptionCode = flmVerifyElement( pStateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) + { + rc = bldReportReason( pRebuildState, eCorruptionCode, + pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, 0xFFFF, 0); + goto Exit; + } + + // This had better not be a LEM element + + if( pStateInfo->uiCurKeyLen == 0) + { + rc = bldReportReason( pRebuildState, FLM_BAD_LEM, + pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, 0xFFFF, 0); + goto Exit; + } + + // This element had better not be the first element + + if( BBE_IS_FIRST( pStateInfo->pElm)) + { + rc = bldReportReason( pRebuildState, FLM_BAD_FIRST_ELM_FLAG, + pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, 0xFFFF, 0); + goto Exit; + } + + // Must stay on the same DRN while processing the record + + if (pStateInfo->uiElmDrn != uiSaveDrn) + { + rc = bldReportReason( pRebuildState, FLM_BAD_CONT_ELM_KEY, + pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->uiElmDrn, 0xFFFF, 0); + goto Exit; + } + + *pbGotNextElmRV = TRUE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine retrieves one record from a block -- at the current + element. It will follow continuation elements if necessary. The + record is returned in pRebuildState->pRecord. A NULL is returned if some + inconsistency was encountered. +*****************************************************************************/ +FSTATIC RCODE bldGetOneRec( + FDB * pDb, + REBUILD_STATE * pRebuildState, + STATE_INFO * pStateInfo, + FLMBOOL bRecovDictRecs, + FLMBOOL * pbGotNewBlockRV, + FLMBOOL * pbGotRecord) +{ + RCODE rc = FERR_OK; + FLMBYTE * pValue; + FLMBYTE * pData; + FLMBYTE * pTempValue; + eCorruptionType eCorruptionCode; + FlmRecord * pRecord = NULL; + FLMBOOL bAllocatedRecord = FALSE; + FLMBOOL bFieldDone; + FLMUINT uiSaveElmRecOffset; + FLMBOOL bGotNextElm; + FLMBOOL bSkipField = FALSE; + FLMBOOL bSkippedField = FALSE; + FLMUINT uiSkipToLevel = 0; + + // Setup things to get the record + + if( pRebuildState->pRecord) + { + pRebuildState->pRecord->clear(); + pRecord = pRebuildState->pRecord; + } + + pValue = NULL; + pTempValue = NULL; + *pbGotRecord = FALSE; + + // Follow elements until we have traversed all continuation elements + // or until we discover some inconsistency. + + for (;;) + { + uiSaveElmRecOffset = pStateInfo->uiElmRecOffset; + if ((eCorruptionCode = flmVerifyElmFOP( pStateInfo)) != FLM_NO_CORRUPTION) + { + if ((bRecovDictRecs) && (eCorruptionCode == FLM_BAD_ELM_FLD_NUM)) + { + bSkipField = TRUE; + bSkippedField = TRUE; + uiSkipToLevel = pStateInfo->uiFieldLevel; + bFieldDone = + (pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) + ? TRUE + : FALSE; + } + else + { + rc = bldReportReason( pRebuildState, eCorruptionCode, pStateInfo->uiBlkAddress, + pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, + uiSaveElmRecOffset, pStateInfo->uiFieldNum); + goto Exit; + } + } + + // See if we are starting a new field + + if( (pStateInfo->uiFOPType == FLM_FOP_STANDARD) || + (pStateInfo->uiFOPType == FLM_FOP_OPEN) || + (pStateInfo->uiFOPType == FLM_FOP_TAGGED) || + (pStateInfo->uiFOPType == FLM_FOP_NO_VALUE) || + (pStateInfo->uiFOPType == FLM_FOP_ENCRYPTED)) + { + // If we skipped a previous field, see if this field is a child + // or grandchild, etc. of the field that was skipped. If so, we skip + // this field as well. We stop skipping when we come to a field + // that is a sibling or aunt/uncle to the field that was skipped. + + if( (bSkipField) && + (pStateInfo->uiFieldLevel <= uiSkipToLevel)) + { + bSkipField = FALSE; + } + + // See if field should be skipped. + + if( !bSkipField) + { + FLMUINT uiState; + FLMUINT uiDictFieldType; + + if( RC_BAD( fdictGetField( pDb->pDict, pStateInfo->uiFieldNum, + &uiDictFieldType, NULL, &uiState))) + { + bSkipField = TRUE; + bSkippedField = TRUE; + uiSkipToLevel = pStateInfo->uiFieldLevel; + } + } + + // If we aren't skipping the field, allocate space for it + + if( !bSkipField) + { + void * pvField; + + if( !pRecord) + { + if( (pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + bAllocatedRecord = TRUE; + } + + // Create a new field in the record. + + if( RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel, + pStateInfo->uiFieldNum, + pStateInfo->uiFieldType, &pvField))) + { + goto Exit; + } + + pStateInfo->pvField = pvField; + + // Allocate space for the field's value and set the field's type. + + if( !pStateInfo->uiFieldLen) + { + pValue = NULL; + } + else + { + if (!pStateInfo->uiEncId) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, + pStateInfo->uiFieldType, + pStateInfo->uiFieldLen, + 0, + 0, + 0, + &pValue, + NULL))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, + pStateInfo->uiFieldType, + pStateInfo->uiFieldLen, + pStateInfo->uiEncFieldLen, + pStateInfo->uiEncId, + FLD_HAVE_ENCRYPTED_DATA, + &pData, + &pValue))) + { + goto Exit; + } + } + } + pTempValue = pValue; + } + bFieldDone = + ((!pStateInfo->uiEncId && + pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) || + (pStateInfo->uiEncId && + pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen)) + ? TRUE + : FALSE; + } + else if( (pStateInfo->uiFOPType == FLM_FOP_JUMP_LEVEL) || + (pStateInfo->uiFOPType == FLM_FOP_NEXT_DRN)) + { + bFieldDone = FALSE; + } + else + { + bFieldDone = + ((!pStateInfo->uiEncId && + pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) || + (pStateInfo->uiEncId && + pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen)) + ? TRUE + : FALSE; + } + + // See if we got some data with this FOP + + if( pValue && + pStateInfo->uiFOPDataLen && + !bSkipField && + pStateInfo->uiFOPType != FLM_FOP_REC_INFO) + { + f_memcpy( pTempValue, + pStateInfo->pFOPData, pStateInfo->uiFOPDataLen); + pTempValue += pStateInfo->uiFOPDataLen; + } + + // See if we are done with this field + + if( bFieldDone) + { + // Verify the value + + if( pStateInfo->uiFieldLen) + { + if( bSkipField || pStateInfo->uiFieldType == 0xFF) + { + eCorruptionCode = FLM_NO_CORRUPTION; + } + else + { + if (!pStateInfo->uiEncId) + { + eCorruptionCode = flmVerifyField( pValue, + pStateInfo->uiFieldLen, + pStateInfo->uiFieldType); + } + + // Encrypted fields are never supposed to be found in the dictionary, so + // if we do not have an ITT table, we will not be able to decrypt this field. + // Therefore, we should be in our first pass. This field / record will + // not be used to recover the dictionary. + + else if (pDb->pFile->pDictList->pIttTbl) + { + if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, + pStateInfo->pvField, pStateInfo->uiEncId, + &pDb->TempPool))) + { + goto Exit; + } + eCorruptionCode = flmVerifyField( pData, + pStateInfo->uiFieldLen, + pStateInfo->uiFieldType); + } + } + + if( eCorruptionCode != FLM_NO_CORRUPTION) + { + rc = bldReportReason( pRebuildState, eCorruptionCode, + pStateInfo->uiBlkAddress, + pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, + uiSaveElmRecOffset, + pStateInfo->uiFieldNum); + goto Exit; + } + } + + // Set pValue to NULL for the next field + + pValue = pTempValue = NULL; + } + + // See if we are at the end of this element + + if( pStateInfo->uiElmRecOffset == pStateInfo->uiElmRecLen) + { + // If the last element flag is set, we are done with this + // record and can return - unless we have a half processed field. + + if( BBE_IS_LAST( pStateInfo->pElm)) + { + if (!bFieldDone) + { + rc = bldReportReason( pRebuildState, FLM_BAD_LAST_ELM_FLAG, + pStateInfo->uiBlkAddress, + pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, + uiSaveElmRecOffset, + pStateInfo->uiFieldNum); + } + else + { + pRebuildState->pRecord = pRecord; + *pbGotRecord = TRUE; + pRecord = NULL; + bAllocatedRecord = FALSE; + } + + goto Exit; + } + else + { + // Attempt to get the next element + + if( (RC_BAD( rc = bldGetNextElm( pRebuildState, pStateInfo, + &bGotNextElm, pbGotNewBlockRV))) || !bGotNextElm) + { + // Need to set bSkippedField to TRUE so that cleanup will + // occur at the bottom of this routine. + + bSkippedField = TRUE; + goto Exit; + } + } + } + } + +Exit: + + if( bSkippedField || RC_BAD( rc)) + { + if (pRebuildState->pRecord) + { + pRebuildState->pRecord->Release(); + pRebuildState->pRecord = NULL; + } + + *pbGotRecord = FALSE; + } + + if( pRecord && bAllocatedRecord) + { + pRecord->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine recovers any dictionary records from the corrupted file + that it can recover, attempting to find any that may not have been + in the dictionary file that was passed into the rebuild routines. +*****************************************************************************/ +FSTATIC RCODE bldSaveRecovDictRec( + FDB * pDb, // FDB for newly created database + RECOV_DICT_INFO ** ppRecovDictInfoRV, // Recover info + FlmRecord * pRecord, // Dictionary Record + FLMUINT uiDrn, // Dict. record DRN + FLMBOOL bGotFromDataRec, // Was this dictionary record + // extracted from a data record? + FLMUINT uiBlkAddress, // Block the record was + // recovered from + FLMUINT uiElmOffset // Offset in block the + // record was recovered + // from + ) +{ + RCODE rc = FERR_OK; + RECOV_DICT_INFO * pRecovDictInfo; + RECOV_DICT_REC * pRecovDictRec; + RECOV_DICT_REC * pNewDictRec = NULL; + RECOV_DICT_REC * pPrevDictRec; + FLMUINT uiRecType = pRecord->getFieldID( pRecord->root()); + FlmRecord * pDummyRec; + LFILE * pDictLFile; + + // Ignore any record that is not a dictionary record or + // an unregistered (comment) record. + + if( uiRecType < FLM_RESERVED_TAG_NUMS) + { + goto Exit; + } + + // Determine if the record already exists in the dictionary. If it + // does, simply ignore this record - the record that was loaded from + // the dictionary file takes precedence. + + if( RC_OK( rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, + &pDictLFile))) + { + pDummyRec = NULL; + rc = FSReadRecord( pDb, pDictLFile, uiDrn, + &pDummyRec, NULL, NULL); + if( pDummyRec) + { + pDummyRec->Release(); + } + } + + if( rc != FERR_NOT_FOUND) + { + goto Exit; + } + + rc = FERR_OK; + + // If the dictionary was not found in the list, create an entry in the + // list. + + if( (pRecovDictInfo = *ppRecovDictInfoRV) == NULL) + { + if( RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( RECOV_DICT_INFO), &pRecovDictInfo))) + { + goto Exit; + } + + GedPoolInit( &pRecovDictInfo->pool, 512); + *ppRecovDictInfoRV = pRecovDictInfo; + } + + // Determine if the record is already in our list of records. If so, + // simply ignore it, or remove the old one. The old one is removed if + // it is "less desirable to recover". Desirability of record types is + // as follows: + // + // 1. Field Definitions && Template definitions take top priority + // 2. Container Definitions + // 3. Area Definitions + // 4. Reserve Definitions + // 5. Index Definitions + // 6. Other + + pPrevDictRec = NULL; + pRecovDictRec = pRecovDictInfo->pRecovRecs; + + while( pRecovDictRec) + { + if( pRecovDictRec->pRec->getID() != uiDrn) + { + pPrevDictRec = pRecovDictRec; + pRecovDictRec = pRecovDictRec->pNext; + } + else if( bGotFromDataRec && !pRecovDictRec->bGotFromDataRec) + { + // Throw away this record and keep the one that was previously + // recovered from the dictionary container. Records recovered + // from the dictionary container are preferred to ones that + // were extracted from a data record. + + goto Exit; + } + else if( !bGotFromDataRec && pRecovDictRec->bGotFromDataRec) + { + // Throw away prior dictionary record that was recovered, because + // we got it from a data record. This newer one was actually found + // in the dictionary, so we will keep it in preference to the + // earlier one that we found in a data record. + + goto Remove_Rec; + } + else + { + switch (pRecovDictRec->pRec->getFieldID( pRecovDictRec->pRec->root())) + { + case FLM_FIELD_TAG: + goto Exit; + + case FLM_CONTAINER_TAG: + if( uiRecType == FLM_FIELD_TAG) + { +Remove_Rec: + if (pPrevDictRec) + { + pPrevDictRec->pNext = pRecovDictRec->pNext; + } + else + { + pRecovDictInfo->pRecovRecs = pRecovDictRec->pNext; + } + + // Might as well use the old structure for the new record, + // so we don't have to allocate it below. + + pNewDictRec = pRecovDictRec; + + if( pRecovDictRec->pRec) + { + pRecovDictRec->pRec->Release(); + pRecovDictRec->pRec = NULL; + } + + f_memset( pNewDictRec, 0, sizeof( RECOV_DICT_REC)); + } + else + { + goto Exit; + } + break; + + case FLM_AREA_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG) + { + goto Remove_Rec; + } + else + { + goto Exit; + } + + case FLM_RESERVED_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG) + { + goto Remove_Rec; + } + else + { + goto Exit; + } + + case FLM_INDEX_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG || + uiRecType == FLM_RESERVED_TAG) + { + goto Remove_Rec; + } + else + { + goto Exit; + } + + default: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG || + uiRecType == FLM_RESERVED_TAG || + uiRecType == FLM_INDEX_TAG) + { + goto Remove_Rec; + } + else + { + goto Exit; + } + } + break; + } + } + + // Link the record into the list of records that need to be + // recovered. We don't add it right away, because we want to be sure + // and do them in a certain order, as follows: + // + // 1. Field definitions + // 2. Container definitions + // 3. Area definitions + // 4. Template definitions + // 5. Index definitions + // 6. Reserve definitions + // 7. Other + + if( !pNewDictRec) + { + // All elements of pNewDictRec are initialized below. + + if( (pNewDictRec = (RECOV_DICT_REC *)GedPoolAlloc( + &pRecovDictInfo->pool, sizeof( RECOV_DICT_REC))) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pNewDictRec->pNext = NULL; + pNewDictRec->bAdded = FALSE; + } + + if( (pNewDictRec->pRec = pRecord->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + } + + pNewDictRec->bGotFromDataRec = bGotFromDataRec; + pNewDictRec->pRec->setID( uiDrn); + pNewDictRec->uiBlkAddress = uiBlkAddress; + pNewDictRec->uiElmOffset = uiElmOffset; + pPrevDictRec = NULL; + pRecovDictRec = pRecovDictInfo->pRecovRecs; + + while( pRecovDictRec) + { + switch( pRecovDictRec->pRec->getFieldID( pRecovDictRec->pRec->root())) + { + case FLM_FIELD_TAG: + break; + + case FLM_CONTAINER_TAG: + if( uiRecType == FLM_FIELD_TAG) + { + goto Insert_Rec; + } + break; + + case FLM_AREA_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG) + { + goto Insert_Rec; + } + break; + + case FLM_INDEX_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG) + { + goto Insert_Rec; + } + break; + + case FLM_RESERVED_TAG: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG || + uiRecType == FLM_INDEX_TAG) + { + goto Insert_Rec; + } + break; + + default: + if( uiRecType == FLM_FIELD_TAG || + uiRecType == FLM_CONTAINER_TAG || + uiRecType == FLM_AREA_TAG || + uiRecType == FLM_INDEX_TAG || + uiRecType == FLM_RESERVED_TAG) + { + goto Insert_Rec; + } + break; + } + + pPrevDictRec = pRecovDictRec; + pRecovDictRec = pRecovDictRec->pNext; + } + +Insert_Rec: + + pNewDictRec->pNext = pRecovDictRec; + if( pPrevDictRec) + { + pPrevDictRec->pNext = pNewDictRec; + } + else + { + pRecovDictInfo->pRecovRecs = pNewDictRec; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine frees all of the recovery dictionary information. +*****************************************************************************/ +FSTATIC void bldFreeRecovDictInfo( + RECOV_DICT_INFO * pRecovDictInfo) +{ + if( pRecovDictInfo) + { + RECOV_DICT_REC * pDictRec; + + pDictRec = pRecovDictInfo->pRecovRecs; + while (pDictRec) + { + if (pDictRec->pRec) + { + pDictRec->pRec->Release(); + pDictRec->pRec = NULL; + } + pDictRec = pDictRec->pNext; + } + + GedPoolFree( &pRecovDictInfo->pool); + f_free( &pRecovDictInfo); + } +} + +/*************************************************************************** +Desc: This routine adds all of the recovered dictionary records for a + specific dictionary. It makes sure to do parent dictionaries first. +*****************************************************************************/ +FSTATIC RCODE bldDoDict( + FDB * pDb, + REBUILD_STATE * pRebuildState, + RECOV_DICT_INFO * pDictToDo, + FLMBOOL * pbStartedTransRV) +{ + RCODE rc = FERR_OK; + RECOV_DICT_REC * pDictRec; + RECOV_DICT_REC * pFirstDictRecInTrans = NULL; + LFILE * pDictLFile; + STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; + REBUILD_INFO * pCallbackData = &pRebuildState->CallbackData; + FLMBOOL bHaveLFile; + FLMBOOL bAddedAtLeastOne; + FLMBOOL bFailedAtLeastOne; + FlmRecord * pSaveRec; + FLMUINT uiRecordsPerTrans; + FLMUINT uiRecordInTrans; + FLMUINT uiDictRecId; + + // Commit the current update transaction + + if( *pbStartedTransRV) + { + *pbStartedTransRV = FALSE; + if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) + { + goto Exit; + } + } + + // Add all of the records for pDictToDo - one per transaction. + + bHaveLFile = FALSE; + uiRecordsPerTrans = 100; + +Do_Dict_Recs: + + pDictRec = pDictToDo->pRecovRecs; + bAddedAtLeastOne = FALSE; + bFailedAtLeastOne = FALSE; + uiRecordInTrans = 0; + + while (pDictRec) + { + if (!pDictRec->bAdded) + { + if( !uiRecordInTrans) + { + // Start an update transaction + + if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 5))) + { + goto Exit; + } + + *pbStartedTransRV = TRUE; + pFirstDictRecInTrans = pDictRec; + } + uiRecordInTrans++; + + // Don't optimize and keep pDictLFile as a local variable! + + if( RC_BAD( rc = fdictGetContainer( + pDb->pDict, FLM_DICT_CONTAINER, &pDictLFile))) + { + rc = FERR_OK; + goto Exit; + } + bHaveLFile = TRUE; + + // Call the status callback to give the application a chance + // to change the definition record + + if( fnStatusFunc) + { + if( RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_ADD_DICT_REC_STATUS, + (void *)pDictRec->pRec, + (void *)0, + pRebuildState->AppArg))) + { + goto Exit; + } + } + + // Add the dictionary record. + // NOTE: We are deliberately ignoring return codes here - so we + // can attempt to process as many records as possible. + + uiDictRecId = pDictRec->pRec->getID(); + + if (RC_BAD( flmLFileDictUpdate( pDb, pDictLFile, + &uiDictRecId, pDictRec->pRec, + NULL, FALSE, FALSE, NULL, TRUE))) + { + *pbStartedTransRV = FALSE; + (void)flmAbortDbTrans( pDb); + bFailedAtLeastOne = TRUE; + uiRecordInTrans = 0; + } + else if( (uiRecordInTrans >= uiRecordsPerTrans) || !pDictRec->pNext) + { + *pbStartedTransRV = FALSE; + if (RC_BAD( flmCommitDbTrans( pDb, 0, FALSE))) + { + bFailedAtLeastOne = TRUE; + } + else + { + // Set bAdded to TRUE on all dict recs in transaction. + + while( pFirstDictRecInTrans != pDictRec) + { + pFirstDictRecInTrans->bAdded = TRUE; + pFirstDictRecInTrans = pFirstDictRecInTrans->pNext; + } + + pDictRec->bAdded = bAddedAtLeastOne = TRUE; + pRebuildState->CallbackData.uiRecsRecov += uiRecordInTrans; + + if( fnStatusFunc) + { + if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, + (void *)pCallbackData, + (void *)0, + pRebuildState->AppArg))) + { + goto Exit; + } + } + + f_yieldCPU(); + } + + uiRecordInTrans = 0; + } + } + + pDictRec = pDictRec->pNext; + } + + // Retry the add loop until either they all FAIL or they all succeed. + // This is done to handle record template dependencies - or other + // dependencies for that matter - that may not have been in the proper + // order in the list. + + if( (bAddedAtLeastOne || uiRecordsPerTrans > 1) && bFailedAtLeastOne) + { + // We MUST do single record transactions from this point on + // so the commit above will be called if the last pDictRec + // was added on any prior pass. + + uiRecordsPerTrans = 1; + goto Do_Dict_Recs; + } + + // Log the records that failed + + pDictRec = pDictToDo->pRecovRecs; + pSaveRec = pRebuildState->pRecord; + + while (pDictRec) + { + if (!pDictRec->bAdded) + { + RCODE TempRc; + + pRebuildState->pRecord = pDictRec->pRec; + if (RC_BAD( TempRc = bldReportReason( pRebuildState, + FLM_DICT_REC_ADD_ERR, + pDictRec->uiBlkAddress, pDictRec->uiElmOffset, + pDictRec->pRec->getID(), 0xFFFF, 0))) + { + rc = TempRc; + } + } + pDictRec = pDictRec->pNext; + } + pRebuildState->pRecord = pSaveRec; + +Exit: + + bldFreeRecovDictInfo( pDictToDo); + if ((RC_OK( rc)) && (!(*pbStartedTransRV))) + { + if (RC_OK( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 5))) + { + *pbStartedTransRV = TRUE; + } + } + + return( rc); +} +/**************************************************************************** +Desc: Extract create options from file header and log header pieces. +****************************************************************************/ +void flmGetCreateOpts( + FILE_HDR * pFileHdr, + FLMBYTE * pucLogHdr, + CREATE_OPTS * pCreateOpts) +{ + f_memset( pCreateOpts, 0, sizeof( CREATE_OPTS)); + if (pFileHdr) + { + pCreateOpts->uiBlockSize = pFileHdr->uiBlockSize; + pCreateOpts->uiVersionNum = pFileHdr->uiVersionNum; + pCreateOpts->uiDefaultLanguage = pFileHdr->uiDefaultLanguage; + pCreateOpts->uiAppMajorVer = pFileHdr->uiAppMajorVer; + pCreateOpts->uiAppMinorVer = pFileHdr->uiAppMinorVer; + } + else + { + pCreateOpts->uiBlockSize = DEFAULT_BLKSIZ; + pCreateOpts->uiVersionNum = FLM_CUR_FILE_FORMAT_VER_NUM; + pCreateOpts->uiDefaultLanguage = DEFAULT_LANG; + + // uiAppMajorVer and uiAppMinorVer are already zero. + } + + if (pucLogHdr) + { + pCreateOpts->uiMinRflFileSize = + (FLMUINT)FB2UD( &pucLogHdr [LOG_RFL_MIN_FILE_SIZE]); + pCreateOpts->uiMaxRflFileSize = + (FLMUINT)FB2UD( &pucLogHdr [LOG_RFL_MAX_FILE_SIZE]); + pCreateOpts->bKeepRflFiles = + (FLMBOOL)((pucLogHdr [LOG_KEEP_RFL_FILES]) ? TRUE : FALSE); + pCreateOpts->bLogAbortedTransToRfl = + (FLMBOOL)((pucLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL]) ? TRUE : FALSE); + } + else + { + pCreateOpts->uiMinRflFileSize = DEFAULT_MIN_RFL_FILE_SIZE; + pCreateOpts->uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE; + pCreateOpts->bKeepRflFiles = DEFAULT_KEEP_RFL_FILES_FLAG; + pCreateOpts->bLogAbortedTransToRfl = DEFAULT_LOG_ABORTED_TRANS_FLAG; + } +} + +/**************************************************************************** +Desc: Rebuilds a damaged database. +Notes: This routine performs the following actions: 1) A temporary database + is created; 2) A copy of the source database is saved; 3) The source + database is scanned. Data records from all containers are extracted + and stored in the temporary database. 4) When the rebuild is + complete, the temporary database file is copied over the source + database file. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbRebuild( + const char * pszSourceDbPath, + const char * pszSourceDataDir, + const char * pszDestDbPath, + const char * pszDestDataDir, + const char * pszDestRflDir, + const char * pszDictPath, + CREATE_OPTS * pCreateOpts, + FLMUINT * puiTotRecsRV, + FLMUINT * puiRecsRecovRV, + STATUS_HOOK fnStatusFunc, + void * pvStatusData) +{ + RCODE rc = FERR_OK; + FDB * pDb = NULL; + FFILE * pFile; + F_SuperFileHdl * pSFileHdl = NULL; + FLMBOOL bFileLocked = FALSE; + FLMBOOL bWriteLocked = FALSE; + REBUILD_STATE * pRebuildState = NULL; + HDR_INFO * pHdrInfo; + CREATE_OPTS * pDefaultCreateOpts = NULL; + FLMUINT uiTransID; + ServerLockObject * pWriteLockObj = NULL; + ServerLockObject * pFileLockObj = NULL; + FLMBOOL bMutexLocked = FALSE; + F_FileHdlImp * pLockFileHdl = NULL; + FLOCK_INFO LockInfo; + FLMUINT uiFileNumber; + FLMUINT uiDbVersion = 0; + FLMBOOL bUsedFFile = FALSE; + FLMBOOL bBadHeader = FALSE; + FlmECache * pECacheMgr = NULL; + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // See if there is an FFILE structure for this file + // May unlock and re-lock the global mutex. + + if( RC_BAD( rc = flmFindFile( pszSourceDbPath, pszSourceDataDir, + &pFile))) + { + goto Exit; + } + + // If we didn't find an FFILE structure, get an + // exclusive lock on the file. + + if( !pFile) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Attempt to get an exclusive lock on the file. + + if( RC_BAD( rc = flmCreateLckFile( pszSourceDbPath, &pLockFileHdl))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmCheckFFileState( pFile))) + { + goto Exit; + } + + // The call to flmVerifyFileUse will wait if the file is in + // the process of being opened by another thread. + + if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile))) + { + goto Exit; + } + + // Increment the use count on the FFILE so it will not + // disappear while we are copying the file. + + if (++pFile->uiUseCount == 1) + { + flmUnlinkFileFromNUList( pFile); + } + bUsedFFile = TRUE; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // See if the thread already has a file lock. If so, there + // is no need to obtain another. However, we also want to + // make sure there is no write lock. If there is, + // we cannot do the rebuild right now. + + pFile->pFileLockObj->GetLockInfo( (FLMINT)0, &LockInfo); + if (LockInfo.eCurrLockType == FLM_LOCK_EXCLUSIVE && + LockInfo.uiThreadId == f_threadId()) + { + + // See if there is already a transaction going. + + pFile->pWriteLockObj->GetLockInfo( (FLMINT)0, &LockInfo); + if ((LockInfo.eCurrLockType == FLM_LOCK_EXCLUSIVE) && + (LockInfo.uiThreadId == f_threadId())) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + } + else + { + pFileLockObj = pFile->pFileLockObj; + pFileLockObj->AddRef(); + if (RC_BAD( rc = pFileLockObj->Lock( TRUE, NULL, FALSE, TRUE, + FLM_NO_TIMEOUT, 0))) + { + goto Exit; + } + bFileLocked = TRUE; + } + + // Lock the write object to eliminate contention with + // the checkpoint thread. + + pWriteLockObj = pFile->pWriteLockObj; + pWriteLockObj->AddRef(); + + // Only contention here is with the checkpoint thread. + // Wait forever for the checkpoint thread to give + // up the lock. + + if (RC_BAD( rc = dbWriteLock( pFile))) + { + goto Exit; + } + bWriteLocked = TRUE; + } + + if( RC_BAD( rc = f_calloc( + sizeof( REBUILD_STATE), &pRebuildState))) + { + goto Exit; + } + + if( RC_BAD( rc = f_calloc( + sizeof( HDR_INFO), &pRebuildState->pHdrInfo))) + { + goto Exit; + } + + if( RC_BAD( rc = f_calloc( + sizeof( STATE_INFO), &pRebuildState->pStateInfo))) + { + goto Exit; + } + + if( RC_BAD( rc = f_calloc( + sizeof( CREATE_OPTS), &pDefaultCreateOpts))) + { + goto Exit; + } + + if( RC_BAD( rc = f_calloc( + LOG_HEADER_SIZE, &pRebuildState->pLogHdr))) + { + goto Exit; + } + + if( RC_BAD( rc = f_calloc( + MAX_KEY_SIZ, &pRebuildState->pKeyBuffer))) + { + goto Exit; + } + + pRebuildState->AppArg = pvStatusData; + pRebuildState->fnStatusFunc = fnStatusFunc; + pHdrInfo = pRebuildState->pHdrInfo; + + /* Open the corrupted database. */ + + if ((pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSFileHdl->Setup( NULL, pszSourceDbPath, + pszSourceDataDir))) + { + goto Exit; + } + + pRebuildState->pSFileHdl = pSFileHdl; + + /* + Check the header information to see if we were in the middle + of a previous copy. + */ + + if (RC_OK( rc = flmGetHdrInfo( pSFileHdl, + &pHdrInfo->FileHdr, + &pHdrInfo->LogHdr, + pRebuildState->pLogHdr))) + { + + if (!pCreateOpts) + { + flmGetCreateOpts( &pHdrInfo->FileHdr, + pRebuildState->pLogHdr, pDefaultCreateOpts); + pCreateOpts = pDefaultCreateOpts; + } + rc = FERR_OK; + uiDbVersion = pHdrInfo->FileHdr.uiVersionNum; + pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, + pRebuildState->pLogHdr); + } + else if ((rc == FERR_BLOCK_CHECKSUM) || + (rc == FERR_INCOMPLETE_LOG) || + (rc == FERR_DATA_ERROR) || + ((rc == FERR_UNSUPPORTED_VERSION) && + (pHdrInfo->FileHdr.uiVersionNum == 0))) + { + if ((rc == FERR_BLOCK_CHECKSUM) || + (rc == FERR_DATA_ERROR)) + { + bBadHeader = TRUE; + } + rc = FERR_OK; + if (!pCreateOpts) + { + flmGetCreateOpts( &pHdrInfo->FileHdr, + pRebuildState->pLogHdr, pDefaultCreateOpts); + pCreateOpts = pDefaultCreateOpts; + } + uiDbVersion = pHdrInfo->FileHdr.uiVersionNum; + pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, + pRebuildState->pLogHdr); + } + else if (rc == FERR_UNSUPPORTED_VERSION || rc == FERR_NEWER_FLAIM) + { + goto Exit; + } + else if ((rc == FERR_NOT_FLAIM) || + (!VALID_BLOCK_SIZE( pHdrInfo->FileHdr.uiBlockSize))) + { + FLMUINT uiSaveBlockSize; + FLMUINT uiCalcBlockSize = 0; + FLMBYTE ucFileHdrBuf[ FLM_FILE_HEADER_SIZE]; + + uiDbVersion = (FLMUINT)((rc != FERR_NOT_FLAIM) + ? pHdrInfo->FileHdr.uiVersionNum + : FLM_CUR_FILE_FORMAT_VER_NUM); + pSFileHdl->SetDbVersion( uiDbVersion); + pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, + pRebuildState->pLogHdr); + if (!pCreateOpts) + { + if (rc != FERR_NOT_FLAIM) + { + flmGetCreateOpts( &pHdrInfo->FileHdr, + pRebuildState->pLogHdr, pDefaultCreateOpts); + } + else + { + flmGetCreateOpts( NULL, NULL, pDefaultCreateOpts); + } + + // Set block size to zero, so we will always take the calculated + // block size below. + + pDefaultCreateOpts->uiBlockSize = 0; + pCreateOpts = pDefaultCreateOpts; + } + + /* Try to determine the correct block size. */ + + if (RC_BAD( rc = bldDetermineBlkSize( pSFileHdl, + pRebuildState->uiMaxFileSize, + &uiCalcBlockSize, + fnStatusFunc, &pRebuildState->CallbackData, + pRebuildState->AppArg))) + { + goto Exit; + } + + uiSaveBlockSize = pCreateOpts->uiBlockSize; + pCreateOpts->uiBlockSize = uiCalcBlockSize; + + // Initialize pHdrInfo->FileHdr to useable values. + + flmInitFileHdrInfo( pCreateOpts, &pHdrInfo->FileHdr, ucFileHdrBuf); + + /* + Only use the passed-in block size (uiSaveBlockSize) if it + was non-zero. + */ + + if (uiSaveBlockSize) + pCreateOpts->uiBlockSize = uiSaveBlockSize; + } + else + { + goto Exit; + } + + // Calculate the file size. + + pSFileHdl->SetDbVersion( uiDbVersion); + pRebuildState->CallbackData.ui64DatabaseSize = 0; + for (uiFileNumber = 1;;uiFileNumber++) + { + FLMUINT uiTmpSize; + + if (RC_BAD( pSFileHdl->GetFileSize( uiFileNumber, &uiTmpSize))) + { + break; + } + pRebuildState->CallbackData.ui64DatabaseSize += (FLMUINT64)uiTmpSize; + } + + // Delete the destination database in case it already exists. + + if (RC_BAD( rc = FlmDbRemove( pszDestDbPath, pszDestDataDir, + pszDestRflDir, TRUE))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + /* + If no block size has been specified or determined yet, use what we + read from the file header. + */ + + if (!pCreateOpts->uiBlockSize) + pCreateOpts->uiBlockSize = pHdrInfo->FileHdr.uiBlockSize; + + pSFileHdl->SetDbVersion( pHdrInfo->FileHdr.uiVersionNum); + pSFileHdl->SetBlockSize( pHdrInfo->FileHdr.uiBlockSize); + + /* + Set the ECache manger into the super file handle + */ + + if( pFile && pFile->pECacheMgr) + { + pSFileHdl->setECacheMgr( pFile->pECacheMgr); + } + else if( gv_FlmSysData.bOkToUseESM) + { + if( (pECacheMgr = f_new FlmECache) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( !pECacheMgr->setupECache( pHdrInfo->FileHdr.uiBlockSize, + pRebuildState->uiMaxFileSize)) + { + pECacheMgr->Release(); + pECacheMgr = NULL; + } + else + { + pSFileHdl->setECacheMgr( pECacheMgr); + } + } + + /* + When creating the new file, set the transaction ID to one greater than it + is in the corrupt file. However, don't let it get greater than about + 2 billion - want to leave room for 2 billion transactions in case they + were corrupted somehow in our old file. + */ + + uiTransID = + ((FLMUINT)FB2UD( &pRebuildState->pLogHdr [LOG_CURR_TRANS_ID]) + 1) & 0x7FFFFFFF; + + if (RC_BAD( rc = flmCreateNewFile( pszDestDbPath, pszDestDataDir, + pszDestRflDir, + pszDictPath, NULL, + pCreateOpts, uiTransID, (FDB * *)&pRebuildState->hDb, + pRebuildState))) + { + goto Exit; + } + pDb = (FDB *)pRebuildState->hDb; + + /* Rebuild the database */ + + if (RC_BAD( rc = flmDbRebuildFile( pRebuildState, bBadHeader))) + { + goto Exit; + } + +Exit: + + /* Close the temporary database, if it is still open. */ + + if (pDb) + { + FFILE * pTmpFile; + FFILE * pTmpFile1; + + // Get the FFILE pointer for the temporary file before closing it. + + pTmpFile = pDb->pFile; + + (void)FlmDbClose( (HFDB *)&pDb); + + // Force temporary FFILE structure to be cleaned up, if it + // isn't already gone. The following code searches for the + // temporary file in the not-used list. If it finds it, + // it will unlink it. + + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + pTmpFile1 = gv_FlmSysData.pLrnuFile; + while (pTmpFile1) + { + if (pTmpFile1 == pTmpFile) + { + flmFreeFile( pTmpFile); + break; + } + pTmpFile1 = pTmpFile1->pNextNUFile; + } + } + + if (bUsedFFile) + { + if (!bMutexLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + if (!(--pFile->uiUseCount)) + { + flmLinkFileToNUList( pFile); + } + } + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + /* Unlock the file, if it is locked. */ + + if (bWriteLocked) + { + dbWriteUnlock( pFile); + bWriteLocked = FALSE; + } + + if (bFileLocked) + { + RCODE rc3; + + if (RC_BAD( rc3 = pFileLockObj->Unlock( TRUE, NULL))) + { + if (RC_OK( rc)) + { + rc = rc3; + } + } + bFileLocked = FALSE; + } + + if( pSFileHdl) + { + pSFileHdl->Release(); + } + + if( pECacheMgr) + { + pECacheMgr->Release(); + pECacheMgr = NULL; + } + + if (pWriteLockObj) + { + pWriteLockObj->Release(); + pWriteLockObj = NULL; + } + if (pFileLockObj) + { + pFileLockObj->Release(); + pFileLockObj = NULL; + } + + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + + if( pDefaultCreateOpts) + { + f_free( &pDefaultCreateOpts); + } + + if (pRebuildState) + { + if( puiTotRecsRV) + { + *puiTotRecsRV = pRebuildState->CallbackData.uiTotRecs; + } + + if( puiRecsRecovRV) + { + *puiRecsRecovRV = pRebuildState->CallbackData.uiRecsRecov; + } + + if ((pRebuildState->pStateInfo) && + (pRebuildState->pStateInfo->pRecord)) + { + pRebuildState->pStateInfo->pRecord->Release(); + + } + + if (pRebuildState->pRecord) + { + pRebuildState->pRecord->Release(); + pRebuildState->pRecord = NULL; + } + + if( pRebuildState->pHdrInfo) + { + f_free( &pRebuildState->pHdrInfo); + } + + if( pRebuildState->pStateInfo) + { + f_free( &pRebuildState->pStateInfo); + } + + if( pRebuildState->pLogHdr) + { + f_free( &pRebuildState->pLogHdr); + } + + if( pRebuildState->pKeyBuffer) + { + f_free( &pRebuildState->pKeyBuffer); + } + + f_free( &pRebuildState); + } + else + { + if( puiTotRecsRV) + { + *puiTotRecsRV = 0; + } + + if( puiRecsRecovRV) + { + *puiRecsRecovRV = 0; + } + } + + return( rc); +} + + +/*************************************************************************** +Desc: This routine reads through a database and makes a best guess as to + the true block size of the database. +*****************************************************************************/ +FSTATIC RCODE bldDetermineBlkSize( + F_SuperFileHdl * pSFileHdl, // Super file handle for database. + FLMUINT uiMaxFileSize, + FLMUINT * puiBlkSizeRV, // Calculated block size is returned here. + STATUS_HOOK fnStatusFunc, // Callback function. + REBUILD_INFO * pCallbackData, // Callback structure. + void * AppArg) // User data for callback. +{ + RCODE rc = FERR_OK; + FLMBYTE ucBlkHeader [BH_OVHD]; + FLMUINT uiBytesRead; + FLMUINT uiBlkAddress; + FLMUINT uiFileNumber = 0; + FLMUINT uiOffset = 0; + FLMUINT uiCount4K = 0; + FLMUINT uiCount8K = 0; + FLMUINT64 ui64BytesDone = 0; + F_FileHdlImp * pFileHdl = NULL; + + /* Start from byte offset 0 in the first file. */ + + pCallbackData->iDoingFlag = REBUILD_GET_BLK_SIZ; + pCallbackData->bStartFlag = TRUE; + for (;;) + { + if (uiOffset >= uiMaxFileSize || !uiFileNumber) + { + uiOffset = 0; + uiFileNumber++; + if (RC_BAD( rc = pSFileHdl->GetFileHdl( + uiFileNumber, FALSE, &pFileHdl))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + break; + } + goto Exit; + } + } + + if ((RC_OK(rc = pFileHdl->Read( uiOffset, BH_OVHD, ucBlkHeader, + &uiBytesRead))) || + (rc == FERR_IO_END_OF_FILE)) + { + if (RC_OK( rc)) + { + ui64BytesDone += (FLMUINT64)MIN_BLOCK_SIZE; + } + else + { + ui64BytesDone += (FLMUINT64)uiBytesRead; + } + uiBlkAddress = GET_BH_ADDR( ucBlkHeader); + if ((uiBytesRead == BH_OVHD) && + (FSGetFileOffset( uiBlkAddress) == uiOffset)) + { + if (uiOffset % 4096 == 0) + uiCount4K++; + if (uiOffset % 8192 == 0) + uiCount8K++; + } + if (rc != FERR_OK || uiBytesRead < BH_OVHD) + { + + // Even if the file is not full size, set offset to + // the maximum file offset so we will attempt to go + // to the next file at the top of this loop. If that + // fails, we will assume we truly are at EOF. + + uiOffset = uiMaxFileSize; + } + else + { + uiOffset += MIN_BLOCK_SIZE; + } + + /* Call the callback function to report copy progress. */ + + if (fnStatusFunc != NULL) + { + pCallbackData->ui64BytesExamined = ui64BytesDone; + if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, + (void *)pCallbackData, + (void *)0, + AppArg))) + { + goto Exit; + } + pCallbackData->bStartFlag = FALSE; + } + + f_yieldCPU(); + } + else + { + goto Exit; + } + } + if (rc == FERR_IO_END_OF_FILE) + rc = FERR_OK; + + // If our count of 4K blocks is greater than 66% of the number + // of 4K blocks that would fit in the database, we will use + // a 4K block size. Otherwise, we will use an 8K block size. + + if (uiCount4K > + (FLMUINT)(((ui64BytesDone / + (FLMUINT64)4096) * (FLMUINT64)66) / (FLMUINT64)100)) + { + *puiBlkSizeRV = 4096; + } + else + { + *puiBlkSizeRV = 8192; + } +Exit: + return( rc); +} diff --git a/flaim/src/fsv.cpp b/flaim/src/fsv.cpp new file mode 100644 index 0000000..ff5db74 --- /dev/null +++ b/flaim/src/fsv.cpp @@ -0,0 +1,5545 @@ +//------------------------------------------------------------------------- +// Desc: FLAIM server functions +// 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$ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +FSTATIC RCODE fsvIteratorParse( + FSV_WIRE * pWire, + POOL * pPool); + +FSTATIC RCODE fsvIteratorWhereParse( + FSV_WIRE * pWire, + POOL * pPool); + +FSTATIC RCODE fsvIteratorFromParse( + FSV_WIRE * pWire, + POOL * pPool); + +FSTATIC RCODE fsvIteratorSelectParse( + FSV_WIRE * pWire, + POOL * pPool); + +FSTATIC RCODE fsvDbGetBlocks( + HFDB hDb, + FLMUINT uiAddress, + FLMUINT uiMinTransId, + FLMUINT * puiCount, + FLMUINT * puiBlocksExamined, + FLMUINT * puiNextBlkAddr, + FLMUINT uiFlags, + POOL * pPool, + FLMBYTE ** ppBlocks, + FLMUINT * puiBytes); + +FSTATIC RCODE fsvGetHandles( + FSV_WIRE * pWire); + +RCODE fsvTcpListener( + F_Thread * pThread); + +RCODE fsvTcpClientHandler( + F_Thread * pThread); + +RCODE fsvTcpVulture( + F_Thread * pThread); + +RCODE fsvTcpAcceptConnection( + F_MUTEX * phHandlerSem, + FCS_TCP * pClient); + +FLMBOOL gv_bTcpAllowConnections = TRUE; +FLMBOOL gv_bTcpRunning = FALSE; +F_Thread * gv_TcpHandlers[ FSV_MAX_TCP_HANDLERS]; +F_Thread * gv_pTcpListenerThrd = NULL; +FSV_SCTX * gv_pGlobalContext = NULL; + +/**************************************************************************** +Desc: Initializes the server's global context. +****************************************************************************/ +RCODE fsvInitGlobalContext( + FLMUINT uiMaxSessions, + const char * pszServerBasePath, + FSV_LOG_FUNC pLogFunc) +{ + RCODE rc = FERR_OK; + FSV_SCTX * pTmpContext = NULL; + + if (gv_pGlobalContext) + { + + // Context already initialized + + goto Exit; + } + + if ((pTmpContext = f_new FSV_SCTX) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pTmpContext->Setup( uiMaxSessions, pszServerBasePath, + pLogFunc))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + if (pTmpContext) + { + pTmpContext->Release(); + } + } + else if (pTmpContext) + { + gv_pGlobalContext = pTmpContext; + } + + return (rc); +} + +/**************************************************************************** +Desc: Frees any resources allocated to the global context. +****************************************************************************/ +void fsvFreeGlobalContext(void) +{ + if (gv_pGlobalContext) + { + gv_pGlobalContext->Release(); + gv_pGlobalContext = NULL; + } +} + +/**************************************************************************** +Desc: Sets the server's base (relative) path +****************************************************************************/ +RCODE fsvSetBasePath( + const char * pszServerBasePath) +{ + RCODE rc = FERR_OK; + + if (!gv_pGlobalContext) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_pGlobalContext->SetBasePath( pszServerBasePath))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Sets the server's temporary directory +****************************************************************************/ +RCODE fsvSetTempDir( + const char * pszTempDir) +{ + RCODE rc = FERR_OK; + + if (!gv_pGlobalContext) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_pGlobalContext->SetTempDir( pszTempDir))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Returns a pointer to the server's global context object. +****************************************************************************/ +RCODE fsvGetGlobalContext( + FSV_SCTX ** ppGlobalContext) +{ + *ppGlobalContext = gv_pGlobalContext; + return (FERR_OK); +} + +/**************************************************************************** +Desc: This is the function that processes FLAIM requests. +****************************************************************************/ +RCODE fsvProcessRequest( + FCS_DIS * pDataIStream, + FCS_DOS * pDataOStream, + POOL * pScratchPool, + FLMUINT * puiSessionIdRV) +{ + void * pvMark = NULL; + FSV_WIRE Wire(pDataIStream, pDataOStream); + RCODE rc = FERR_OK; + + // Set the temporary pool + + if (pScratchPool) + { + pvMark = GedPoolMark( pScratchPool); + Wire.setPool( pScratchPool); + } + + // Read the request + + if (RC_BAD( rc = Wire.read())) + { + goto Exit; + } + + // Close the input stream. + + pDataIStream->close(); + Wire.setDIStream( NULL); + + // Get any required handles. + + if (RC_BAD( rc = fsvGetHandles( &Wire))) + { + goto Exit; + } + + // Call the appropriate handler function. + + switch (Wire.getClass()) + { + case FCS_OPCLASS_GLOBAL: + { + if (RC_BAD( rc = fsvOpClassGlobal( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_SESSION: + { + if (RC_BAD( rc = fsvOpClassSession( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_DATABASE: + { + if (RC_BAD( rc = fsvOpClassDatabase( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_TRANS: + { + if (RC_BAD( rc = fsvOpClassTransaction( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_RECORD: + { + if (RC_BAD( rc = fsvOpClassRecord( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_ITERATOR: + { + if (RC_BAD( rc = fsvOpClassIterator( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_BLOB: + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + break; + } + + case FCS_OPCLASS_DIAG: + { + if (RC_BAD( rc = fsvOpClassDiag( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_FILE: + { + if (RC_BAD( rc = fsvOpClassFile( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_ADMIN: + { + if (RC_BAD( rc = fsvOpClassAdmin( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_INDEX: + { + if (RC_BAD( rc = fsvOpClassIndex( &Wire))) + { + goto Exit; + } + break; + } + + case FCS_OPCLASS_MISC: + { + if (RC_BAD( rc = fsvOpClassMisc( &Wire))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + } + + if (puiSessionIdRV) + { + + // Set the session ID so that the calling routine has the option of + // performing cleanup on an error + + *puiSessionIdRV = Wire.getSessionId(); + } + +Exit: + + if (RC_BAD( rc)) + { + + // If the input stream is still open, the handler never send any + // data to the client. Close the input stream and try to send the + // error code to the client. + + if (pDataIStream->isOpen()) + { + (void) pDataIStream->close(); + Wire.setDIStream( NULL); + } + + if (RC_OK( Wire.sendOpcode( Wire.getClass(), Wire.getOp()))) + { + if (RC_OK( Wire.sendRc( rc))) + { + if (RC_OK( Wire.sendTerminate())) + { + pDataOStream->close(); + } + } + } + } + else + { + pDataOStream->close(); + } + + if (pScratchPool) + { + GedPoolReset( pScratchPool, pvMark); + } + + return (rc); +} + +/**************************************************************************** +Desc: Performs a diagnostic operation +****************************************************************************/ +RCODE fsvOpClassDiag( + FSV_WIRE * pWire) +{ + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Service the request. + + switch (pWire->getOp()) + { + case FCS_OP_DIAG_HTD_ECHO: + { + // Simply echo the record back to the client. This is done below + // when the response is sent to the client. + + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_DIAG, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + switch (pWire->getOp()) + { + case FCS_OP_DIAG_HTD_ECHO: + { + if (pWire->getRecord() != NULL) + { + if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_HTD, + pWire->getRecord()))) + { + goto Exit; + } + } + break; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a file system operation +****************************************************************************/ +RCODE fsvOpClassFile( + FSV_WIRE * pWire) +{ + RCODE rc = FERR_OK; + RCODE opRc = FERR_OK; + FSV_SCTX * pServerContext = NULL; + FLMUNICODE * puzSourcePath; + char szSourcePath[F_PATH_MAX_SIZE]; + + // Set up local variables. + + if (RC_BAD( opRc = fsvGetGlobalContext( &pServerContext))) + { + goto OP_EXIT; + } + + puzSourcePath = pWire->getFilePath(); + if (puzSourcePath) + { + + // Convert the UNICODE URL to a server path. + + if (RC_BAD( rc = pServerContext->BuildFilePath( puzSourcePath, + szSourcePath))) + { + goto Exit; + } + } + + // Service the request. + + switch (pWire->getOp()) + { + case FCS_OP_FILE_EXISTS: + { + if (!puzSourcePath) + { + opRc = RC_SET( FERR_SYNTAX); + goto OP_EXIT; + } + + if (RC_BAD( opRc = gv_FlmSysData.pFileSystem->Exists( szSourcePath))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_FILE_DELETE: + { + if (!puzSourcePath) + { + opRc = RC_SET( FERR_SYNTAX); + goto OP_EXIT; + } + + if (RC_BAD( opRc = gv_FlmSysData.pFileSystem->Delete( szSourcePath))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_FILE, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs an administrative operation +****************************************************************************/ +RCODE fsvOpClassAdmin( + FSV_WIRE * pWire) +{ + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_ADMIN, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a global operation +****************************************************************************/ +RCODE fsvOpClassGlobal( + FSV_WIRE * pWire) +{ + FSV_SCTX * pServerContext; + NODE * pTree = NULL; + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Service the request. + + if (RC_BAD( rc = fsvGetGlobalContext( &pServerContext))) + { + goto Exit; + } + + switch (pWire->getOp()) + { + case FCS_OP_GLOBAL_STATS_START: + { + if (RC_BAD( opRc = FlmConfig( FLM_START_STATS, 0, 0))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GLOBAL_STATS_STOP: + { + if (RC_BAD( opRc = FlmConfig( FLM_STOP_STATS, 0, 0))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GLOBAL_STATS_RESET: + { + if (RC_BAD( opRc = FlmConfig( FLM_RESET_STATS, 0, 0))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GLOBAL_MEM_INFO_GET: + { + FLM_MEM_INFO memInfo; + + FlmGetMemoryInfo( &memInfo); + if (RC_BAD( opRc = fcsBuildMemInfo( &memInfo, + pWire->getPool(), &pTree))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GLOBAL_GET_THREAD_INFO: + { + if (RC_BAD( opRc = fcsBuildThreadInfo( pWire->getPool(), &pTree))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_GLOBAL, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + if (pTree) + { + if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pTree))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a session operation +****************************************************************************/ +RCODE fsvOpClassSession( + FSV_WIRE * pWire) +{ + FLMUINT uiSessionIdRV; + FSV_SCTX * pServerContext; + FSV_SESN * pSession = NULL; + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Service the request. + + if (RC_BAD( opRc = fsvGetGlobalContext( &pServerContext))) + { + goto OP_EXIT; + } + + switch (pWire->getOp()) + { + case FCS_OP_SESSION_OPEN: + { + // Create a new session. + + if (RC_BAD( opRc = pServerContext->OpenSession( + pWire->getClientVersion(), pWire->getFlags(), + &uiSessionIdRV, &pSession))) + { + goto OP_EXIT; + } + + break; + } + + case FCS_OP_SESSION_CLOSE: + { + // Close the session. + + if (RC_BAD( opRc = pServerContext->CloseSession( pWire->getSessionId()))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_SESSION, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + if (pWire->getOp() == FCS_OP_SESSION_OPEN) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_SESSION_ID, + uiSessionIdRV))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_SESSION_COOKIE, + pSession->getCookie()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_FLAGS, + FCS_SESSION_GEDCOM_SUPPORT))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_FLAIM_VERSION, + FLM_CUR_FILE_FORMAT_VER_NUM))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a record or DRN operation +****************************************************************************/ +RCODE fsvOpClassRecord( + FSV_WIRE * pWire) +{ + FSV_SESN * pSession; + HFDB hDb; + FLMUINT uiContainer; + FLMUINT uiIndex; + FLMUINT uiAutoTrans; + FLMUINT uiDrn; + FLMUINT uiFlags; + FlmRecord * pRecord = NULL; + FlmRecord * pRecordRV = NULL; + FLMUINT uiDrnRV = 0; + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Get a pointer to the session object. + + if ((pSession = pWire->getSession()) == NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + // Get the database handle. This is needed by all of the record + // operations. + + if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + // Initialize local variables. + + uiContainer = pWire->getContainerId(); + uiIndex = pWire->getIndexId(); + uiDrn = pWire->getDrn(); + uiAutoTrans = pWire->getAutoTrans(); + uiFlags = pWire->getFlags(); + pRecord = pWire->getRecord(); + + // Perform the operation. + + switch (pWire->getOp()) + { + case FCS_OP_RECORD_RETRIEVE: + { + if (!uiFlags) + { + uiFlags = FO_EXACT; + } + + if (pWire->getBoolean()) + { + + // Fetch the record + + if (RC_BAD( opRc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, + uiFlags, &pRecordRV, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + + // Just get the DRN + + if (RC_BAD( opRc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, + uiFlags, NULL, &uiDrnRV))) + { + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_RECORD_ADD: + { + uiDrnRV = uiDrn; + if (RC_BAD( opRc = FlmRecordAdd( hDb, uiContainer, &uiDrnRV, pRecord, + uiAutoTrans))) + { + goto OP_EXIT; + } + + break; + } + + case FCS_OP_RECORD_MODIFY: + { + if (RC_BAD( opRc = FlmRecordModify( hDb, uiContainer, uiDrn, pRecord, + uiAutoTrans))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_RECORD_DELETE: + { + if (RC_BAD( opRc = FlmRecordDelete( hDb, uiContainer, uiDrn, + uiAutoTrans))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_RESERVE_NEXT_DRN: + { + uiDrnRV = uiDrn; + if (RC_BAD( opRc = FlmReserveNextDrn( hDb, uiContainer, &uiDrnRV))) + { + goto OP_EXIT; + } + + break; + } + + case FCS_OP_KEY_RETRIEVE: + { + if (pSession->getClientVersion() >= FCS_VERSION_1_1_1) + { + if (RC_BAD( opRc = FlmKeyRetrieve( hDb, uiIndex, uiContainer, + pRecord, uiDrn, uiFlags, &pRecordRV, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + FLMUINT uiKeyContainer = 0; + + if (pRecord) + { + uiKeyContainer = pRecord->getContainerID(); + } + + // Older clients sent index # in the container tag. + + if (RC_BAD( opRc = FlmKeyRetrieve( hDb, uiContainer, uiKeyContainer, + pRecord, uiDrn, uiFlags, &pRecordRV, &uiDrnRV))) + { + goto OP_EXIT; + } + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_RECORD, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + if (pRecordRV) + { + if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_RECORD, pRecordRV))) + { + goto Exit; + } + } + + if (uiDrnRV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_DRN, uiDrnRV))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + if (pRecordRV) + { + pRecordRV->Release(); + } + + return (rc); +} + +/**************************************************************************** +Desc: Performs a database operation. +****************************************************************************/ +RCODE fsvOpClassDatabase( + FSV_WIRE * pWire) +{ + RCODE rc = FERR_OK; + RCODE opRc = FERR_OK; + FSV_SESN * pSession; + HFDB hDb = HFDB_NULL; + CREATE_OPTS CreateOptsRV; + FLMUINT uiBlockCountRV = 0; + FLMUINT uiBlocksExaminedRV = 0; + FLMUINT uiBlockAddrRV = 0; + FLMUINT uiTransIdRV; + FLMUINT64 ui64NumValue1RV = 0; + FLMUINT64 ui64NumValue2RV = 0; + FLMUINT64 ui64NumValue3RV = 0; + FLMBOOL bBoolValueRV = FALSE; + FLMUINT uiItemIdRV = 0; + char szItemName[64]; + NODE * pHTDRV = NULL; + char szPathRV[F_PATH_MAX_SIZE]; + F_NameTable nameTable; + FLMBOOL bHaveCreateOptsVal = FALSE; + FLMBOOL bHavePathValue = FALSE; + FLMBYTE * pBinary = NULL; + FLMUINT uiBinSize = 0; + + szItemName[0] = 0; + + if ((pSession = pWire->getSession()) == NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + if (pWire->getOp() != FCS_OP_DATABASE_OPEN && + pWire->getOp() != FCS_OP_DATABASE_CREATE) + { + + // Get the database handle for all database operations other than + // open and create. + + if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + } + + switch (pWire->getOp()) + { + case FCS_OP_DATABASE_OPEN: + { + if (RC_BAD( opRc = pSession->OpenDatabase( pWire->getFilePath(), + pWire->getFilePath3(), pWire->getFilePath2(), + pWire->getFlags()))) + { + goto OP_EXIT; + } + + break; + } + + case FCS_OP_DATABASE_CREATE: + { + CREATE_OPTS createOpts; + pWire->copyCreateOpts( &createOpts); + + if (RC_BAD( opRc = pSession->CreateDatabase( pWire->getFilePath(), + pWire->getFilePath3(), pWire->getFilePath2(), + pWire->getDictPath(), pWire->getDictBuffer(), &createOpts))) + { + goto OP_EXIT; + } + + break; + } + + case FCS_OP_DATABASE_CLOSE: + { + if (RC_BAD( opRc = pSession->CloseDatabase())) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DB_REDUCE_SIZE: + { + if (RC_BAD( opRc = FlmDbReduceSize( hDb, (FLMUINT) pWire->getCount(), + &uiBlockCountRV))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GET_ITEM_NAME: + { + if (RC_BAD( opRc = FlmGetItemName( hDb, pWire->getItemId(), + sizeof(szItemName), szItemName))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GET_NAME_TABLE: + { + if (RC_BAD( rc = nameTable.setupFromDb( hDb))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GET_COMMIT_CNT: + { + if (RC_BAD( opRc = FlmDbGetCommitCnt( hDb, &uiBlockCountRV))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_GET_TRANS_ID: + { + if (RC_BAD( opRc = FlmDbGetTransId( hDb, &uiTransIdRV))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DATABASE_GET_CONFIG: + { + switch ((eDbGetConfigType) pWire->getType()) + { + case FDB_GET_VERSION: + { + // Doing via create opts to maintain backward compatibility. + + f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_VERSION, + (void *) &CreateOptsRV.uiVersionNum))) + { + goto OP_EXIT; + } + + bHaveCreateOptsVal = TRUE; + break; + } + + case FDB_GET_BLKSIZ: + { + // Doing via create opts to maintain backward compatibility. + + f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_BLKSIZ, + (void *) &CreateOptsRV.uiBlockSize))) + { + goto OP_EXIT; + } + + bHaveCreateOptsVal = TRUE; + break; + } + + case FDB_GET_DEFAULT_LANG: + { + // Doing via create opts to maintain backward compatibility. + + f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_DEFAULT_LANG, + (void *) &CreateOptsRV.uiDefaultLanguage))) + { + goto OP_EXIT; + } + + bHaveCreateOptsVal = TRUE; + break; + } + + case FDB_GET_TRANS_ID: + case FDB_GET_RFL_FILE_NUM: + case FDB_GET_RFL_HIGHEST_NU: + case FDB_GET_LAST_BACKUP_TRANS_ID: + case FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: + case FDB_GET_FILE_EXTEND_SIZE: + case FDB_GET_APP_DATA: + { + FLMUINT uiTmpValue; + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, + (eDbGetConfigType) pWire->getType(), + (void *) &uiTmpValue))) + { + goto OP_EXIT; + } + + ui64NumValue1RV = (FLMUINT64) uiTmpValue; + break; + } + + case FDB_GET_RFL_FILE_SIZE_LIMITS: + { + FLMUINT uiTmpValue1; + FLMUINT uiTmpValue2; + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, + FDB_GET_RFL_FILE_SIZE_LIMITS, + (void *) &uiTmpValue1, + (void *) &uiTmpValue2))) + { + goto OP_EXIT; + } + + ui64NumValue1RV = (FLMUINT64) uiTmpValue1; + ui64NumValue2RV = (FLMUINT64) uiTmpValue2; + break; + } + + case FDB_GET_RFL_KEEP_FLAG: + case FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG: + case FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG: + { + if (RC_BAD( opRc = FlmDbGetConfig( hDb, + (eDbGetConfigType) pWire->getType(), + (void *) &bBoolValueRV))) + { + goto OP_EXIT; + } + break; + } + + case FDB_GET_PATH: + { + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_PATH, + (void *) szPathRV))) + { + goto OP_EXIT; + } + + bHavePathValue = TRUE; + break; + } + + case FDB_GET_CHECKPOINT_INFO: + { + CHECKPOINT_INFO checkpointInfo; + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, + FDB_GET_CHECKPOINT_INFO, (void *) &checkpointInfo))) + { + goto OP_EXIT; + } + + if (RC_BAD( opRc = fcsBuildCheckpointInfo( &checkpointInfo, + pWire->getPool(), &pHTDRV))) + { + goto OP_EXIT; + } + break; + } + + case FDB_GET_LOCK_HOLDER: + { + LOCK_USER lockUser; + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_LOCK_HOLDER, + (void *) &lockUser))) + { + goto OP_EXIT; + } + + if (RC_BAD( opRc = fcsBuildLockUser( &lockUser, FALSE, + pWire->getPool(), &pHTDRV))) + { + goto OP_EXIT; + } + break; + } + + case FDB_GET_LOCK_WAITERS: + { + LOCK_USER * pLockUser = NULL; + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_LOCK_WAITERS, + (void *) &pLockUser))) + { + if (pLockUser) + { + FlmFreeMem( &pLockUser); + } + + goto OP_EXIT; + } + + if (RC_BAD( opRc = fcsBuildLockUser( pLockUser, TRUE, + pWire->getPool(), &pHTDRV))) + { + if (pLockUser) + { + FlmFreeMem( &pLockUser); + } + + goto OP_EXIT; + } + + if (pLockUser) + { + FlmFreeMem( &pLockUser); + } + break; + } + + case FDB_GET_RFL_DIR: + { + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_RFL_DIR, + (void *) szPathRV))) + { + goto OP_EXIT; + } + + bHavePathValue = TRUE; + break; + } + + case FDB_GET_SERIAL_NUMBER: + { + uiBinSize = F_SERIAL_NUM_SIZE; + + if( (pBinary = (FLMBYTE *) GedPoolAlloc( + pWire->getPool(), uiBinSize)) == NULL) + { + opRc = RC_SET( FERR_MEM); + goto OP_EXIT; + } + + if (RC_BAD( opRc = FlmDbGetConfig( hDb, + FDB_GET_SERIAL_NUMBER, (void *) pBinary))) + { + goto OP_EXIT; + } + break; + } + + case FDB_GET_SIZES: + { + if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_SIZES, + (void *) &ui64NumValue1RV, + (void *) &ui64NumValue2RV, + (void *) &ui64NumValue3RV))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_DATABASE_CONFIG: + { + switch ((eDbConfigType) pWire->getType()) + { + case FDB_SET_APP_VERSION: + case FDB_RFL_KEEP_FILES: + case FDB_RFL_ROLL_TO_NEXT_FILE: + case FDB_KEEP_ABORTED_TRANS_IN_RFL: + case FDB_AUTO_TURN_OFF_KEEP_RFL: + case FDB_SET_APP_DATA: + { + if (RC_BAD( opRc = FlmDbConfig( hDb, + (eDbConfigType) pWire->getType(), + (void *) ((FLMUINT) pWire->getNumber2()), + (void *) ((FLMUINT) pWire->getNumber3())))) + { + goto OP_EXIT; + } + break; + } + + case FDB_RFL_FILE_LIMITS: + case FDB_FILE_EXTEND_SIZE: + { + if (RC_BAD( opRc = FlmDbConfig( hDb, + (eDbConfigType) pWire->getType(), + (void *) ((FLMUINT) pWire->getNumber1()), + (void *) ((FLMUINT) pWire->getNumber2())))) + { + goto OP_EXIT; + } + break; + } + + case FDB_RFL_DIR: + { + char * pszPath; + POOL * pPool = pWire->getPool(); + void * pvMark = GedPoolMark( pPool); + + if (RC_BAD( rc = fcsConvertUnicodeToNative( pPool, + pWire->getFilePath(), &pszPath))) + { + goto Exit; + } + + if (RC_BAD( opRc = FlmDbConfig( hDb, + (eDbConfigType) pWire->getType(), + (void *) pszPath, + (void *) ((FLMUINT) pWire->getNumber3())))) + { + goto OP_EXIT; + } + + GedPoolReset( pPool, pvMark); + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_DATABASE_LOCK: + { + if (RC_BAD( opRc = FlmDbLock( hDb, + (FLOCK_TYPE) (FLMUINT) pWire->getNumber1(), + (FLMINT) pWire->getSignedValue(), (FLMUINT) pWire->getFlags() + ))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DATABASE_UNLOCK: + { + if (RC_BAD( opRc = FlmDbUnlock( hDb))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DATABASE_GET_BLOCK: + { + uiBlockCountRV = (FLMUINT) pWire->getCount(); + if (RC_BAD( opRc = fsvDbGetBlocks( hDb, pWire->getAddress(), + pWire->getTransId(), &uiBlockCountRV, &uiBlocksExaminedRV, + &uiBlockAddrRV, pWire->getFlags(), pWire->getPool(), + &pBinary, &uiBinSize))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DATABASE_CHECKPOINT: + { + if (RC_BAD( opRc = FlmDbCheckpoint( hDb, pWire->getFlags()))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_DB_SET_BACKUP_FLAG: + { + FLMBOOL bNewState = pWire->getBoolean(); + + if (!IsInCSMode( hDb)) + { + FDB * pDb = (FDB *) hDb; + + f_mutexLock( gv_FlmSysData.hShareMutex); + if (pDb->pFile->bBackupActive && bNewState) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + opRc = RC_SET( FERR_BACKUP_ACTIVE); + goto OP_EXIT; + } + + pDb->pFile->bBackupActive = bNewState; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + else + { + if (RC_BAD( opRc = fcsSetBackupActiveFlag( hDb, bNewState))) + { + goto OP_EXIT; + } + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_DATABASE, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + switch (pWire->getOp()) + { + case FCS_OP_DB_REDUCE_SIZE: + case FCS_OP_GET_COMMIT_CNT: + { + + // Return a count + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_COUNT, uiBlockCountRV))) + { + goto Exit; + } + break; + } + + case FCS_OP_GET_NAME_TABLE: + { + + // Return the name table. + + if (RC_OK( opRc)) + { + if (RC_BAD( rc = pWire->sendNameTable( WIRE_VALUE_NAME_TABLE, + &nameTable))) + { + goto Exit; + } + } + break; + } + + case FCS_OP_GET_ITEM_NAME: + { + FLMUNICODE * puzItemNameRV; + + if (RC_OK( opRc)) + { + if (szItemName[0]) + { + if (RC_BAD( rc = fcsConvertNativeToUnicode( pWire->getPool(), + szItemName, &puzItemNameRV))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendString( WIRE_VALUE_ITEM_NAME, + puzItemNameRV))) + { + goto Exit; + } + } + } + break; + } + + case FCS_OP_GET_ITEM_ID: + { + if (uiItemIdRV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ITEM_ID, uiItemIdRV))) + { + goto Exit; + } + } + break; + } + + case FCS_OP_GET_TRANS_ID: + { + + // Return the transaction id for the database. + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_TRANSACTION_ID, + uiTransIdRV))) + { + goto Exit; + } + break; + } + + case FCS_OP_DATABASE_GET_BLOCK: + { + + // Return the requested block + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_COUNT, uiBlockCountRV))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER2, + uiBlocksExaminedRV))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ADDRESS, uiBlockAddrRV))) + { + goto Exit; + } + + if (uiBlockCountRV) + { + if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_BLOCK, pBinary, + uiBinSize))) + { + goto Exit; + } + } + break; + } + + case FCS_OP_DATABASE_GET_CONFIG: + { + switch (pWire->getType()) + { + case FDB_GET_SERIAL_NUMBER: + if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_SERIAL_NUM, pBinary, + uiBinSize))) + { + goto Exit; + } + break; + default: + break; + } + break; + } + } + + if (bHaveCreateOptsVal) + { + if (RC_BAD( rc = pWire->sendCreateOpts( WIRE_VALUE_CREATE_OPTS, + &CreateOptsRV))) + { + goto Exit; + } + } + + if (ui64NumValue1RV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER1, ui64NumValue1RV))) + { + goto Exit; + } + } + + if (ui64NumValue2RV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER2, ui64NumValue2RV))) + { + goto Exit; + } + } + + if (ui64NumValue3RV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER3, ui64NumValue3RV))) + { + goto Exit; + } + } + + if (bBoolValueRV) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_BOOLEAN, bBoolValueRV))) + { + goto Exit; + } + } + + if (bHavePathValue) + { + FLMUNICODE * puzPath; + + if (RC_BAD( rc = fcsConvertNativeToUnicode( pWire->getPool(), szPathRV, + &puzPath))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendString( WIRE_VALUE_FILE_PATH, puzPath))) + { + goto Exit; + } + } + + if (pHTDRV) + { + if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pHTDRV))) + { + goto Exit; + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs an iterator (cursor) operation +****************************************************************************/ +RCODE fsvOpClassIterator( + FSV_WIRE * pWire) +{ + RCODE rc = FERR_OK; + RCODE opRc = FERR_OK; + FSV_SESN * pSession = NULL; + HFCURSOR hIterator = HFCURSOR_NULL; + FLMBOOL bDoDrnOp = FALSE; + FlmRecord * pRecordRV = NULL; + FlmRecord * pTmpRecord = NULL; + FLMUINT uiIteratorIdRV = FCS_INVALID_ID; + FLMUINT uiCountRV = 0; + FLMUINT uiDrnRV = 0; + FLMBOOL bFlag = FALSE; + + // Get a pointer to the session object. + + if ((pSession = pWire->getSession()) == NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + // Get the iterator handle. + + if ((hIterator = pWire->getIteratorHandle()) == HFDB_NULL) + { + if (pWire->getOp() != FCS_OP_ITERATOR_INIT) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + } + + // Examine the wire flags for the operation. + + bDoDrnOp = (FLMBOOL) + ( + (pWire->getFlags() & FCS_ITERATOR_DRN_FLAG) ? (FLMBOOL) TRUE : + (FLMBOOL) FALSE + ); + + // Perform the requested operation. + + switch (pWire->getOp()) + { + case FCS_OP_ITERATOR_INIT: + { + // Build the query. + + if (RC_BAD( opRc = fsvIteratorParse( pWire, pWire->getPool()))) + { + goto OP_EXIT; + } + + uiIteratorIdRV = pWire->getIteratorId(); + break; + } + + case FCS_OP_ITERATOR_FREE: + { + // Free the iterator. + + if (RC_BAD( opRc = pSession->FreeIterator( pWire->getIteratorId()))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_ITERATOR_FIRST: + { + // Retrieve the first record (or DRN) in the result set. + + if (bDoDrnOp) + { + if (RC_BAD( opRc = FlmCursorFirstDRN( hIterator, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + if (RC_BAD( opRc = FlmCursorFirst( hIterator, &pRecordRV))) + { + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_ITERATOR_LAST: + { + // Retrieve the last record (or DRN) in the result set. + + if (bDoDrnOp) + { + if (RC_BAD( opRc = FlmCursorLastDRN( hIterator, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + if (RC_BAD( opRc = FlmCursorLast( hIterator, &pRecordRV))) + { + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_ITERATOR_NEXT: + { + // Retrieve the next record (or DRN) in the result set. + + if (bDoDrnOp) + { + if (RC_BAD( opRc = FlmCursorNextDRN( hIterator, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + if (RC_BAD( opRc = FlmCursorNext( hIterator, &pRecordRV))) + { + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_ITERATOR_PREV: + { + // Retrieve the previous record (or DRN) in the result set. + + if (bDoDrnOp) + { + if (RC_BAD( opRc = FlmCursorPrevDRN( hIterator, &uiDrnRV))) + { + goto OP_EXIT; + } + } + else + { + if (RC_BAD( opRc = FlmCursorPrev( hIterator, &pRecordRV))) + { + goto OP_EXIT; + } + } + break; + } + + case FCS_OP_ITERATOR_COUNT: + { + // Count the number of records in the result set. + + if (RC_BAD( opRc = FlmCursorRecCount( hIterator, &uiCountRV))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_ITERATOR_TEST_REC: + { + if ((pTmpRecord = pWire->getRecord()) != NULL) + { + pTmpRecord->AddRef(); + + if (RC_BAD( opRc = FlmCursorTestRec( hIterator, pTmpRecord, &bFlag))) + { + goto OP_EXIT; + } + + pTmpRecord->Release(); + pTmpRecord = NULL; + } + else + { + if (RC_BAD( opRc = FlmCursorTestDRN( hIterator, pWire->getDrn(), + &bFlag))) + { + goto OP_EXIT; + } + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_ITERATOR, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + if (pRecordRV) + { + + // Send the retrieved record. + + if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_RECORD, pRecordRV))) + { + goto Exit; + } + } + + if (uiDrnRV) + { + + // Send the record's DRN. + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_DRN, uiDrnRV))) + { + goto Exit; + } + } + + if (uiCountRV) + { + + // Send the record count. + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_RECORD_COUNT, uiCountRV))) + { + goto Exit; + } + } + + if (uiIteratorIdRV != FCS_INVALID_ID) + { + + // Send the iterator's ID. + + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ITERATOR_ID, + uiIteratorIdRV))) + { + goto Exit; + } + } + + if (bFlag) + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_BOOLEAN, bFlag))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + if (pRecordRV) + { + pRecordRV->Release(); + } + + if (pTmpRecord) + { + pTmpRecord->Release(); + pTmpRecord = NULL; + } + + return (rc); +} + +/**************************************************************************** +Desc: Performs a transaction operation +****************************************************************************/ +RCODE fsvOpClassTransaction( + FSV_WIRE * pWire) +{ + RCODE rc = FERR_OK; + RCODE opRc = FERR_OK; + FSV_SESN * pSession; + HFDB hDb; + FLMUINT uiTransTypeRV; + FLMBYTE * pBlock = NULL; + FLMUINT uiBlockSize = 0; + FLMUINT uiFlmTransFlags = 0; + + // Get a pointer to the session object. + + if ((pSession = pWire->getSession()) == NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + // Get a handle to the database in case this is a database transaction + // operation + + hDb = (HFDB) pWire->getFDB(); + + // Perform the requested operation. + + switch (pWire->getOp()) + { + case FCS_OP_TRANSACTION_BEGIN: + { + // Start a database transaction. + + if (pWire->getFlags() & FCS_TRANS_FLAG_GET_HEADER) + { + uiBlockSize = 2048; + if ((pBlock = (FLMBYTE *) GedPoolAlloc( + pWire->getPool(), uiBlockSize)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto OP_EXIT; + } + } + + if (pWire->getFlags() & FCS_TRANS_FLAG_DONT_KILL) + { + uiFlmTransFlags |= FLM_DONT_KILL_TRANS; + } + + if (pWire->getFlags() & FCS_TRANS_FLAG_DONT_POISON) + { + uiFlmTransFlags |= FLM_DONT_POISON_CACHE; + } + + if (RC_BAD( opRc = FlmDbTransBegin( hDb, + pWire->getTransType() | uiFlmTransFlags, + pWire->getMaxLockWait(), pBlock))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_TRANSACTION_COMMIT: + { + // Commit a database transaction. + + if (RC_BAD( opRc = FlmDbTransCommit( hDb))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_TRANSACTION_COMMIT_EX: + { + // Commit a database transaction. + + if (RC_BAD( opRc = fsvDbTransCommitEx( hDb, pWire))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_TRANSACTION_ABORT: + { + // Abort a database transaction. + + if (RC_BAD( opRc = FlmDbTransAbort( hDb))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_TRANSACTION_GET_TYPE: + { + // Get the database transaction type. + + if (RC_BAD( opRc = FlmDbGetTransType( hDb, &uiTransTypeRV))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_TRANS, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (pBlock) + { + if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_BLOCK, + pBlock, uiBlockSize))) + { + goto Exit; + } + } + + if (RC_OK( opRc)) + { + switch (pWire->getOp()) + { + case FCS_OP_TRANSACTION_GET_TYPE: + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_TRANSACTION_TYPE, + uiTransTypeRV))) + { + goto Exit; + } + break; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a maintenance operation. +****************************************************************************/ +RCODE fsvOpClassMaintenance( + FSV_WIRE * pWire) +{ + FSV_SESN * pSession; + HFDB hDb; + POOL pool; + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Initialize a temporary pool. + + GedPoolInit( &pool, 1024); + + // Service the request. + + if ((pSession = pWire->getSession()) == NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + switch (pWire->getOp()) + { + case FCS_OP_CHECK: + { + if (RC_BAD( opRc = FlmDbCheck( hDb, NULL, NULL, NULL, + pWire->getFlags(), &pool, NULL, NULL, 0))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_MAINTENANCE, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + switch (pWire->getOp()) + { + case FCS_OP_CHECK: + { + break; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs an index operation +****************************************************************************/ +RCODE fsvOpClassIndex( + FSV_WIRE * pWire) +{ + HFDB hDb = HFDB_NULL; + FLMUINT uiIndex; + FINDEX_STATUS indexStatus; + POOL * pTmpPool = pWire->getPool(); + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Get the database handle. This is needed by all of the index + // operations. + + if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) + { + opRc = RC_SET( FERR_BAD_HDL); + goto OP_EXIT; + } + + // Initialize local variables. + + uiIndex = pWire->getIndexId(); + + // Service the request. + + switch (pWire->getOp()) + { + case FCS_OP_INDEX_GET_STATUS: + { + if (RC_BAD( opRc = FlmIndexStatus( hDb, uiIndex, &indexStatus))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_INDEX_GET_NEXT: + { + if (RC_BAD( opRc = FlmIndexGetNext( hDb, &uiIndex))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_INDEX_SUSPEND: + { + if (RC_BAD( opRc = FlmIndexSuspend( hDb, uiIndex))) + { + goto OP_EXIT; + } + break; + } + + case FCS_OP_INDEX_RESUME: + { + if (RC_BAD( opRc = FlmIndexResume( hDb, uiIndex))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_INDEX, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + switch (pWire->getOp()) + { + case FCS_OP_INDEX_GET_STATUS: + { + NODE * pStatusTree; + + if (RC_BAD( fcsBuildIndexStatus( &indexStatus, pTmpPool, + &pStatusTree))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pStatusTree))) + { + goto Exit; + } + break; + } + + case FCS_OP_INDEX_GET_NEXT: + { + if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_INDEX_ID, uiIndex))) + { + goto Exit; + } + break; + } + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Performs a misc. operation +****************************************************************************/ +RCODE fsvOpClassMisc( + FSV_WIRE * pWire) +{ + FLMBYTE ucSerialNum[F_SERIAL_NUM_SIZE]; + RCODE opRc = FERR_OK; + RCODE rc = FERR_OK; + + // Service the request. + + switch (pWire->getOp()) + { + case FCS_OP_CREATE_SERIAL_NUM: + { + if (RC_BAD( opRc = f_createSerialNumber( ucSerialNum))) + { + goto OP_EXIT; + } + break; + } + + default: + { + opRc = RC_SET( FERR_NOT_IMPLEMENTED); + goto OP_EXIT; + } + } + +OP_EXIT: + + // Send the server's response. + + if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_MISC, pWire->getOp()))) + { + goto Exit; + } + + if (RC_BAD( rc = pWire->sendRc( opRc))) + { + goto Exit; + } + + if (RC_OK( opRc)) + { + if (pWire->getOp() == FCS_OP_CREATE_SERIAL_NUM) + { + if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_SERIAL_NUM, ucSerialNum, + F_SERIAL_NUM_SIZE))) + { + goto Exit; + } + } + else + { + flmAssert( rc == FERR_NOT_IMPLEMENTED); + } + } + + if (RC_BAD( rc = pWire->sendTerminate())) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Configures an iterator based on from, where, select, and config + clauses provided by the client. +****************************************************************************/ +FSTATIC RCODE fsvIteratorParse( + FSV_WIRE * pWire, + POOL * pPool) +{ + RCODE rc = FERR_OK; + + // Parse the "from" clause. This contains record source information. + + if (pWire->getIteratorFrom()) + { + if (RC_BAD( rc = fsvIteratorFromParse( pWire, pPool))) + { + goto Exit; + } + } + + if (pWire->getIteratorHandle() == HFCURSOR_NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Parse the "where" clause. This contains the criteria. + + if (pWire->getIteratorWhere()) + { + if (RC_BAD( rc = fsvIteratorWhereParse( pWire, pPool))) + { + goto Exit; + } + } + + // Parse the "select" clause. This contains customized view + // information. + + if (pWire->getIteratorSelect()) + { + if (RC_BAD( rc = fsvIteratorSelectParse( pWire, pPool))) + { + goto Exit; + } + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Adds selection criteria to an iterator. +****************************************************************************/ +FSTATIC RCODE fsvIteratorWhereParse( + FSV_WIRE * pWire, + POOL * pPool) +{ + HFCURSOR hIterator = pWire->getIteratorHandle(); + NODE * pWhere = pWire->getIteratorWhere(); + NODE * pCurNode; + NODE * pTmpNode; + void * pPoolMark; + FLMUINT uiTag; + RCODE rc = FERR_OK; + + // If no "where" clause, jump to exit. + + if (!pWhere) + { + goto Exit; + } + + // Process each component of the "where" clause. + + pCurNode = GedChild( pWhere); + while (pCurNode) + { + uiTag = GedTagNum( pCurNode); + switch (uiTag) + { + case FCS_ITERATOR_MODE: + { + FLMUINT uiFlags = 0; + + // Set the iterator's mode flags + + if (RC_BAD( rc = GedGetUINT( pCurNode, &uiFlags))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorSetMode( hIterator, uiFlags))) + { + goto Exit; + } + break; + } + + // Add an attribute to the criteria. + + case FCS_ITERATOR_ATTRIBUTE: + { + FLMUINT uiAttrId; + + // Get the attribute ID. + + if (RC_BAD( rc = GedGetUINT( pCurNode, &uiAttrId))) + { + goto Exit; + } + + // Add the attribute. + + if (uiTag == FCS_ITERATOR_ATTRIBUTE) + { + if (RC_BAD( rc = FlmCursorAddField( hIterator, uiAttrId, 0))) + { + goto Exit; + } + } + else + { + + // Sanity check. + + flmAssert( 0); + } + break; + } + + // Add an attribute path to the criteria. + + case FCS_ITERATOR_ATTRIBUTE_PATH: + { + FLMUINT puiPath[FCS_ITERATOR_MAX_PATH + 1]; + FLMUINT uiAttrId; + FLMUINT uiPathPos = 0; + FLMUINT uiStartLevel; + + if ((pTmpNode = GedFind( GED_TREE, pCurNode, + FCS_ITERATOR_ATTRIBUTE, 1)) != NULL) + { + + // Build the attribute path. + + uiStartLevel = GedNodeLevel( pTmpNode); + while (pTmpNode && GedNodeLevel( pTmpNode) >= uiStartLevel) + { + if (GedNodeLevel( pTmpNode) == uiStartLevel && + GedTagNum( pTmpNode) == FCS_ITERATOR_ATTRIBUTE) + { + if (RC_BAD( rc = GedGetUINT( pTmpNode, &uiAttrId))) + { + goto Exit; + } + + puiPath[uiPathPos++] = uiAttrId; + if (uiPathPos > FCS_ITERATOR_MAX_PATH) + { + rc = RC_SET( FERR_SYNTAX); + goto Exit; + } + } + + pTmpNode = pTmpNode->next; + } + + puiPath[uiPathPos] = 0; + } + + // Add the attribute path. + + if (RC_BAD( rc = FlmCursorAddFieldPath( hIterator, puiPath, 0))) + { + goto Exit; + } + break; + } + + // Add a numeric value to the criteria. + + case FCS_ITERATOR_NUMBER_VALUE: + case FCS_ITERATOR_REC_PTR_VALUE: + { + + // To save conversion time, cheat to determine if the number + // is negative. + + FLMBYTE * pucValue = (FLMBYTE *) GedValPtr( pCurNode); + FLMBOOL bNegative = ((*pucValue & 0xF0) == 0xB0) + ? TRUE + : FALSE; + + if (bNegative) + { + FLMINT32 i32Value; + + if (uiTag == FCS_ITERATOR_REC_PTR_VALUE) + { + rc = RC_SET( FERR_SYNTAX); + goto Exit; + } + + if (RC_BAD( rc = GedGetINT32( pCurNode, &i32Value))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_INT32_VAL, + &i32Value, 0))) + { + goto Exit; + } + } + else + { + FLMUINT32 ui32Value; + FLMUINT uiValue; + + if (RC_BAD( rc = GedGetUINT32( pCurNode, &ui32Value))) + { + goto Exit; + } + + if (uiTag == FCS_ITERATOR_NUMBER_VALUE) + { + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_UINT32_VAL, + &ui32Value, 0))) + { + goto Exit; + } + } + else if (uiTag == FCS_ITERATOR_REC_PTR_VALUE) + { + uiValue = ui32Value; + if (RC_BAD( rc = FlmCursorAddValue( hIterator, + FLM_REC_PTR_VAL, &uiValue, 0))) + { + goto Exit; + } + } + else + { + + // Sanity check. + + flmAssert( 0); + } + } + break; + } + + // Add a binary value to the criteria. + + case FCS_ITERATOR_BINARY_VALUE: + { + FLMBYTE * pucValue = (FLMBYTE *) GedValPtr( pCurNode); + FLMUINT uiValLen = GedValLen( pCurNode); + + if (GedValType( pCurNode) != FLM_BINARY_TYPE) + { + rc = RC_SET( FERR_SYNTAX); + goto Exit; + } + + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_BINARY_VAL, + pucValue, uiValLen))) + { + goto Exit; + } + break; + } + + // Add a UNICODE string value to the criteria. + + case FCS_ITERATOR_UNICODE_VALUE: + { + FLMUINT uiLen; + FLMUNICODE * puzBuf; + + // Mark the pool. + + pPoolMark = GedPoolMark( pPool); + + // Determine the length of the string. + + if (RC_BAD( rc = GedGetUNICODE( pCurNode, NULL, &uiLen))) + { + goto Exit; + } + + // Allocate a temporary buffer. + + uiLen += 2; + if ((puzBuf = (FLMUNICODE *) GedPoolAlloc( pPool, uiLen)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Extract the string and add it to the criteria. + + if (RC_BAD( rc = GedGetUNICODE( pCurNode, puzBuf, &uiLen))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_UNICODE_VAL, + puzBuf, 0))) + { + goto Exit; + } + + GedPoolReset( pPool, pPoolMark); + break; + } + + // Add a NATIVE, WP60, or Word String value to the criteria. + + case FCS_ITERATOR_NATIVE_VALUE: + case FCS_ITERATOR_WP60_VALUE: + case FCS_ITERATOR_WDSTR_VALUE: + { + FLMUINT uiLen; + FLMBYTE * pucBuf; + + // Mark the pool. + + pPoolMark = GedPoolMark( pPool); + + // Determine the length of the string. + + if (uiTag == FCS_ITERATOR_NATIVE_VALUE) + { + if (RC_BAD( rc = GedGetNATIVE( pCurNode, NULL, &uiLen))) + { + goto Exit; + } + } + else + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + + // Allocate a temporary buffer. + + uiLen += 2; + if ((pucBuf = (FLMBYTE *) GedPoolAlloc( pPool, uiLen)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Extract the string and add it to the criteria. + + if (uiTag == FCS_ITERATOR_NATIVE_VALUE) + { + if (RC_BAD( rc = GedGetNATIVE( pCurNode, (char *) pucBuf, + &uiLen))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_STRING_VAL, + pucBuf, 0))) + { + goto Exit; + } + } + + GedPoolReset( pPool, pPoolMark); + break; + } + + // Add a native (internal) text value + + case FCS_ITERATOR_FLM_TEXT_VALUE: + { + if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_TEXT_VAL, + GedValPtr( pCurNode), GedValLen( pCurNode)))) + { + goto Exit; + } + break; + } + + // Add an operator to the criteria. + + case FCS_ITERATOR_OPERATOR: + { + FLMUINT uiOp; + QTYPES eTranslatedOp; + + // Get the C/S operator ID. + + if (RC_BAD( rc = GedGetUINT( pCurNode, &uiOp))) + { + goto Exit; + } + + if (!uiOp || + ((uiOp - FCS_ITERATOR_OP_START) >= FCS_ITERATOR_OP_END)) + { + rc = RC_SET( FERR_SYNTAX); + goto Exit; + } + + // Translate the C/S ID to a FLAIM operator ID. + + if (RC_BAD( rc = fcsTranslateQCSToQFlmOp( uiOp, &eTranslatedOp))) + { + goto Exit; + } + + // Add the operator to the criteria. + + if (RC_BAD( rc = FlmCursorAddOp( hIterator, eTranslatedOp))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET( FERR_SYNTAX); + goto Exit; + } + } + + pCurNode = GedSibNext( pCurNode); + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Adds source information to an iterator. +****************************************************************************/ +FSTATIC RCODE fsvIteratorFromParse( + FSV_WIRE * pWire, + POOL * pPool) +{ + HFDB hDb = HFDB_NULL; + HFCURSOR hIterator = pWire->getIteratorHandle(); + FLMUINT uiIteratorId = FCS_INVALID_ID; + NODE * pFrom = pWire->getIteratorFrom(); + NODE * pCurNode; + NODE * pCSAttrNode; + NODE * pTmpNode; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pPool); + + // If no "from" clause, jump to exit. + + if (!pFrom) + { + goto Exit; + } + + // Process each component of the "from" clause. + + if (hIterator == HFCURSOR_NULL) + { + FSV_SESN * pSession; + FLMUINT uiContainerId = FLM_DATA_CONTAINER; + FLMUINT uiPath[4]; + + uiPath[0] = FCS_ITERATOR_FROM; + uiPath[1] = FCS_ITERATOR_CANDIDATE_SET; + uiPath[2] = FCS_ITERATOR_RECORD_SOURCE; + uiPath[3] = 0; + if ((pCSAttrNode = GedPathFind( GED_TREE, pFrom, uiPath, 1)) == NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Get the database handle. + + if ((pSession = pWire->getSession()) == NULL) + { + rc = RC_SET( FERR_BAD_HDL); + goto Exit; + } + + hDb = pSession->GetDatabase(); + + // Get the container ID. A default value of FLM_DATA_CONTAINER will + // be used if a container ID is not found. + + if ((pTmpNode = GedFind( GED_TREE, pCSAttrNode, + FCS_ITERATOR_CONTAINER_ID, 1)) != NULL) + { + if (RC_BAD( rc = GedGetUINT( pTmpNode, &uiContainerId))) + { + goto Exit; + } + } + + // Initialize the cursor when we get the source - only one source is + // allowed. + + if (RC_BAD( rc = pSession->InitializeIterator( &uiIteratorId, hDb, + uiContainerId, &hIterator))) + { + goto Exit; + } + + // Set the iterator handle and ID so they will be available for the + // parser to use. + + pWire->setIteratorId( uiIteratorId); + pWire->setIteratorHandle( hIterator); + } + + pCurNode = GedChild( pFrom); + while (pCurNode) + { + switch (GedTagNum( pCurNode)) + { + case FCS_ITERATOR_CANDIDATE_SET: + { + + // Process record sources and indexes. + + pCSAttrNode = GedChild( pCurNode); + while (pCSAttrNode) + { + switch (GedTagNum( pCSAttrNode)) + { + + // Define a record source. + + case FCS_ITERATOR_RECORD_SOURCE: + { + + // Handled above. + + break; + } + + // Specify a FLAIM index. + + case FCS_ITERATOR_FLAIM_INDEX: + { + FLMUINT uiIndexId; + + // Get the index ID. + + if (RC_BAD( rc = GedGetUINT( pCSAttrNode, &uiIndexId))) + { + goto Exit; + } + + // Add the index. + + if (RC_BAD( rc = FlmCursorConfig( hIterator, + FCURSOR_SET_FLM_IX, (void *) uiIndexId, + (void *) 0))) + { + goto Exit; + } + break; + } + + // Set the record type. + + case FCS_ITERATOR_RECORD_TYPE: + { + FLMUINT uiRecordType; + + // Get the record type. + + if (RC_BAD( rc = GedGetUINT( pCSAttrNode, &uiRecordType))) + { + goto Exit; + } + + // Add the record type. + + if (RC_BAD( rc = FlmCursorConfig( hIterator, + FCURSOR_SET_REC_TYPE, + (void *) uiRecordType, + (void *) 0))) + { + goto Exit; + } + break; + } + + case FCS_ITERATOR_OK_TO_RETURN_KEYS: + { + FLMUINT uiOkToReturnKeys; + + if (RC_BAD( rc = GedGetUINT( + pCSAttrNode, &uiOkToReturnKeys))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorConfig( hIterator, + FCURSOR_RETURN_KEYS_OK, + (void *) (FLMBOOL) (uiOkToReturnKeys + ? TRUE + : FALSE), + (void *) NULL))) + { + goto Exit; + } + break; + } + } + + pCSAttrNode = GedSibNext( pCSAttrNode); + } + break; + } + + case FCS_ITERATOR_MODE: + { + FLMUINT uiFlags; + + // Get the mode flags. + + if (RC_BAD( rc = GedGetUINT( pCurNode, &uiFlags))) + { + goto Exit; + } + + if (RC_BAD( rc = FlmCursorSetMode( hIterator, uiFlags))) + { + goto Exit; + } + break; + } + } + + pCurNode = GedSibNext( pCurNode); + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Adds a view to an iterator +****************************************************************************/ +FSTATIC RCODE fsvIteratorSelectParse( + FSV_WIRE * pWire, + POOL * pPool) +{ + NODE * pSelect = pWire->getIteratorSelect(); + NODE * pCurNode; + NODE * pView = NULL; + FLMBOOL bNullViewNotRec = FALSE; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pPool); + + // If no "select" clause, jump to exit. + + if (!pSelect) + { + goto Exit; + } + + pCurNode = GedChild( pSelect); + while (pCurNode) + { + switch (GedTagNum( pCurNode)) + { + case FCS_ITERATOR_VIEW_TREE: + { + pView = GedChild( pCurNode); + break; + } + + case FCS_ITERATOR_NULL_VIEW_NOT_REC: + { + bNullViewNotRec = TRUE; + break; + } + } + + pCurNode = GedSibNext( pCurNode); + } + + // Set the view record, if any (not supported). + + if (GedChild( pCurNode)) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Reads blocks from the database +****************************************************************************/ +FSTATIC RCODE fsvDbGetBlocks( + HFDB hDb, + FLMUINT uiAddress, + FLMUINT uiMinTransId, + FLMUINT * puiCount, + FLMUINT * puiBlocksExamined, + FLMUINT * puiNextBlkAddr, + FLMUINT uiFlags, + POOL * pPool, + FLMBYTE ** ppBlocks, + FLMUINT * puiBytes) +{ + FDB * pDb = (FDB *) hDb; + FLMBOOL bDbInitialized = FALSE; + FLMBOOL bTransStarted = FALSE; + FLMUINT uiLoop; + FLMUINT uiCount = *puiCount; + SCACHE * pSCache = NULL; + FLMUINT uiBlockSize; + FLMUINT uiMaxFileSize; + RCODE rc = FERR_OK; + + *ppBlocks = NULL; + *puiCount = 0; + *puiBlocksExamined = 0; + *puiBytes = 0; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + bDbInitialized = TRUE; + + 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, uiCount))) + { + 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) + { + *puiBytes = Wire.getBlockSize(); + if ((*ppBlocks = (FLMBYTE *) GedPoolAlloc( pPool, *puiBytes)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_memcpy( *ppBlocks, Wire.getBlock(), *puiBytes); + } + + goto Exit; + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto Exit; + } + + if (!uiCount) + { + uiCount = 1; + } + + uiBlockSize = pDb->pFile->FileHdr.uiBlockSize; + uiMaxFileSize = pDb->pFile->uiMaxFileSize; + bDbInitialized = TRUE; + if (RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, + &bTransStarted))) + { + goto Exit; + } + + if ((*ppBlocks = (FLMBYTE *) GedPoolAlloc( + pPool, uiBlockSize * uiCount)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Read uiCount blocks from the database starting at uiAddress. If none + // of the blocks meet the min trans ID criteria, we will not return any + // blocks to the reader. + + *puiNextBlkAddr = BT_END; + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + if (!FSAddrIsBelow( FSBlkAddress( FSGetFileNumber( uiAddress), + FSGetFileOffset( uiAddress)), pDb->LogHdr.uiLogicalEOF)) + { + rc = RC_SET( FERR_IO_END_OF_FILE); + goto Exit; + } + + if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiAddress, NULL, + &pSCache))) + { + goto Exit; + } + + if (FB2UD( &pSCache->pucBlk[BH_TRANS_ID]) >= uiMinTransId) + { + f_memcpy( (*ppBlocks + ((*puiCount) * uiBlockSize)), pSCache->pucBlk, + uiBlockSize); + (*puiCount)++; + (*puiBytes) += uiBlockSize; + } + (*puiBlocksExamined)++; + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + + uiAddress += uiBlockSize; + if (FSGetFileOffset( uiAddress) >= uiMaxFileSize) + { + uiAddress = FSBlkAddress( FSGetFileNumber( uiAddress) + 1, 0); + } + + *puiNextBlkAddr = uiAddress; + } + +Exit: + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + if (bTransStarted) + { + RCODE rc2 = flmAbortDbTrans( pDb); + if (RC_OK( rc)) + { + rc = rc2; + } + } + + if (bDbInitialized) + { + fdbExit( pDb); + } + + return (rc); +} + +/**************************************************************************** +Desc: Commits a database transaction and updates the log header +****************************************************************************/ +RCODE fsvDbTransCommitEx( + HFDB hDb, + FSV_WIRE * pWire) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *) hDb; + FLMBOOL bIgnore; + FLMBOOL bForceCheckpoint = FALSE; + FLMBYTE * pucHeader = NULL; + + if (pWire->getFlags() & FCS_TRANS_FORCE_CHECKPOINT) + { + bForceCheckpoint = TRUE; + } + + pucHeader = pWire->getBlock(); + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + + 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, pucHeader, + bForceCheckpoint); + } + + goto Exit; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) + { + goto Exit; + } + + // If there is an invisible transaction going, it should not be + // commitable by an application. + + if ((pDb->uiTransType == FLM_NO_TRANS) || + (pDb->uiFlags & FDB_INVISIBLE_TRANS)) + { + rc = RC_SET( FERR_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( pDb->AbortRc)) + { + rc = RC_SET( FERR_ABORT_TRANS); + goto Exit; + } + + // Fix up the log header. Currently, only fields directly related to a + // backup operation are updated. + + if (pucHeader) + { + FLMBYTE * pLogHdr = &pucHeader[16]; + FLMBYTE * pucUncommittedHdr = &pDb->pFile->ucUncommittedLogHdr[0]; + + f_memcpy( &pucUncommittedHdr[LOG_LAST_BACKUP_TRANS_ID], + &pLogHdr[LOG_LAST_BACKUP_TRANS_ID], 4); + + f_memcpy( &pucUncommittedHdr[LOG_BLK_CHG_SINCE_BACKUP], + &pLogHdr[LOG_BLK_CHG_SINCE_BACKUP], 4); + + f_memcpy( &pucUncommittedHdr[LOG_INC_BACKUP_SEQ_NUM], + &pLogHdr[LOG_INC_BACKUP_SEQ_NUM], 4); + + f_memcpy( &pucUncommittedHdr[LOG_INC_BACKUP_SERIAL_NUM], + &pLogHdr[LOG_INC_BACKUP_SERIAL_NUM], F_SERIAL_NUM_SIZE); + } + + // Commit the transaction + + rc = flmCommitDbTrans( pDb, 0, bForceCheckpoint); + +Exit: + + flmExit( FLM_DB_TRANS_COMMIT, pDb, rc); + return (rc); +} + +/**************************************************************************** +Desc: Looks up session, database, and iterator handles. +****************************************************************************/ +FSTATIC RCODE fsvGetHandles( + FSV_WIRE * pWire) +{ + FSV_SCTX * pServerContext = NULL; + FSV_SESN * pSession = NULL; + HFCURSOR hIterator = HFCURSOR_NULL; + RCODE rc = FERR_OK; + + if (RC_BAD( rc = fsvGetGlobalContext( &pServerContext))) + { + goto Exit; + } + + if (pWire->getSessionId() != FCS_INVALID_ID) + { + if (RC_BAD( pServerContext->GetSession( pWire->getSessionId(), &pSession))) + { + rc = RC_SET( FERR_BAD_HDL); + goto Exit; + } + + if (pSession->getCookie() != pWire->getSessionCookie()) + { + rc = RC_SET( FERR_BAD_HDL); + goto Exit; + } + + pWire->setSession( pSession); + } + + if (pSession) + { + pWire->setFDB( (FDB *) pSession->GetDatabase()); + if (pWire->getIteratorId() != FCS_INVALID_ID) + { + if (RC_BAD( rc = pSession->GetIterator( pWire->getIteratorId(), + &hIterator))) + { + goto Exit; + } + + pWire->setIteratorHandle( hIterator); + } + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvPostStreamedRequest( + FSV_SESN * pSession, + FLMBYTE * pucPacket, + FLMUINT uiPacketSize, + FLMBOOL bLastPacket, + FCS_BIOS * pSessionResponse) +{ + FLMBOOL bReleaseSession = FALSE; + RCODE rc = FERR_OK; + POOL localPool; + + GedPoolInit( &localPool, 1024); + + if (!pSession && !bLastPacket) + { + + // If this is a session open request, the request must be contained + // in a single packet. + + rc = RC_SET( FERR_ILLEGAL_OP); + goto Exit; + } + + if (!pSession) + { + FCS_BIOS biosInput; + FCS_DIS dataIStream; + FCS_DOS dataOStream; + + if (RC_BAD( rc = dataIStream.setup( &biosInput))) + { + goto Exit; + } + + if (RC_BAD( rc = dataOStream.setup( pSessionResponse))) + { + goto Exit; + } + + if (RC_BAD( rc = biosInput.write( pucPacket, uiPacketSize))) + { + goto Exit; + } + + if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, + &localPool, NULL))) + { + goto Exit; + } + } + else + { + FCS_BIOS * pServerBIStream; + FCS_BIOS * pServerBOStream; + + // Need to add a reference to the session object so that if the + // request closes the session, the response stream will not be + // destructed until the response has been returned to the client. + + pSession->AddRef(); + bReleaseSession = TRUE; + + if (RC_BAD( rc = pSession->GetBIStream( &pServerBIStream))) + { + goto Exit; + } + + if (RC_BAD( rc = pSession->GetBOStream( &pServerBOStream))) + { + goto Exit; + } + + if (RC_BAD( rc = pServerBIStream->write( pucPacket, uiPacketSize))) + { + goto Exit; + } + + if (bLastPacket) + { + FCS_DIS dataIStream; + FCS_DOS dataOStream; + + if (RC_BAD( rc = dataIStream.setup( pServerBIStream))) + { + goto Exit; + } + + if (RC_BAD( rc = dataOStream.setup( pServerBOStream))) + { + goto Exit; + } + + GedPoolReset( pSession->getWireScratchPool(), NULL); + if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, + pSession->getWireScratchPool(), NULL))) + { + goto Exit; + } + } + } + +Exit: + + GedPoolFree( &localPool); + + if (bReleaseSession) + { + pSession->Release(); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvGetStreamedResponse( + FSV_SESN * pSession, + FLMBYTE * pucPacketBuffer, + FLMUINT uiMaxPacketSize, + FLMUINT * puiPacketSize, + FLMBOOL * pbLastPacket) +{ + FCS_BIOS * pServerBOStream = NULL; + RCODE rc = FERR_OK; + + if (RC_BAD( rc = pSession->GetBOStream( &pServerBOStream))) + { + goto Exit; + } + + if (RC_BAD( rc = pServerBOStream->read( pucPacketBuffer, uiMaxPacketSize, + puiPacketSize))) + { + if (rc == FERR_EOF_HIT) + { + *pbLastPacket = TRUE; + rc = FERR_OK; + } + + goto Exit; + } + + if (!pServerBOStream->isDataAvailable()) + { + *pbLastPacket = TRUE; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSV_SCTX::FSV_SCTX(void) +{ + m_uiSessionToken = 0; + m_uiCacheSize = FSV_DEFAULT_CACHE_SIZE; + m_bSetupCalled = FALSE; + m_paSessions = NULL; + m_hMutex = F_MUTEX_NULL; + m_szServerBasePath[0] = '\0'; + m_pLogFunc = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSV_SCTX::~FSV_SCTX(void) +{ + FLMUINT uiSlot; + + if (m_bSetupCalled) + { + + // Clean up and free the session table. + + for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) + { + if (m_paSessions[uiSlot] != NULL) + { + m_paSessions[uiSlot]->Release(); + } + } + + f_free( &m_paSessions); + + // Free the session semaphore. + + (void) f_mutexDestroy( &m_hMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::Setup( + FLMUINT uiMaxSessions, + const char * pszServerBasePath, + FSV_LOG_FUNC pLogFunc) +{ + RCODE rc = FERR_OK; + FLMUINT uiSlot; + + // Make sure that setup has not been called. + + flmAssert( m_bSetupCalled == FALSE); + + // If zero was passed as the value of uiMaxSessions, use the default. + + if (!uiMaxSessions) + { + m_uiMaxSessions = FSV_DEFAULT_MAX_CONNECTIONS; + } + else + { + m_uiMaxSessions = uiMaxSessions; + } + + // Initialize the session table. + + if (RC_BAD( rc = f_alloc( sizeof(FSV_SESN *) * m_uiMaxSessions, &m_paSessions + ))) + { + goto Exit; + } + + for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) + { + m_paSessions[uiSlot] = NULL; + } + + // Initialize the context mutex + + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Set the server's home path. + + if (pszServerBasePath) + { + f_strcpy( m_szServerBasePath, pszServerBasePath); + } + else + { + m_szServerBasePath[0] = '\0'; + } + + // Set the logging function. + + m_pLogFunc = pLogFunc; + + // Set the setup flag. + + m_bSetupCalled = TRUE; + +Exit: + + // Clean up any allocations if an error was encountered. + + if (RC_BAD( rc)) + { + if (m_paSessions != NULL) + { + f_free( &m_paSessions); + } + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::OpenSession( + FLMUINT uiVersion, + FLMUINT uiFlags, + FLMUINT * puiIdRV, + FSV_SESN ** ppSessionRV) +{ + FLMUINT uiSlot; + FLMUINT uiCurrTime; + FLMBOOL bLocked = FALSE; + FSV_SESN * pSession = NULL; + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Initialize the Id + + *puiIdRV = 0; + + // Create a new session object + + if ((pSession = f_new FSV_SESN) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Allocate the session object + + if (RC_BAD( rc = pSession->Setup( this, uiVersion, uiFlags))) + { + goto Exit; + } + + // Lock the context mutex + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + // Find an empty slot in the table. + + for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) + { + if (!m_paSessions[uiSlot]) + { + break; + } + } + + if (uiSlot >= m_uiMaxSessions) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Assign the session to the table slot. + + m_paSessions[uiSlot] = pSession; + + // Increment the session token. + + m_uiSessionToken++; + + // If the session token is 0xFFFF, reset it to 1. Because + // FSV_INVALID_ID is 0xFFFFFFFF, it is important to reset the session + // token so that a session will never be assigned an invalid ID. + + if (m_uiSessionToken == 0xFFFF) + { + m_uiSessionToken = 1; + } + + // Set the session's ID. + + *puiIdRV = uiSlot | (m_uiSessionToken << 16); + pSession->setId( *puiIdRV); + + // Set the session's cookie using the current time. + + f_timeGetSeconds( &uiCurrTime); + pSession->setCookie( uiCurrTime); + + // Unlock the context mutex + + f_mutexUnlock( m_hMutex); + bLocked = FALSE; + +Exit: + + if (RC_BAD( rc)) + { + if (pSession) + { + pSession->Release(); + pSession = NULL; + } + } + else + { + if (ppSessionRV) + { + *ppSessionRV = pSession; + } + } + + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::CloseSession( + FLMUINT uiId) +{ + FLMUINT uiSlot = (0x0000FFFF & uiId); + FLMBOOL bLocked = FALSE; + FSV_SESN * pSession = NULL; + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Lock the context mutex + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + // Make sure that the slot is valid. + + if (uiSlot >= m_uiMaxSessions) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Get a pointer to the table entry. + + if ((pSession = m_paSessions[uiSlot]) == NULL) + { + + // Session already closed + + goto Exit; + } + + // Verify the session ID. + + if (pSession->getId() != uiId) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Free the session. + + pSession->Release(); + + // Reset the table entry. + + m_paSessions[uiSlot] = NULL; + +Exit: + + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::GetSession( + FLMUINT uiId, + FSV_SESN ** ppSession) +{ + FLMUINT uiSlot = (0x0000FFFF & uiId); + FLMBOOL bLocked = FALSE; + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Lock the context mutex + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + // Make sure that the slot is valid. + + if (uiSlot >= m_uiMaxSessions) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Get a pointer to the entry in the session table. + + if ((*ppSession = m_paSessions[uiSlot]) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Verify the session ID. + + if ((*ppSession)->getId() != uiId) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::SetBasePath( + const char * pszServerBasePath) +{ + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Lock the context mutex + + f_mutexLock( m_hMutex); + + // Set the server's base path. + + if (pszServerBasePath) + { + f_strcpy( m_szServerBasePath, pszServerBasePath); + } + else + { + m_szServerBasePath[0] = '\0'; + } + + // Unlock the context mutex + + f_mutexUnlock( m_hMutex); + return (FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::GetBasePath( + char * pszServerBasePath) +{ + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Lock the context mutex + + f_mutexLock( m_hMutex); + + // Copy the base path. + + f_strcpy( pszServerBasePath, m_szServerBasePath); + + // Unlock the context mutex + + f_mutexUnlock( m_hMutex); + return (FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::BuildFilePath( + const FLMUNICODE * puzUrlString, + char * pszFilePathRV) +{ + RCODE rc = FERR_OK; + char szBasePath[F_PATH_MAX_SIZE]; + FUrl Url; + char * pucAsciiUrl; + const char * pszFile; + POOL tmpPool; + + // Initialize a temporary pool. + + GedPoolInit( &tmpPool, 256); + + // Attempt to convert the UNICODE URL to a native string + + if (RC_BAD( rc = fcsConvertUnicodeToNative( &tmpPool, puzUrlString, + &pucAsciiUrl))) + { + goto Exit; + } + + // Parse the URL. + + if (RC_BAD( rc = Url.SetUrl( pucAsciiUrl))) + { + goto Exit; + } + + pszFile = Url.GetFile(); + + if (Url.GetRelative()) + { + + // Get the server's base path. + + GetBasePath( szBasePath); + + // Build the database path. + + f_strcpy( pszFilePathRV, szBasePath); + if (RC_BAD( rc = f_pathAppend( pszFilePathRV, pszFile))) + { + goto Exit; + } + } + else + { + + // Absolute path. Use the path "as-is." + + f_strcpy( pszFilePathRV, pszFile); + } + +Exit: + + GedPoolFree( &tmpPool); + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SCTX::SetTempDir( + const char * pszTempDir) +{ + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Set the temporary directory. There is no need to lock the context + // semaphore because the state of the context is not being changed. + + if (RC_BAD( rc = FlmConfig( FLM_TMPDIR, (void *) pszTempDir, 0))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FSV_SCTX::LogMessage( + FSV_SESN * pSession, + const char * pucMsg, + RCODE rc, + FLMUINT uiMsgSeverity) +{ + if (m_pLogFunc) + { + f_mutexLock( m_hMutex); + m_pLogFunc( pucMsg, rc, uiMsgSeverity, (void *) pSession); + f_mutexUnlock( m_hMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSV_SESN::FSV_SESN(void) +{ + m_pServerContext = NULL; + m_hDb = HFDB_NULL; + m_uiSessionId = FCS_INVALID_ID; + m_uiCookie = 0; + m_uiFlags = 0; + m_pBIStream = NULL; + m_pBOStream = NULL; + m_bSetupCalled = FALSE; + m_uiClientProtocolVersion = 0; + GedPoolInit( &m_wireScratchPool, 2048); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSV_SESN::~FSV_SESN(void) +{ + FLMUINT uiLoop; + + if (m_bSetupCalled) + { + + // Free iterator resources. + + for (uiLoop = 0; uiLoop < MAX_SESN_ITERATORS; uiLoop++) + { + if (m_IteratorList[uiLoop] != HFCURSOR_NULL) + { + (void) FlmCursorFree( &m_IteratorList[uiLoop]); + } + } + + // Close the database + + if (m_hDb != HFDB_NULL) + { + (void) FlmDbClose( &m_hDb); + } + + // Free the buffer streams + + if (m_pBIStream) + { + m_pBIStream->Release(); + } + + if (m_pBOStream) + { + m_pBOStream->Release(); + } + } + + GedPoolFree( &m_wireScratchPool); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::Setup( + FSV_SCTX * pServerContext, + FLMUINT uiVersion, + FLMUINT uiFlags) +{ + FLMUINT uiLoop; + RCODE rc = FERR_OK; + + // Make sure that setup has not been called. + + flmAssert( m_bSetupCalled == FALSE); + + // Verify that the requested version is supported. + + if (uiVersion > FCS_VERSION_1_1_1) + { + rc = RC_SET( FERR_UNSUPPORTED_VERSION); + goto Exit; + } + + m_uiClientProtocolVersion = uiVersion; + + // Set the server context. + + m_pServerContext = pServerContext; + + // Initialize the iterator list + + for (uiLoop = 0; uiLoop < MAX_SESN_ITERATORS; uiLoop++) + { + m_IteratorList[uiLoop] = HFCURSOR_NULL; + } + + m_bSetupCalled = TRUE; + m_uiFlags = uiFlags; + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::OpenDatabase( + FLMUNICODE * puzDbPath, + FLMUNICODE * puzDataDir, + FLMUNICODE * puzRflDir, + FLMUINT uiOpenFlags) +{ + RCODE rc = FERR_OK; + char * pszDbPath = NULL; + char * pszDataDir; + char * pszRflDir; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_hDb == HFDB_NULL); + + if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 3, &pszDbPath))) + { + goto Exit; + } + + pszDataDir = pszDbPath + F_PATH_MAX_SIZE; + pszRflDir = pszDataDir + F_PATH_MAX_SIZE; + + // Perform some sanity checking. + + if (!puzDbPath) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Convert the UNICODE URL to a server path. + + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDbPath, pszDbPath))) + { + goto Exit; + } + + // Convert the data directory + + if (puzDataDir) + { + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDataDir, pszDataDir))) + { + goto Exit; + } + } + else + { + pszDataDir = NULL; + } + + // Convert the RFL path + + if (puzRflDir) + { + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzRflDir, pszRflDir))) + { + goto Exit; + } + } + else + { + *pszRflDir = 0; + } + + // Open the database. + + if (RC_BAD( rc = FlmDbOpen( pszDbPath, pszDataDir, pszRflDir, uiOpenFlags, + NULL, &m_hDb))) + { + goto Exit; + } + +Exit: + + if (pszDbPath) + { + f_free( &pszDbPath); + } + + // Free resources + + if (RC_BAD( rc)) + { + if (m_hDb != HFDB_NULL) + { + (void) FlmDbClose( &m_hDb); + } + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::CreateDatabase( + FLMUNICODE * puzDbPath, + FLMUNICODE * puzDataDir, + FLMUNICODE * puzRflDir, + FLMUNICODE * puzDictPath, + FLMUNICODE * puzDictBuf, + CREATE_OPTS * pCreateOpts) +{ + RCODE rc = FERR_OK; + POOL tmpPool; + char * pucDictBuf = NULL; + char * pszDbPath = NULL; + char * pszDataDir; + char * pszRflDir; + char * pszDictPath; + + // Initialize a temporary pool. + + GedPoolInit( &tmpPool, 1024); + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_hDb == HFDB_NULL); + + if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 4, &pszDbPath))) + { + goto Exit; + } + + pszDataDir = pszDbPath + F_PATH_MAX_SIZE; + pszRflDir = pszDataDir + F_PATH_MAX_SIZE; + pszDictPath = pszRflDir + F_PATH_MAX_SIZE; + + // Perform some sanity checking. + + if (!puzDbPath) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Convert the DB URL to a server path. + + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDbPath, pszDbPath))) + { + goto Exit; + } + + // Convert the dictionary URL to a server path. + + if (puzDictPath) + { + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDictPath, pszDictPath + ))) + { + goto Exit; + } + } + else + { + pszDictPath = NULL; + } + + // Convert the data directory + + if (puzDataDir) + { + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDataDir, pszDataDir))) + { + goto Exit; + } + } + else + { + pszDataDir = NULL; + } + + // Convert the RFL path + + if (puzRflDir) + { + if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzRflDir, pszRflDir))) + { + goto Exit; + } + } + else + { + *pszRflDir = 0; + } + + // Attempt to convert the UNICODE dictionary buffer to a native string + + if (puzDictBuf) + { + if (RC_BAD( rc = fcsConvertUnicodeToNative( &tmpPool, puzDictBuf, + &pucDictBuf))) + { + goto Exit; + } + } + + // Create the database. + + if (RC_BAD( rc = FlmDbCreate( pszDbPath, pszDataDir, pszRflDir, pszDictPath, + pucDictBuf, pCreateOpts, &m_hDb))) + { + goto Exit; + } + +Exit: + + if (pszDbPath) + { + f_free( &pszDbPath); + } + + // Free resources + + if (RC_BAD( rc)) + { + if (m_hDb != HFDB_NULL) + { + (void) FlmDbClose( &m_hDb); + } + } + + GedPoolFree( &tmpPool); + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::CloseDatabase(void) +{ + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Close the database. + + if (m_hDb != HFDB_NULL) + { + if (RC_BAD( rc = FlmDbClose( &m_hDb))) + { + goto Exit; + } + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::InitializeIterator( + FLMUINT * puiIteratorIdRV, + HFDB hDb, + FLMUINT uiContainer, + HFCURSOR * phIteratorRV) +{ + HFCURSOR hIterator = HFCURSOR_NULL; + FLMUINT uiSlot; + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Set the iterator id. + + *puiIteratorIdRV = FCS_INVALID_ID; + + // Find a slot in the session's iterator table + + for (uiSlot = 0; uiSlot < MAX_SESN_ITERATORS; uiSlot++) + { + if (m_IteratorList[uiSlot] == HFCURSOR_NULL) + { + break; + } + } + + // Too many open iterators + + if (uiSlot == MAX_SESN_ITERATORS) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + // Initialize a new iterator (cursor). + + if (RC_BAD( rc = FlmCursorInit( hDb, uiContainer, &hIterator))) + { + goto Exit; + } + + // Add the iterator to the iterator list. + + m_IteratorList[uiSlot] = hIterator; + *puiIteratorIdRV = uiSlot; + +Exit: + + // Free resources + + if (RC_BAD( rc)) + { + if (hIterator != HFCURSOR_NULL) + { + (void) FlmCursorFree( &hIterator); + } + } + else + { + if (phIteratorRV) + { + *phIteratorRV = hIterator; + } + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::FreeIterator( + FLMUINT uiIteratorId) +{ + HFCURSOR hIterator = HFCURSOR_NULL; + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + // Find the iterator in the resource bag and remove it. + + if (uiIteratorId >= MAX_SESN_ITERATORS || + m_IteratorList[uiIteratorId] == HFCURSOR_NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + hIterator = m_IteratorList[uiIteratorId]; + m_IteratorList[uiIteratorId] = HFCURSOR_NULL; + + // Free the iterator. + + if (RC_BAD( rc = FlmCursorFree( &hIterator))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::GetIterator( + FLMUINT uiIteratorId, + HFCURSOR * phIteratorRV) +{ + RCODE rc = FERR_OK; + + // Make sure that setup has been called. + + flmAssert( m_bSetupCalled == TRUE); + + if (uiIteratorId >= MAX_SESN_ITERATORS || + m_IteratorList[uiIteratorId] == HFCURSOR_NULL) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + *phIteratorRV = m_IteratorList[uiIteratorId]; + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::GetBIStream( + FCS_BIOS ** ppBIStream) +{ + RCODE rc = FERR_OK; + + *ppBIStream = NULL; + + if (!m_pBIStream) + { + m_pBIStream = f_new FCS_BIOS; + if (!m_pBIStream) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + *ppBIStream = m_pBIStream; + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_SESN::GetBOStream( + FCS_BIOS ** ppBOStream) +{ + RCODE rc = FERR_OK; + + *ppBOStream = NULL; + + if (!m_pBOStream) + { + m_pBOStream = f_new FCS_BIOS; + if (!m_pBOStream) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + *ppBOStream = m_pBOStream; + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE flmStreamEventDispatcher( + FCS_BIOS * pStream, + FLMUINT uiEvent, + void * UserData) +{ + CS_CONTEXT * pCSContext = (CS_CONTEXT *) UserData; + FLMUINT uiStreamHandlerId = FSEV_HANDLER_UNKNOWN; + RCODE rc = FERR_OK; + + // Determine the handler + + if (pCSContext->uiStreamHandlerId == FSEV_HANDLER_UNKNOWN) + { + if (f_stricmp( pCSContext->pucAddr, "DS") == 0) + { + uiStreamHandlerId = FSEV_HANDLER_DS; + } + else if (f_stricmp( pCSContext->pucAddr, "LOOPBACK") == 0) + { + uiStreamHandlerId = FSEV_HANDLER_LOOPBACK; + } + + pCSContext->uiStreamHandlerId = uiStreamHandlerId; + } + else + { + uiStreamHandlerId = pCSContext->uiStreamHandlerId; + } + + // Invoke the handler + + switch (uiStreamHandlerId) + { + case FSEV_HANDLER_LOOPBACK: + { + if (RC_BAD( rc = fsvStreamLoopback( pStream, uiEvent, UserData))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + } + + // Release CPU to prevent CPU hog + + f_yieldCPU(); + +Exit: + + if (RC_BAD( rc)) + { + + // Clear the saved handler ID in case a new handler is tried + + pCSContext->uiStreamHandlerId = FSEV_HANDLER_UNKNOWN; + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvStreamLoopback( + FCS_BIOS * pStream, + FLMUINT uiEvent, + void * UserData) +{ + CS_CONTEXT * pCSContext = (CS_CONTEXT *) UserData; + FCS_DIS dataIStream; + FCS_DOS dataOStream; + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( pStream); + + if (uiEvent == FCS_BIOS_EOM_EVENT) + { + if (RC_BAD( rc = dataIStream.setup( (FCS_BIOS *) (pCSContext->pOStream)))) + { + goto Exit; + } + + if (RC_BAD( rc = dataOStream.setup( (FCS_BIOS *) (pCSContext->pIStream)))) + { + goto Exit; + } + + if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, + &(pCSContext->pool), NULL))) + { + goto Exit; + } + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvStartTcpListener( + FLMUINT uiPort) +{ + RCODE rc = FERR_OK; + + if (RC_BAD( rc = f_threadCreate( &gv_pTcpListenerThrd, fsvTcpListener, + "DB TCP Listener", FLM_DEFAULT_THREAD_GROUP, 0, + (void *) uiPort))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void fsvShutdownTcpListener(void) +{ + f_threadDestroy( &gv_pTcpListenerThrd); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvTcpListener( + F_Thread * pThread) +{ + RCODE rc = FERR_OK; + FCS_TCP_SERVER * pServer; + FCS_TCP * pClient = NULL; + F_Thread * pVultureThread = NULL; + F_MUTEX hHandlerSem = F_MUTEX_NULL; + FLMUINT uiLoop; + FLMUINT uiPort = (FLMUINT) pThread->getParm1(); + + // Initialize TCP + + if ((pServer = f_new FCS_TCP_SERVER) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = pServer->bind( uiPort))) + { + gv_bTcpAllowConnections = FALSE; + goto Exit; + } + + for (uiLoop = 0; uiLoop < FSV_MAX_TCP_HANDLERS; uiLoop++) + { + gv_TcpHandlers[uiLoop] = NULL; + } + + if (RC_BAD( rc = f_mutexCreate( &hHandlerSem))) + { + goto Exit; + } + + if (RC_BAD( rc = f_threadCreate( &pVultureThread, fsvTcpVulture, + "DB TCP Vulture", FLM_DEFAULT_THREAD_GROUP, 0, + (void *) (&hHandlerSem)))) + { + goto Exit; + } + + gv_bTcpRunning = TRUE; + + for (;;) + { + if (pThread->getShutdownFlag()) + { + goto Exit; + } + + if (pClient == NULL) + { + if ((pClient = f_new FCS_TCP) == NULL) + { + f_sleep( 100); + continue; + } + } + + // See if a client is waiting to connect. + + if (RC_OK( rc = pServer->connectClient( pClient, 2, 1200))) + { + if (RC_BAD( fsvTcpAcceptConnection( &hHandlerSem, pClient))) + { + pClient->Release(); + } + + pClient = NULL; + } + else + { + if (rc != FERR_SVR_READ_TIMEOUT) + { + // Drop the current client. + + pClient->Release(); + pClient = NULL; + + // Re-initialize the listener. + + pServer->Release(); + pServer = f_new FCS_TCP_SERVER; + flmAssert( pServer != NULL); + + if (RC_BAD( rc = pServer->bind( uiPort))) + { + gv_bTcpAllowConnections = FALSE; + } + } + } + } + +Exit: + + // Shut down all threads and free any allocated resources + + f_threadDestroy( &pVultureThread); + + if (pClient) + { + pClient->Release(); + } + + if (pServer) + { + pServer->Release(); + } + + gv_bTcpRunning = FALSE; + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvTcpVulture( + F_Thread * pThread) +{ + F_MUTEX * phHandlerMutex = (F_MUTEX *) pThread->getParm1(); + FLMUINT uiLoop; + FLMUINT uiActiveThreads; + FLMUINT uiRetry; + FLMBOOL bShutdown = FALSE; + + while (!bShutdown) + { + if (pThread->getShutdownFlag()) + { + bShutdown = TRUE; + break; + } + + f_mutexLock( *phHandlerMutex); + + uiActiveThreads = 0; + uiLoop = 0; + while (uiLoop < FSV_MAX_TCP_HANDLERS) + { + if (gv_TcpHandlers[uiLoop] != NULL) + { + if (!gv_TcpHandlers[uiLoop]->isThreadRunning()) + { + f_threadDestroy( &(gv_TcpHandlers[uiLoop])); + } + else + { + uiActiveThreads++; + } + } + + uiLoop++; + } + + f_mutexUnlock( *phHandlerMutex); + + for (uiLoop = 0; uiLoop < 100; uiLoop++) + { + if (pThread->getShutdownFlag()) + { + bShutdown = TRUE; + break; + } + + f_sleep( 100); + } + } + + uiRetry = 0; + while (uiRetry < 60) + { + uiActiveThreads = 0; + uiLoop = 0; + while (uiLoop < FSV_MAX_TCP_HANDLERS) + { + if (gv_TcpHandlers[uiLoop] != NULL) + { + if (!gv_TcpHandlers[uiLoop]->isThreadRunning()) + { + f_threadDestroy( &(gv_TcpHandlers[uiLoop])); + } + else + { + gv_TcpHandlers[uiLoop]->setShutdownFlag(); + uiActiveThreads++; + } + } + + uiLoop++; + } + + if (uiActiveThreads == 0) + { + break; + } + + f_sleep( 1000); + uiRetry++; + } + + return (FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvTcpAcceptConnection( + F_MUTEX * phHandlerMutex, + FCS_TCP * pClient) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiLoop; + F_Thread * pClientThrd; + + f_mutexLock( *phHandlerMutex); + bMutexLocked = TRUE; + + uiLoop = 0; + while (uiLoop < FSV_MAX_TCP_HANDLERS) + { + if (gv_TcpHandlers[uiLoop] == NULL) + { + break; + } + + uiLoop++; + } + + if (uiLoop < FSV_MAX_TCP_HANDLERS) + { + if (RC_BAD( rc = f_threadCreate( &pClientThrd, fsvTcpClientHandler, + "DB TCP Handler", FLM_DEFAULT_THREAD_GROUP, 0, pClient))) + { + goto Exit; + } + + gv_TcpHandlers[uiLoop] = pClientThrd; + } + else + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( *phHandlerMutex); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE fsvTcpClientHandler( + F_Thread * pThread) +{ + RCODE rc = FERR_OK; + FCS_TCP * pClient = (FCS_TCP *) pThread->getParm1(); + FCS_IPIS * pIpIStream; + FCS_IPOS * pIpOStream = NULL; + FCS_DIS * pDataIStream = NULL; + FCS_DOS * pDataOStream = NULL; + FLMUINT uiSessionId = FCS_INVALID_ID; + POOL pool; + + // Initialize the scratch pool + + GedPoolInit( &pool, 2048); + + // Allocate required objects. + + if ((pIpIStream = f_new FCS_IPIS( pClient)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if ((pIpOStream = f_new FCS_IPOS( pClient)) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if ((pDataIStream = f_new FCS_DIS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if ((pDataOStream = f_new FCS_DOS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + for (;;) + { + if (pThread->getShutdownFlag()) + { + goto Exit; + } + + if (RC_BAD( rc = pClient->socketPeekRead( 5))) + { + if (rc != FERR_SVR_READ_TIMEOUT) + { + goto Exit; + } + } + else + { + + // Configure the data input stream. + + if (RC_BAD( rc = pDataIStream->setup( pIpIStream))) + { + goto Exit; + } + + // Configure the data output stream. + + if (RC_BAD( rc = pDataOStream->setup( pIpOStream))) + { + goto Exit; + } + + // Process the request. + + if (RC_BAD( rc = fsvProcessRequest( pDataIStream, pDataOStream, &pool, + &uiSessionId))) + { + goto Exit; + } + } + } + +Exit: + + if (pDataIStream) + { + pDataIStream->Release(); + } + + if (pDataOStream) + { + pDataOStream->Release(); + } + + if (pIpIStream) + { + pIpIStream->Release(); + } + + if (pIpOStream) + { + pIpOStream->Release(); + } + + if (pClient) + { + pClient->Release(); + } + + if (RC_BAD( rc) && uiSessionId != FCS_INVALID_ID) + { + FSV_SCTX * pServerContext = NULL; + + // Close the session and release any resources held by the client + // (open transactions, etc.) + + if (RC_OK( fsvGetGlobalContext( &pServerContext))) + { + pServerContext->CloseSession( uiSessionId); + } + } + + GedPoolFree( &pool); + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FSV_WIRE::reset(void) +{ + resetCommon(); + m_uiOpSeqNum = 0; + m_uiClientVersion = 0; + m_uiAutoTrans = 0; + m_uiMaxLockWait = 0; + m_puzDictPath = NULL; + m_puzDictBuf = NULL; + m_puzFileName = NULL; + m_pucPassword = NULL; + m_pDrnList = NULL; + m_uiAreaId = 0; + m_pIteratorSelect = NULL; + m_pIteratorFrom = NULL; + m_pIteratorWhere = NULL; + m_pIteratorConfig = NULL; + m_pSession = NULL; + m_hIterator = HFCURSOR_NULL; + m_uiType = 0; + m_bSendGedcom = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FSV_WIRE::setSession( + FSV_SESN * pSession) +{ + m_pSession = pSession; + + // See if GEDCOM is supported by the client + + if (m_pSession && (m_pSession->getFlags() & FCS_SESSION_GEDCOM_SUPPORT)) + { + m_bSendGedcom = TRUE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FSV_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))) + { + 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_OP_SEQ_NUM: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiOpSeqNum, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_CLIENT_VERSION: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiClientVersion, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_DICT_FILE_PATH: + { + if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzDictPath))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_DICT_BUFFER: + { + if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzDictBuf))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_PASSWORD: + { + if (RC_BAD( rc = m_pDIStream->readBinary( m_pPool, + &m_pucPassword, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_TYPE: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiType, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_AREA_ID: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiAreaId, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_FILE_NAME: + { + if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzFileName))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_AUTOTRANS: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiAutoTrans, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_ITERATOR_SELECT: + { + if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, + &m_pIteratorSelect, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_ITERATOR_FROM: + { + if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, + &m_pIteratorFrom, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_ITERATOR_WHERE: + { + if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, + &m_pIteratorWhere, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_ITERATOR_CONFIG: + { + if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, + &m_pIteratorConfig, NULL))) + { + goto Exit; + } + break; + } + + case WIRE_VALUE_MAX_LOCK_WAIT: + { + if (RC_BAD( rc = readNumber( uiTag, &m_uiMaxLockWait, NULL))) + { + goto Exit; + } + break; + } + + default: + { + if (RC_BAD( rc = skipValue( uiTag))) + { + goto Exit; + } + break; + } + } + } + } + +Exit: + + return (rc); +} diff --git a/flaim/src/ftext.cpp b/flaim/src/ftext.cpp new file mode 100644 index 0000000..358e69a --- /dev/null +++ b/flaim/src/ftext.cpp @@ -0,0 +1,4812 @@ +//------------------------------------------------------------------------- +// Desc: FLAIM text routines and conversion tables +// Tabs: 3 +// +// Copyright (c) 1991-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$ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define UTOWP60_ENTRIES 1502 +#define WP60toUni_MAX 15 + +#define Upper_JP_A 0x2520 +#define Upper_JP_Z 0x2539 +#define Upper_KR_A 0x5420 +#define Upper_KR_Z 0x5439 +#define Upper_CS_A 0x82FC +#define Upper_CS_Z 0x8316 +#define Upper_CT_A 0xA625 +#define Upper_CT_Z 0xA63E + +#define Lower_JP_a 0x2540 +#define Lower_JP_z 0x2559 +#define Lower_KR_a 0x5440 +#define Lower_KR_z 0x5459 +#define Lower_CS_a 0x82DC +#define Lower_CS_z 0x82F5 +#define Lower_CT_a 0xA60B +#define Lower_CT_z 0xA624 + +// Number of characters in each character set + +#define ASC_N 95 +#define ML1_N 242 +#define ML2_N 145 +#define BOX_N 88 +#define TYP_N 103 +#define ICN_N 255 +#define MTH_N 238 +#define MTX_N 229 +#define GRK_N 219 +#define HEB_N 123 +#define CYR_N 250 +#define KAN_N 63 +#define USR_N 255 +#define ARB_N 196 +#define ARS_N 220 + +// TOTAL: 1447 WP + 255 User Characters + +#define C_N ASC_N + ML1_N + ML2_N + BOX_N + MTH_N + \ + MTX_N + TYP_N + ICN_N + GRK_N + \ + HEB_N + CYR_N + KAN_N + USR_N + ARB_N + \ + ARS_N + +// Misc + +#define WPCH_HIMASK 0x00FF +#define WPCH_LOMASK 0xFF00 +#define WPCH_MAX_COMPLEX 5 +#define NON_DISPLAYABLE_CHAR 0xFF + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct BASE_DIACRIT_TABLE +{ + FLMBYTE base; + FLMBYTE diacrit; +} BASE_DIACRIT_TABLE; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct BASE_DIACRIT +{ + FLMUINT16 char_count; // Number of characters in table + FLMUINT16 start_char; // Start char + BASE_DIACRIT_TABLE * table; +} BASE_DIACRIT; + +/**************************************************************************** +Desc: Defines the range of characters within the set which are case + convertible +****************************************************************************/ +static FLMBYTE flmCaseConvertableRange[] = +{ + 26, 241, // Multinational 1 + 0, 0, // Multinational 2 + 0, 0, // Box Drawing + 0, 0, // Symbol 1 + 0, 0, // Symbol 2 + 0, 0, // Math 1 + 0, 0, // Math 2 + 0, 69, // Greek 1 + 0, 0, // Hebrew + 0, 199, // Cyrillic + 0, 0, // Japanese Kana + 0, 0, // User-defined + 0, 0, // Not defined + 0, 0, // Not defined + 0, 0, // Not defined +}; + +/**************************************************************************** +Desc: Base character location bit mapped table. + (1) - corresponding base char is in same set as combined + (0) - corresponding base char is in ascii set +****************************************************************************/ +FLMBYTE flm_ml1_cb60[] = +{ + 0x00, // 0-7 + 0x00, // 8-15 + 0x00, // 16-23 + 0x00, // 24-31 + 0x00, // 32-39 + 0x00, // 40-47 + 0x55, // 48-55 + 0x00, // 56-63 + 0x00, // 64-71 + 0x00, // 72-79 + 0x00, // 80-87 + 0x00, // 88-95 + 0x00, // 96-103 + 0x00, // 104-111 + 0x00, // 112-119 + 0x00, // 120-127 + 0x14, // 128-135 + 0x44, // 136-143 + 0x00, // 144-151 + 0x00, // 152-159 + 0x00, // 160-167 + 0x00, // 168-175 + 0x00, // 176-183 + 0x00, // 184-191 + 0x00, // 192-199 + 0x00, // 200-207 + 0x00, // 208-215 + 0x00, // 216-223 + 0x00, // 224-231 + 0x04, // 232-239 + 0x00, // 240-241 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +BASE_DIACRIT_TABLE flm_ml1c_table[] = +{ + { 'A', acute }, + { 'a', acute }, + { 'A', circum }, + { 'a', circum }, + { 'A', umlaut }, + { 'a', umlaut }, + { 'A', grave }, + { 'a', grave }, + { 'A', ring }, + { 'a', ring }, + { 0xff, 0xff }, + { 0xff, 0xff }, + { 'C',cedilla}, + { 'c',cedilla}, + { 'E',acute}, + { 'e',acute}, + { 'E',circum}, + { 'e',circum}, + { 'E',umlaut}, + { 'e',umlaut}, + { 'E',grave}, + { 'e',grave}, + { 'I',acute}, + { dotlesi,acute}, + { 'I',circum}, + { dotlesi,circum}, + { 'I',umlaut}, + { dotlesi,umlaut}, + { 'I',grave}, + { dotlesi,grave}, + { 'N',tilde}, + { 'n',tilde}, + { 'O',acute}, + { 'o',acute}, + { 'O',circum}, + { 'o',circum}, + { 'O',umlaut}, + { 'o',umlaut}, + { 'O',grave}, + { 'o',grave}, + { 'U',acute}, + { 'u',acute}, + { 'U',circum}, + { 'u',circum}, + { 'U',umlaut}, + { 'u',umlaut}, + { 'U',grave}, + { 'u',grave}, + { 'Y',umlaut}, + { 'y',umlaut}, + { 'A',tilde}, + { 'a',tilde}, + { 'D',crossb}, + { 'd',crossb}, + { 'O',slash}, + { 'o',slash}, + { 'O',tilde}, + { 'o',tilde}, + { 'Y',acute}, + { 'y',acute}, + { 0xff,0xff}, + { 0xff,0xff}, + { 0xff,0xff}, + { 0xff,0xff}, + { 'A',breve}, + { 'a',breve}, + { 'A',macron}, + { 'a',macron}, + { 'A',ogonek}, + { 'a',ogonek}, + { 'C',acute}, + { 'c',acute}, + { 'C',caron}, + { 'c',caron}, + { 'C',circum}, + { 'c',circum}, + { 'C',dota}, + { 'c',dota}, + { 'D',caron}, + { 'd',caron}, + { 'E',caron}, + { 'e',caron}, + { 'E',dota}, + { 'e',dota}, + { 'E',macron}, + { 'e',macron}, + { 'E',ogonek}, + { 'e',ogonek}, + { 'G',acute}, + { 'g',acute}, + { 'G',breve}, + { 'g',breve}, + { 'G',caron}, + { 'g',caron}, + { 'G',cedilla}, + { 'g',aposab}, + { 'G',circum}, + { 'g',circum}, + { 'G',dota}, + { 'g',dota}, + { 'H',circum}, + { 'h',circum}, + { 'H',crossb}, + { 'h',crossb}, + { 'I',dota}, + { dotlesi,dota}, + { 'I',macron}, + { dotlesi,macron}, + { 'I',ogonek}, + { 'i',ogonek}, + { 'I',tilde}, + { dotlesi,tilde}, + { 0xff,0xff}, + { 0xff,0xff}, + { 'J',circum}, + { dotlesj,circum}, + { 'K',cedilla}, + { 'k',cedilla}, + { 'L',acute}, + { 'l',acute}, + { 'L',caron}, + { 'l',caron}, + { 'L',cedilla}, + { 'l',cedilla}, + { 'L',centerd}, + { 'l',centerd}, + { 'L',stroke}, + { 'l',stroke}, + { 'N',acute}, + { 'n',acute}, + { 'N',aposba}, + { 'n',aposba}, + { 'N',caron}, + { 'n',caron}, + { 'N',cedilla}, + { 'n',cedilla}, + { 'O',dacute}, + { 'o',dacute}, + { 'O',macron}, + { 'o',macron}, + { 0xff,0xff}, + { 0xff,0xff}, + { 'R',acute}, + { 'r',acute}, + { 'R',caron}, + { 'r',caron}, + { 'R',cedilla}, + { 'r',cedilla}, + { 'S',acute}, + { 's',acute}, + { 'S',caron}, + { 's',caron}, + { 'S',cedilla}, + { 's',cedilla}, + { 'S',circum}, + { 's',circum}, + { 'T',caron}, + { 't',caron}, + { 'T',cedilla}, + { 't',cedilla}, + { 'T',crossb}, + { 't',crossb}, + { 'U',breve}, + { 'u',breve}, + { 'U',dacute}, + { 'u',dacute}, + { 'U',macron}, + { 'u',macron}, + { 'U',ogonek}, + { 'u',ogonek}, + { 'U',ring}, + { 'u',ring}, + { 'U',tilde}, + { 'u',tilde}, + { 'W',circum}, + { 'w',circum}, + { 'Y',circum}, + { 'y',circum}, + { 'Z',acute}, + { 'z',acute}, + { 'Z',caron}, + { 'z',caron}, + { 'Z',dota}, + { 'z',dota}, + { 0xff,0xff}, + { 0xff,0xff}, + { 'D',macron}, + {'d',macron}, + {'L',macron}, + {'l',macron}, + {'N',macron}, + {'n',macron}, + {'R',grave}, + {'r',grave}, + {'S',macron}, + {'s',macron}, + {'T',macron}, + {'t',macron}, + {'Y',breve}, + {'y',breve}, + {'Y',grave}, + {'y',grave}, + {'D',aposbes}, + {'d',aposbes}, + {'O',aposbes}, + {'o',aposbes}, + {'U',aposbes}, + {'u',aposbes}, + {'E',breve}, + {'e',breve}, + {'I',breve}, + {dotlesi,breve}, + {0xff,0xff}, + {0xff,0xff}, + {'O',breve}, + {'o',breve} +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +BASE_DIACRIT flm_ml1c = +{ + 216, // Number of characters in table + 26, // Start char + flm_ml1c_table, +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT_TABLE flm_grk_c_table[] = +{ + { 0, ghprime }, // ALPHA High Prime + { 1, gacute }, // alpha acute + { 10, ghprime }, // EPSILON High Prime + { 11, gacute }, // epsilon Acute + { 14, ghprime }, // ETA High Prime + { 15, gacute }, // eta Acute + { 18, ghprime }, // IOTA High Prime + { 19, gacute }, // iota Acute + { 0xFF, 0xFF }, // IOTA Diaeresis + { 19, gdia }, // iota Diaeresis + { 30, ghprime }, // OMICRON High Prime + { 31, gacute }, // omicron Acute + { 42, ghprime }, // UPSILON High Prime + { 43, gacute }, // upsilon Acute + { 0xFF, 0xFF }, // UPSILON Diaeresis + { 43,gdia }, // upsilon Diaeresis + { 50,ghprime }, // OMEGA High Prime + { 51,gacute }, // omega Acute + { 0xFF, 0xFF }, // epsilon (Variant) + { 0xFF, 0xFF }, // theta (Variant) + { 0xFF, 0xFF }, // kappa (Variant) + { 0xFF, 0xFF }, // pi (Variant) + { 0xFF, 0xFF }, // rho (Variant) + { 0xFF, 0xFF }, // sigma (Variant) + { 0xFF, 0xFF }, // UPSILON (Variant) + { 0xFF, 0xFF }, // phi (Variant) + { 0xFF, 0xFF }, // omega (Variant) + { 0xFF, 0xFF }, // Greek Question Mark + { 0xFF, 0xFF }, // Greek Semicolon + { 0xFF, 0xFF }, // High Prime + { 0xFF, 0xFF }, // Low Prime + { 0xFF, 0xFF }, // Acute (Greek) + { 0xFF, 0xFF }, // Diaeresis (Greek) + { gacute,gdia }, // Acute Diaeresis + { ggrave, gdia }, // Grave Diaeresis + { 0xFF, 0xFF }, // Grave (Greek) + { 0xFF, 0xFF }, // Circumflex (Greek) + { 0xFF, 0xFF }, // Smooth Breathing + { 0xFF, 0xFF }, // Rough Breathing + { 0xFF, 0xFF }, // Iota Subscript + { gsmooth, gacute }, // Smooth Breathing Acute + { grough, gacute }, // Rough Breathing Acute + { gsmooth, ggrave }, // Smooth Breathing Grave + { grough, ggrave }, // Rough Breathing Grave + { gsmooth, gcircm }, // Smooth Breathing Circumflex + { grough, gcircm }, // Rough Breathing Circumflex + { gacute, giota }, // Acute w/Iota Subscript + { ggrave, giota }, // Grave w/Iota Subscript + { gcircm, giota }, // Circumflex w/Iota Subscript + { gsmooth, giota }, // Smooth Breathing w/Iota Subscript + { grough, giota }, // Rough Breathing w/Iota Subscript + { gsmact, giota }, // Smooth Breathing Acute w/Iota Subscript + { grgact, giota }, // Rough Breathing Acute w/Iota Subscript + { gsmgrv, giota }, // Smooth Breathing Grave w/Iota Subscript + { grggrv, giota }, // Rough Breathing Grave w/Iota Subscript + { gsmcir, giota }, // Smooth Breathing Circumflex w/Iota Sub + { grgcir, giota }, // Rough Breathing Circumflex w/Iota Sub + { 1, ggrave }, // alpha Grave + { 1, gcircm }, // alpha Circumflex + { 1, giota }, // alpha w/Iota + { 1, gactio }, // alpha Acute w/Iota + { 1, ggrvio }, // alpha Grave w/Iota + { 1, gcirio }, // alpha Circumflex w/Iota + { 1, gsmooth }, // alpha Smooth + { 1, gsmact }, // alpha Smooth Acute + { 1, gsmgrv }, // alpha Smooth Grave + { 1, gsmcir }, // alpha Smooth Circumflex + { 1, gsmio }, // alpha Smooth w/Iota + { 1, gsmaio }, // alpha Smooth Acute w/Iota + { 1, gsmgvio }, // alpha Smooth Grave w/Iota + { 1, gsmcio }, // alpha Smooth Circumflex w/Iota + { 1, grough }, // alpha Rough + { 1, grgact }, // alpha Rough Acute + { 1, grggrv }, // alpha Rough Grave + { 1, grgcir }, // alpha Rough Circumflex + { 1, grgio }, // alpha Rough w/Iota + { 1, grgaio }, // alpha Rough Acute w/Iota + { 1, grggvio }, // alpha Rough Grave w/Iota + { 1, grgcio }, // alpha Rough Circumflex w/Iota + { 11, ggrave }, // epsilon Grave + { 11, gsmooth }, // epsilon Smooth + { 11, gsmact }, // epsilon Smooth Acute + { 11, gsmgrv }, // epsilon Smooth Grave + { 11, grough }, // epsilon Rough + { 11, grgact }, // epsilon Rough Acute + { 11, grggrv }, // epsilon Rough Grave + { 15, ggrave }, // eta Grave + { 15, gcircm }, // eta Circumflex + { 15, giota }, // eta w/Iota + { 15, gactio }, // eta Acute w/Iota + { 15, ggrvio }, // eta Grave w/Iota + { 15, gcirio }, // eta Circumflex w/Iota + { 15, gsmooth }, // eta Smooth + { 15, gsmact }, // eta Smooth Acute + { 15, gsmgrv }, // eta Smooth Grave + { 15, gsmcir }, // eta Smooth Circumflex + { 15, gsmio }, // eta Smooth w/Iota + { 15, gsmaio }, // eta Smooth Acute w/Iota + { 15, gsmgvio }, // eta Smooth Grave w/Iota + { 15, gsmcio }, // eta Smooth Circumflex w/Iota + { 15, grough }, // eta Rough + { 15, grgact }, // eta Rough Acute + { 15, grggrv }, // eta Rough Grave + { 15, grgcir }, // eta Rough Circumflex + { 15, grgio }, // eta Rough w/Iota + { 15, grgaio }, // eta Rough Acute w/Iota + { 15, grggvio }, // eta Rough Grave w/Iota + { 15, grgcio }, // eta Rough Circumflex w/Iota + { 19, ggrave }, // iota Grave + { 19, gcircm }, // iota Circumflex + { 19, gactdia }, // iota Acute Diaeresis + { 19, ggrvdia }, // iota Grave Diaeresis + { 19, gsmooth }, // iota Smooth + { 19, gsmact }, // iota Smooth Acute + { 19, gsmgrv }, // iota Smooth Grave + { 19, gsmcir }, // iota Smooth Circumflex + { 19, grough }, // iota Rough + { 19, grgact }, // iota Rough Acute + { 19, grggrv }, // iota Rough Grave + { 19, grgcir }, // iota Rough Circumflex + { 31, ggrave }, // omicron Grave + { 31, gsmooth }, // omicron Smooth + { 31, gsmact }, // omicron Smooth Acute + { 31, gsmgrv }, // omicron Smooth Grave + { 31, grough }, // omicron Rough + { 31, grgact }, // omicron Rough Acute + { 31, grggrv }, // omicron Rough Grave + { 0xFF, 0xFF }, // rho rough + { 0xFF, 0xFF }, // rho smooth + { 43, ggrave }, // upsilon Grave + { 43, gcircm }, // upsilon Circumflex + { 43, gactdia }, // upsilon Acute Diaeresis + { 43, ggrvdia }, // upsilon Grave Diaeresis + { 43, gsmooth }, // upsilon Smooth + { 43, gsmact }, // upsilon Smooth Acute + { 43, gsmgrv }, // upsilon Smooth Grave + { 43, gsmcir }, // upsilon Smooth Circumflex + { 43, grough }, // upsilon Rough + { 43, grgact }, // upsilon Rough Acute + { 43, grggrv }, // upsilon Rough Grave + { 43, grgcir }, // upsilon Rough Circumflex + { 51, ggrave }, // omega Grave + { 51, gcircm }, // omega Circumflex + { 51, giota }, // omega w/Iota + { 51, gactio }, // omega Acute w/Iota + { 51, ggrvio }, // omega Grave w/Iota + { 51, gcirio }, // omega Circumflex w/Iota + { 51, gsmooth }, // omega Smooth + { 51, gsmact }, // omega Smooth Acute + { 51, gsmgrv }, // omega Smooth Grave + { 51, gsmcir }, // omega Smooth Circumflex + { 51, gsmio }, // omega Smooth w/Iota + { 51, gsmaio }, // omega Smooth Acute w/Iota + { 51, gsmgvio }, // omega Smooth Grave w/Iota + { 51, gsmcio }, // omega Smooth Circumflex w/Iota + { 51, grough }, // omega Rough + { 51, grgact }, // omega Rough Acute + { 51, grggrv }, // omega Rough Grave + { 51, grgcir }, // omega Rough Circumflex + { 51, grgio }, // omega Rough w/Iota + { 51, grgaio }, // omega Rough Acute w/Iota + { 51, grggvio }, // omega Rough Grave w/Iota + { 51, grgcio} // omega Rough Circumflex w/Iota +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT flm_grk_c = +{ + 163, // Number of characters in table + 52, // Start char + flm_grk_c_table +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT_TABLE flm_rus_c_table[] = +{ + { 14, 204 }, // ZHE with right descender + { 15, 204 }, // zhe with right descender + { 0xFF, 0xFF}, // DZE + { 0xFF, 0xFF}, // dze + { 0xFF, 0xFF}, // Z + { 0xFF, 0xFF}, // z + { 18, 206 }, // II with macron + { 19, 206}, // ii with macron + { 0xFF, 0xFF}, // I + { 0xFF, 0xFF}, // i + { 0xFF, 0xFF}, // YI + { 0xFF, 0xFF}, // yi + { 0xFF, 0xFF}, // I ligature + { 0xFF, 0xFF}, // i ligature + { 0xFF, 0xFF}, // JE + { 0xFF, 0xFF}, // je + { 0xFF, 0xFF}, // KJE + { 0xFF, 0xFF}, // kje + { 22, 204}, // KA with right descender + { 23, 204}, // ka with right descender + { 22, 205 }, // KA ogonek + { 23, 205 }, // ka ogonek + { 0xFF, 0xFF}, // KA vertical bar + { 0xFF, 0xFF}, // ka vertical bar + { 0xFF, 0xFF}, // LJE + { 0xFF, 0xFF}, // lje + { 28, 204 }, // EN with right descender + { 29, 204 }, // en with right descender + { 0xFF, 0xFF}, // NJE + { 0xFF, 0xFF}, // nje + { 0xFF, 0xFF}, // ROUND OMEGA + { 0xFF, 0xFF}, // round omega + { 0xFF, 0xFF}, // OMEGA + { 0xFF, 0xFF}, // omega + { 0xFF, 0xFF}, // TSHE + { 0xFF, 0xFF}, // tshe + { 0xFF, 0xFF}, // SHORT U + { 0xFF, 0xFF}, // short u + { 40, 206}, // U with macron + { 41, 206 }, // u with macron + { 0xFF, 0xFF}, // STRAIGHT U + { 0xFF, 0xFF}, // straight u + { 0xFF, 0xFF}, // STRAIGHT U BAR + { 0xFF, 0xFF}, // straight u bar + { 0xFF, 0xFF}, // OU ligature + { 0xFF, 0xFF}, // ou ligature + { 44, 204 }, // KHA with right descender + { 45, 204 }, // kha with right descender + { 44, 205 }, // KHA ogonek + { 45, 205 }, // kha ogonek + { 0xFF, 0xFF}, // H + { 0xFF, 0xFF}, // h + { 0xFF, 0xFF}, // OMEGA titlo + { 0xFF, 0xFF}, // omega titlo + { 0xFF, 0xFF}, // DZHE + { 0xFF, 0xFF}, // dzhe + { 48, 204 }, // CHE with right descender + { 49, 204 }, // che with right descender + { 0xFF, 0xFF}, // CHE vertical bar + { 0xFF, 0xFF}, // che vertical bar + { 0xFF, 0xFF}, // SHCHA (variant) + { 0xFF, 0xFF}, // shcha (variant) + { 0xFF, 0xFF}, // YAT + { 0xFF, 0xFF}, // yat + { 0xFF, 0xFF}, // YUS BOLSHOI + { 0xFF, 0xFF}, // yus bolshoi + { 0xFF, 0xFF}, // BIG MALYI + { 0xFF, 0xFF}, // big malyi + { 0xFF, 0xFF}, // KSI + { 0xFF, 0xFF}, // ksi + { 0xFF, 0xFF}, // PSI + { 0xFF, 0xFF}, // psi + { 0xFF, 0xFF}, // FITA + { 0xFF, 0xFF}, // fita + { 0xFF, 0xFF}, // IZHITSA + { 0xFF, 0xFF}, // izhitsa + { 00, racute}, // Russian A acute + { 01, racute }, // Russian a acute + { 10, racute }, // Russian IE acute + { 11, racute }, // Russian ie acute + { 78, racute }, // Russian E acute + { 79, racute }, // Russian e acute + { 18, racute }, // Russian II acute + { 19, racute }, // Russian ii acute + { 88, racute }, // Russian I acute + { 89, racute }, // Russian i acute + { 90, racute }, // Russian YI acute + { 91, racute }, // Russian yi acute + { 30, racute }, // Russian O acute + { 31, racute }, // Russian o acute + { 40, racute }, // Russian U acute + { 41, racute }, // Russian u acute + { 56, racute }, // Russian YERI acute + { 57, racute }, // Russian yeri acute + { 60, racute }, // Russian REVERSED E acute + { 61, racute }, // Russian reversed e acute + { 62, racute }, // Russian IU acute + { 63, racute }, // Russian iu acute + { 64, racute }, // Russian IA acute + { 65, racute }, // Russian ia acute + { 00, rgrave }, // Russian A grave + { 01, rgrave }, // Russian a grave + { 10, rgrave }, // Russian IE grave + { 11, rgrave }, // Russian ie grave + { 12, rgrave }, // Russian YO grave + { 13, rgrave }, // Russian yo grave + { 18, rgrave }, // Russian I grave + { 19, rgrave }, // Russian i grave + { 30, rgrave }, // Russian O grave + { 31, rgrave }, // Russian o grave + { 40, rgrave }, // Russian U grave + { 41, rgrave }, // Russian u grave + { 56, rgrave }, // Russian YERI grave + { 57, rgrave }, // Russian yeri grave + { 60, rgrave }, // Russian REVERSED E grave + { 61, rgrave }, // Russian reversed e grave + { 62, rgrave }, // Russian IU grave + { 63, rgrave }, // Russian iu grave + { 64, rgrave }, // Russian IA grave + { 65, rgrave} // Russian ia grave +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT flm_rus_c = { + 120, // Number of characters in table + 156, // Start char + flm_rus_c_table, +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +BASE_DIACRIT * flm_car60_c[ NCHSETS] = +{ + (BASE_DIACRIT*)0, // no composed characters for ascii + &flm_ml1c, + (BASE_DIACRIT*)0, // no composed characters for multinational 2 + (BASE_DIACRIT*)0, // no composed characters for line draw + (BASE_DIACRIT*)0, // no composed characters for typographic + (BASE_DIACRIT*)0, // no composed characters for icons + (BASE_DIACRIT*)0, // no composed characters for math + (BASE_DIACRIT*)0, // no composed characters for math extension + &flm_grk_c, // Greek + (BASE_DIACRIT*)0, // Hebrew + &flm_rus_c, // Cyrillic - Russian + (BASE_DIACRIT*)0, // Hiragana or Katakana (Japanese) + (BASE_DIACRIT*)0, // no composed characters for user + (BASE_DIACRIT*)0, // no composed characters for Arabic + (BASE_DIACRIT*)0, // no composed characters for Arabic Script +}; + +/**************************************************************************** +Desc: Number of characters in each character set +****************************************************************************/ +FLMBYTE flm_c60_max[] = +{ + ASC_N, // ascii + ML1_N, // multinational 1 + ML2_N, // multinational 2 + BOX_N, // line draw + TYP_N, // typographic + ICN_N, // icons + MTH_N, // math + MTX_N, // math extension + GRK_N, // Greek + HEB_N, // Hebrew + CYR_N, // Cyrillic - Russian + KAN_N, // Kana + USR_N, // user + ARB_N, // Arabic + ARS_N, // Arabic Script +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 WP_UTOWP60[ UTOWP60_ENTRIES][ 2] = +{ + { 0x00A1, 0x0407 }, // 7 , 4, + { 0x00A2, 0x0413 }, // 19 , 4, + { 0x00A3, 0x040b }, // 11 , 4, + { 0x00A4, 0x0418 }, // 24 , 4, + { 0x00A5, 0x040c }, // 12 , 4, + { 0x00A7, 0x0406 }, // 6 , 4, + { 0x00A9, 0x0417 }, // 23 , 4, + { 0x00AA, 0x040f }, // 15 , 4, + { 0x00AB, 0x0409 }, // 9 , 4, + { 0x00AC, 0x0614 }, // 20 , 6, + { 0x00AE, 0x0416 }, // 22 , 4, + { 0x00B0, 0x0624 }, // 36 , 6, + { 0x00B1, 0x0601 }, // 1 , 6, + { 0x00B2, 0x0414 }, // 20 , 4, + { 0x00B3, 0x041a }, // 26 , 4, + { 0x00B5, 0x0625 }, // 37 , 6, + { 0x00B6, 0x0405 }, // 5 , 4, + { 0x00B7, 0x0101 }, // 101, 1, + { 0x00B9, 0x044e }, // 78 , 4, + { 0x00BA, 0x0410 }, // 16 , 4, + { 0x00BB, 0x040a }, // 10 , 4, + { 0x00BC, 0x0412 }, // 18 , 4, + { 0x00BD, 0x0411 }, // 17 , 4, + { 0x00BE, 0x0419 }, // 25 , 4, + { 0x00BF, 0x0408 }, // 8 , 4, + { 0x00C0, 0x0120 }, // 32 , 1, + { 0x00C1, 0x011a }, // 26 , 1, + { 0x00C2, 0x011c }, // 28 , 1, + { 0x00C3, 0x014c }, // 76 , 1, + { 0x00C4, 0x011e }, // 30 , 1, + { 0x00C5, 0x0122 }, // 34 , 1, + { 0x00C6, 0x0124 }, // 36 , 1, + { 0x00C7, 0x0126 }, // 38 , 1, + { 0x00C8, 0x012e }, // 46 , 1, + { 0x00C9, 0x0128 }, // 40 , 1, + { 0x00CA, 0x012a }, // 42 , 1, + { 0x00CB, 0x012c }, // 44 , 1, + { 0x00CC, 0x0136 }, // 54 , 1, + { 0x00CD, 0x0130 }, // 48 , 1, + { 0x00CE, 0x0132 }, // 50 , 1, + { 0x00CF, 0x0134 }, // 52 , 1, + { 0x00D0, 0x0156 }, // 86 , 1, + { 0x00D1, 0x0138 }, // 56 , 1, + { 0x00D2, 0x0140 }, // 64 , 1, + { 0x00D3, 0x013a }, // 58 , 1, + { 0x00D4, 0x013c }, // 60 , 1, + { 0x00D5, 0x0152 }, // 82 , 1, + { 0x00D6, 0x013e }, // 62 , 1, + { 0x00D7, 0x0627 }, // 39 , 6, + { 0x00D8, 0x0150 }, // 80 , 1, + { 0x00D9, 0x0148 }, // 72 , 1, + { 0x00DA, 0x0142 }, // 66 , 1, + { 0x00DB, 0x0144 }, // 68 , 1, + { 0x00DC, 0x0146 }, // 70 , 1, + { 0x00DD, 0x0154 }, // 84 , 1, + { 0x00DE, 0x0158 }, // 88 , 1, + { 0x00DF, 0x0117 }, // 23 , 1, + { 0x00E0, 0x0121 }, // 33 , 1, + { 0x00E1, 0x011b }, // 27 , 1, + { 0x00E2, 0x011d }, // 29 , 1, + { 0x00E3, 0x014d }, // 77 , 1, + { 0x00E4, 0x011f }, // 31 , 1, + { 0x00E5, 0x0123 }, // 35 , 1, + { 0x00E6, 0x0125 }, // 37 , 1, + { 0x00E7, 0x0127 }, // 39 , 1, + { 0x00E8, 0x012f }, // 47 , 1, + { 0x00E9, 0x0129 }, // 41 , 1, + { 0x00EA, 0x012b }, // 43 , 1, + { 0x00EB, 0x012d }, // 45 , 1, + { 0x00EC, 0x0137 }, // 55 , 1, + { 0x00ED, 0x0131 }, // 49 , 1, + { 0x00EE, 0x0133 }, // 51 , 1, + { 0x00EF, 0x0135 }, // 53 , 1, + { 0x00F0, 0x0157 }, // 87 , 1, + { 0x00F1, 0x0139 }, // 57 , 1, + { 0x00F2, 0x0141 }, // 65 , 1, + { 0x00F3, 0x013b }, // 59 , 1, + { 0x00F4, 0x013d }, // 61 , 1, + { 0x00F5, 0x0153 }, // 83 , 1, + { 0x00F6, 0x013f }, // 63 , 1, + { 0x00F7, 0x0608 }, // 8 , 6, + { 0x00F8, 0x0151 }, // 81 , 1, + { 0x00F9, 0x0149 }, // 73 , 1, + { 0x00FA, 0x0143 }, // 67 , 1, + { 0x00FB, 0x0145 }, // 69 , 1, + { 0x00FC, 0x0147 }, // 71 , 1, + { 0x00FD, 0x0155 }, // 85 , 1, + { 0x00FE, 0x0159 }, // 89 , 1, + { 0x00FF, 0x014b }, // 75 , 1, + { 0x0100, 0x015c }, // 92 , 1, + { 0x0101, 0x015d }, // 93 , 1, + { 0x0102, 0x015a }, // 90 , 1, + { 0x0103, 0x015b }, // 91 , 1, + { 0x0104, 0x015e }, // 94 , 1, + { 0x0105, 0x015f }, // 95 , 1, + { 0x0106, 0x0160 }, // 96 , 1, + { 0x0107, 0x0161 }, // 97 , 1, + { 0x0108, 0x0164 }, // 100, 1, + { 0x0109, 0x0165 }, // 101, 1, + { 0x010A, 0x0166 }, // 102, 1, + { 0x010B, 0x0167 }, // 103, 1, + { 0x010C, 0x0162 }, // 98 , 1, + { 0x010D, 0x0163 }, // 99 , 1, + { 0x010E, 0x0168 }, // 104, 1, + { 0x010F, 0x0169 }, // 105, 1, + { 0x0110, 0x014e }, // 78 , 1, + { 0x0111, 0x014f }, // 79 , 1, + { 0x0112, 0x016e }, // 110, 1, + { 0x0113, 0x016f }, // 111, 1, + { 0x0114, 0x01ea }, // 234, 1, + { 0x0115, 0x01eb }, // 235, 1, + { 0x0116, 0x016c }, // 108, 1, + { 0x0117, 0x016d }, // 109, 1, + { 0x0118, 0x0170 }, // 112, 1, + { 0x0119, 0x0171 }, // 113, 1, + { 0x011A, 0x016a }, // 106, 1, + { 0x011B, 0x016b }, // 107, 1, + { 0x011C, 0x017a }, // 122, 1, + { 0x011D, 0x017b }, // 123, 1, + { 0x011E, 0x0174 }, // 116, 1, + { 0x011F, 0x0175 }, // 117, 1, + { 0x0120, 0x017c }, // 124, 1, + { 0x0121, 0x017d }, // 125, 1, + { 0x0122, 0x0178 }, // 120, 1, + { 0x0123, 0x0179 }, // 121, 1, + { 0x0124, 0x017e }, // 126, 1, + { 0x0125, 0x017f }, // 127, 1, + { 0x0126, 0x0180 }, // 128, 1, + { 0x0127, 0x0181 }, // 129, 1, + { 0x0128, 0x0188 }, // 136, 1, + { 0x0129, 0x0189 }, // 137, 1, + { 0x012A, 0x0184 }, // 132, 1, + { 0x012B, 0x0185 }, // 133, 1, + { 0x012C, 0x01ec }, // 236, 1, + { 0x012D, 0x01ed }, // 237, 1, + { 0x012E, 0x0186 }, // 134, 1, + { 0x012F, 0x0187 }, // 135, 1, + { 0x0130, 0x0182 }, // 130, 1, + { 0x0131, 0x01ef }, // 239, 1, + { 0x0132, 0x018a }, // 138, 1, + { 0x0133, 0x018b }, // 139, 1, + { 0x0134, 0x018c }, // 140, 1, + { 0x0135, 0x018d }, // 141, 1, + { 0x0136, 0x018e }, // 142, 1, + { 0x0137, 0x018f }, // 143, 1, + { 0x0138, 0x0118 }, // 24 , 1, + { 0x0139, 0x0190 }, // 144, 1, + { 0x013A, 0x0191 }, // 145, 1, + { 0x013B, 0x0194 }, // 148, 1, + { 0x013C, 0x0195 }, // 149, 1, + { 0x013D, 0x0192 }, // 146, 1, + { 0x013E, 0x0193 }, // 147, 1, + { 0x013F, 0x0196 }, // 150, 1, + { 0x0140, 0x0197 }, // 151, 1, + { 0x0141, 0x0198 }, // 152, 1, + { 0x0142, 0x0199 }, // 153, 1, + { 0x0143, 0x019a }, // 154, 1, + { 0x0144, 0x019b }, // 155, 1, + { 0x0145, 0x01a0 }, // 160, 1, + { 0x0146, 0x01a1 }, // 161, 1, + { 0x0147, 0x019e }, // 158, 1, + { 0x0148, 0x019f }, // 159, 1, + { 0x0149, 0x019d }, // 157, 1, + { 0x014A, 0x01d2 }, // 210, 1, + { 0x014B, 0x01d3 }, // 211, 1, + { 0x014C, 0x01a4 }, // 164, 1, + { 0x014D, 0x01a5 }, // 165, 1, + { 0x014E, 0x01f0 }, // 240, 1, + { 0x014F, 0x01f1 }, // 241, 1, + { 0x0150, 0x01a2 }, // 162, 1, + { 0x0151, 0x01a3 }, // 163, 1, + { 0x0152, 0x01a6 }, // 166, 1, + { 0x0153, 0x01a7 }, // 167, 1, + { 0x0154, 0x01a8 }, // 168, 1, + { 0x0155, 0x01a9 }, // 169, 1, + { 0x0156, 0x01ac }, // 172, 1, + { 0x0157, 0x01ad }, // 173, 1, + { 0x0158, 0x01aa }, // 170, 1, + { 0x0159, 0x01ab }, // 171, 1, + { 0x015A, 0x01ae }, // 174, 1, + { 0x015B, 0x01af }, // 175, 1, + { 0x015C, 0x01b4 }, // 180, 1, + { 0x015D, 0x01b5 }, // 181, 1, + { 0x015E, 0x01b2 }, // 178, 1, + { 0x015F, 0x01b3 }, // 179, 1, + { 0x0160, 0x01b0 }, // 176, 1, + { 0x0161, 0x01b1 }, // 177, 1, + { 0x0162, 0x01b8 }, // 184, 1, + { 0x0163, 0x01b9 }, // 185, 1, + { 0x0164, 0x01b6 }, // 182, 1, + { 0x0165, 0x01b7 }, // 183, 1, + { 0x0166, 0x01ba }, // 186, 1, + { 0x0167, 0x01bb }, // 187, 1, + { 0x0168, 0x01c6 }, // 198, 1, + { 0x0169, 0x01c7 }, // 199, 1, + { 0x016A, 0x01c0 }, // 192, 1, + { 0x016B, 0x01c1 }, // 193, 1, + { 0x016C, 0x01bc }, // 188, 1, + { 0x016D, 0x01bd }, // 189, 1, + { 0x016E, 0x01c4 }, // 196, 1, + { 0x016F, 0x01c5 }, // 197, 1, + { 0x0170, 0x01be }, // 190, 1, + { 0x0171, 0x01bf }, // 191, 1, + { 0x0172, 0x01c2 }, // 194, 1, + { 0x0173, 0x01c3 }, // 195, 1, + { 0x0174, 0x01c8 }, // 200, 1, + { 0x0175, 0x01c9 }, // 201, 1, + { 0x0176, 0x01ca }, // 202, 1, + { 0x0177, 0x01cb }, // 203, 1, + { 0x0178, 0x014a }, // 74 , 1, + { 0x0179, 0x01cc }, // 204, 1, + { 0x017A, 0x01cd }, // 205, 1, + { 0x017B, 0x01d0 }, // 208, 1, + { 0x017C, 0x01d1 }, // 209, 1, + { 0x017D, 0x01ce }, // 206, 1, + { 0x017E, 0x01cf }, // 207, 1, + { 0x0192, 0x040e }, // 14 , 4, + { 0x0194, 0x0a7c }, // 124, 10, + { 0x01A0, 0x01e6 }, // 230, 1, + { 0x01A1, 0x01e7 }, // 231, 1, + { 0x01AF, 0x01e8 }, // 232, 1, + { 0x01B0, 0x01e9 }, // 233, 1, + { 0x01C0, 0x0605 }, // 5 , 6, + { 0x0250, 0x0237 }, // 55 , 2, + { 0x0251, 0x0238 }, // 56 , 2, + { 0x0252, 0x0239 }, // 57 , 2, + { 0x0253, 0x023a }, // 58 , 2, + { 0x0254, 0x023c }, // 60 , 2, + { 0x0255, 0x023d }, // 61 , 2, + { 0x0256, 0x023f }, // 63 , 2, + { 0x0257, 0x0240 }, // 64 , 2, + { 0x0258, 0x0241 }, // 65 , 2, + { 0x0259, 0x0242 }, // 66 , 2, + { 0x025A, 0x0243 }, // 67 , 2, + { 0x025B, 0x0244 }, // 68 , 2, + { 0x025C, 0x0245 }, // 69 , 2, + { 0x025D, 0x0246 }, // 70 , 2, + { 0x025E, 0x0248 }, // 72 , 2, + { 0x025F, 0x0249 }, // 73 , 2, + { 0x0260, 0x024c }, // 76 , 2, + { 0x0261, 0x024b }, // 75 , 2, + { 0x0262, 0x024d }, // 77 , 2, + { 0x0263, 0x024f }, // 79 , 2, + { 0x0264, 0x0250 }, // 80 , 2, + { 0x0265, 0x0251 }, // 81 , 2, + { 0x0266, 0x0252 }, // 82 , 2, + { 0x0267, 0x0253 }, // 83 , 2, + { 0x0268, 0x0255 }, // 85 , 2, + { 0x0269, 0x0257 }, // 87 , 2, + { 0x026A, 0x0256 }, // 86 , 2, + { 0x026B, 0x025a }, // 90 , 2, + { 0x026C, 0x025b }, // 91 , 2, + { 0x026D, 0x025c }, // 92 , 2, + { 0x026E, 0x025e }, // 94 , 2, + { 0x026F, 0x0260 }, // 96 , 2, + { 0x0270, 0x0261 }, // 97 , 2, + { 0x0271, 0x0262 }, // 98 , 2, + { 0x0272, 0x0263 }, // 99 , 2, + { 0x0273, 0x0264 }, // 100, 2, + { 0x0274, 0x0265 }, // 101, 2, + { 0x0275, 0x0279 }, // 121, 2, + { 0x0276, 0x0266 }, // 102, 2, + { 0x0277, 0x0267 }, // 103, 2, + { 0x0278, 0x024a }, // 74 , 2, + { 0x0279, 0x0269 }, // 105, 2, + { 0x027A, 0x026a }, // 106, 2, + { 0x027B, 0x026b }, // 107, 2, + { 0x027C, 0x026c }, // 108, 2, + { 0x027D, 0x026d }, // 109, 2, + { 0x027E, 0x026e }, // 110, 2, + { 0x027F, 0x026f }, // 111, 2, + { 0x0280, 0x0270 }, // 112, 2, + { 0x0281, 0x0271 }, // 113, 2, + { 0x0282, 0x0272 }, // 114, 2, + { 0x0283, 0x0273 }, // 115, 2, + { 0x0284, 0x0274 }, // 116, 2, + { 0x0285, 0x0275 }, // 117, 2, + { 0x0286, 0x0276 }, // 118, 2, + { 0x0287, 0x0277 }, // 119, 2, + { 0x0288, 0x0278 }, // 120, 2, + { 0x0289, 0x027a }, // 122, 2, + { 0x028A, 0x027b }, // 123, 2, + { 0x028B, 0x027d }, // 125, 2, + { 0x028C, 0x027c }, // 124, 2, + { 0x028D, 0x027e }, // 126, 2, + { 0x028E, 0x025f }, // 95 , 2, + { 0x028F, 0x0280 }, // 128, 2, + { 0x0290, 0x0281 }, // 129, 2, + { 0x0291, 0x0282 }, // 130, 2, + { 0x0292, 0x0283 }, // 131, 2, + { 0x0293, 0x0284 }, // 132, 2, + { 0x0294, 0x0285 }, // 133, 2, + { 0x0295, 0x0286 }, // 134, 2, + { 0x0296, 0x0287 }, // 135, 2, + { 0x0297, 0x023e }, // 62 , 2, + { 0x0298, 0x028a }, // 138, 2, + { 0x0299, 0x023b }, // 59 , 2, + { 0x029A, 0x0247 }, // 71 , 2, + { 0x029B, 0x024e }, // 78 , 2, + { 0x029C, 0x0254 }, // 84 , 2, + { 0x029D, 0x0258 }, // 88 , 2, + { 0x029E, 0x0259 }, // 89 , 2, + { 0x029F, 0x025d }, // 93 , 2, + { 0x02A0, 0x0268 }, // 104, 2, + { 0x02A1, 0x0288 }, // 136, 2, + { 0x02A2, 0x0289 }, // 137, 2, + { 0x02A3, 0x028b }, // 139, 2, + { 0x02A4, 0x028c }, // 140, 2, + { 0x02A5, 0x028d }, // 141, 2, + { 0x02A6, 0x028e }, // 142, 2, + { 0x02A7, 0x028f }, // 143, 2, + { 0x02A8, 0x0290 }, // 144, 2, + { 0x02B0, 0x0235 }, // 53 , 2, + { 0x02B6, 0x0236 }, // 54 , 2, + { 0x02B9, 0x0200 }, // 0 , 2, + { 0x02BA, 0x0201 }, // 1 , 2, + { 0x02BB, 0x0202 }, // 2 , 2, + { 0x02BC, 0x0205 }, // 5 , 2, + { 0x02BD, 0x0204 }, // 4 , 2, + { 0x02BE, 0x0207 }, // 7 , 2, + { 0x02BF, 0x0208 }, // 8 , 2, + { 0x02C6, 0x0217 }, // 23 , 2, + { 0x02C7, 0x0218 }, // 24 , 2, + { 0x02C8, 0x020f }, // 15 , 2, + { 0x02C9, 0x0211 }, // 17 , 2, + { 0x02CA, 0x0212 }, // 18 , 2, + { 0x02CB, 0x0213 }, // 19 , 2, + { 0x02CC, 0x0210 }, // 16 , 2, + { 0x02CD, 0x0214 }, // 20 , 2, + { 0x02CE, 0x0215 }, // 21 , 2, + { 0x02CF, 0x0216 }, // 22 , 2, + { 0x02D0, 0x020a }, // 10 , 2, + { 0x02D1, 0x020b }, // 11 , 2, + { 0x02D2, 0x022a }, // 42 , 2, + { 0x02D3, 0x022b }, // 43 , 2, + { 0x02DA, 0x021b }, // 27 , 2, + { 0x02DB, 0x0231 }, // 49 , 2, + { 0x02DC, 0x0219 }, // 25 , 2, + { 0x02DE, 0x0233 }, // 51 , 2, + { 0x0300, 0x0100 }, // 0 , 1, + { 0x0301, 0x0106 }, // 6 , 1, + { 0x0302, 0x0103 }, // 3 , 1, + { 0x0303, 0x0102 }, // 2 , 1, + { 0x0304, 0x0108 }, // 8 , 1, + { 0x0305, 0x0115 }, // 21 , 1, + { 0x0306, 0x0116 }, // 22 , 1, + { 0x0307, 0x010f }, // 15 , 1, + { 0x0308, 0x0107 }, // 7 , 1, + { 0x030A, 0x010e }, // 14 , 1, + { 0x030B, 0x0110 }, // 16 , 1, + { 0x030C, 0x0113 }, // 19 , 1, + { 0x0310, 0x0209 }, // 9 , 2, + { 0x0311, 0x0858 }, // 88 , 8, + { 0x0313, 0x0109 }, // 9 , 1, + { 0x0314, 0x085a }, // 90 , 8, + { 0x0315, 0x010a }, // 10 , 1, + { 0x031C, 0x0221 }, // 33 , 2, + { 0x031D, 0x0222 }, // 34 , 2, + { 0x031E, 0x0223 }, // 35 , 2, + { 0x031F, 0x0224 }, // 36 , 2, + { 0x0320, 0x0225 }, // 37 , 2, + { 0x0321, 0x0226 }, // 38 , 2, + { 0x0322, 0x0227 }, // 39 , 2, + { 0x0323, 0x021e }, // 30 , 2, + { 0x0324, 0x0220 }, // 32 , 2, + { 0x0325, 0x021a }, // 26 , 2, + { 0x0326, 0x010c }, // 12 , 1, + { 0x0327, 0x0111 }, // 17 , 1, + { 0x0328, 0x0112 }, // 18 , 1, + { 0x0329, 0x020e }, // 14 , 2, + { 0x032A, 0x0228 }, // 40 , 2, + { 0x032B, 0x0229 }, // 41 , 2, + { 0x032C, 0x021d }, // 29 , 2, + { 0x032D, 0x021c }, // 28 , 2, + { 0x032E, 0x020d }, // 13 , 2, + { 0x0335, 0x0104 }, // 4 , 1, + { 0x0337, 0x0114 }, // 20 , 1, + { 0x0338, 0x0105 }, // 5 , 1, + { 0x033E, 0x0230 }, // 48 , 2, + { 0x0345, 0x085b }, // 91 , 8, + { 0x0374, 0x0851 }, // 81 , 8, + { 0x0375, 0x0852 }, // 82 , 8, + { 0x0391, 0x0800 }, // 0 , 8, + { 0x0392, 0x0802 }, // 2 , 8, + { 0x0393, 0x0806 }, // 6 , 8, + { 0x0394, 0x0808 }, // 8 , 8, + { 0x0395, 0x080a }, // 10 , 8, + { 0x0396, 0x080c }, // 12 , 8, + { 0x0397, 0x080e }, // 14 , 8, + { 0x0398, 0x0810 }, // 16 , 8, + { 0x0399, 0x0812 }, // 18 , 8, + { 0x039A, 0x0814 }, // 20 , 8, + { 0x039B, 0x0816 }, // 22 , 8, + { 0x039C, 0x0818 }, // 24 , 8, + { 0x039D, 0x081a }, // 26 , 8, + { 0x039E, 0x081c }, // 28 , 8, + { 0x039F, 0x081e }, // 30 , 8, + { 0x03A0, 0x0820 }, // 32 , 8, + { 0x03A1, 0x0822 }, // 34 , 8, + { 0x03A3, 0x0824 }, // 36 , 8, + { 0x03A4, 0x0828 }, // 40 , 8, + { 0x03A5, 0x082a }, // 42 , 8, + { 0x03A6, 0x082c }, // 44 , 8, + { 0x03A7, 0x082e }, // 46 , 8, + { 0x03A8, 0x0830 }, // 48 , 8, + { 0x03A9, 0x0832 }, // 50 , 8, + { 0x03AA, 0x083c }, // 60 , 8, + { 0x03AB, 0x0842 }, // 66 , 8, + { 0x03AC, 0x0835 }, // 53 , 8, + { 0x03AD, 0x0837 }, // 55 , 8, + { 0x03AE, 0x0839 }, // 57 , 8, + { 0x03AF, 0x083b }, // 59 , 8, + { 0x03B1, 0x0801 }, // 1 , 8, + { 0x03B2, 0x0803 }, // 3 , 8, + { 0x03B3, 0x0807 }, // 7 , 8, + { 0x03B4, 0x0809 }, // 9 , 8, + { 0x03B5, 0x080b }, // 11 , 8, + { 0x03B6, 0x080d }, // 13 , 8, + { 0x03B7, 0x080f }, // 15 , 8, + { 0x03B8, 0x0811 }, // 17 , 8, + { 0x03B9, 0x0813 }, // 19 , 8, + { 0x03BA, 0x0815 }, // 21 , 8, + { 0x03BB, 0x0817 }, // 23 , 8, + { 0x03BC, 0x0819 }, // 25 , 8, + { 0x03BD, 0x081b }, // 27 , 8, + { 0x03BE, 0x081d }, // 29 , 8, + { 0x03BF, 0x081f }, // 31 , 8, + { 0x03C0, 0x0821 }, // 33 , 8, + { 0x03C1, 0x0823 }, // 35 , 8, + { 0x03C2, 0x0827 }, // 39 , 8, + { 0x03C3, 0x0825 }, // 37 , 8, + { 0x03C4, 0x0829 }, // 41 , 8, + { 0x03C5, 0x082b }, // 43 , 8, + { 0x03C6, 0x082d }, // 45 , 8, + { 0x03C7, 0x082f }, // 47 , 8, + { 0x03C8, 0x0831 }, // 49 , 8, + { 0x03C9, 0x0833 }, // 51 , 8, + { 0x03CA, 0x083d }, // 61 , 8, + { 0x03CB, 0x0843 }, // 67 , 8, + { 0x03CC, 0x083f }, // 63 , 8, + { 0x03CD, 0x0841 }, // 65 , 8, + { 0x03CE, 0x0845 }, // 69 , 8, + { 0x03D0, 0x0805 }, // 5 , 8, + { 0x03D1, 0x0847 }, // 71 , 8, + { 0x03D2, 0x084c }, // 76 , 8, + { 0x03D5, 0x084d }, // 77 , 8, + { 0x03D6, 0x0849 }, // 73 , 8, + { 0x03D7, 0x084f }, // 79 , 8, + { 0x03DA, 0x08d7 }, // 215, 8, + { 0x03DB, 0x084B }, // 75 , 8, + { 0x03DC, 0x08d8 }, // 216, 8, + { 0x03DE, 0x08d9 }, // 217, 8, + { 0x03E0, 0x08da }, // 218, 8, + { 0x03F0, 0x0848 }, // 72 , 8, + { 0x03F1, 0x084a }, // 74 , 8, + { 0x0401, 0x0a0c }, // 12 , 10, + { 0x0402, 0x0a4a }, // 74 , 10, + { 0x0403, 0x0a44 }, // 68 , 10, + { 0x0404, 0x0a4e }, // 78 , 10, + { 0x0405, 0x0a52 }, // 82 , 10, + { 0x0406, 0x0a58 }, // 88 , 10, + { 0x0407, 0x0a5a }, // 90 , 10, + { 0x0408, 0x0a5e }, // 94 , 10, + { 0x0409, 0x0a68 }, // 104, 10, + { 0x040A, 0x0a6c }, // 108, 10, + { 0x040B, 0x0a72 }, // 114, 10, + { 0x040C, 0x0a60 }, // 96 , 10, + { 0x040E, 0x0a74 }, // 116, 10, + { 0x040F, 0x0a86 }, // 134, 10, + { 0x0410, 0x0a00 }, // 0 , 10, + { 0x0411, 0x0a02 }, // 2 , 10, + { 0x0412, 0x0a04 }, // 4 , 10, + { 0x0413, 0x0a06 }, // 6 , 10, + { 0x0414, 0x0a08 }, // 8 , 10, + { 0x0415, 0x0a0a }, // 10 , 10, + { 0x0416, 0x0a0e }, // 14 , 10, + { 0x0417, 0x0a10 }, // 16 , 10, + { 0x0418, 0x0a12 }, // 18 , 10, + { 0x0419, 0x0a14 }, // 20 , 10, + { 0x041A, 0x0a16 }, // 22 , 10, + { 0x041B, 0x0a18 }, // 24 , 10, + { 0x041C, 0x0a1a }, // 26 , 10, + { 0x041D, 0x0a1c }, // 28 , 10, + { 0x041E, 0x0a1e }, // 30 , 10, + { 0x041F, 0x0a20 }, // 32 , 10, + { 0x0420, 0x0a22 }, // 34 , 10, + { 0x0421, 0x0a24 }, // 36 , 10, + { 0x0422, 0x0a26 }, // 38 , 10, + { 0x0423, 0x0a28 }, // 40 , 10, + { 0x0424, 0x0a2a }, // 42 , 10, + { 0x0425, 0x0a2c }, // 44 , 10, + { 0x0426, 0x0a2e }, // 46 , 10, + { 0x0427, 0x0a30 }, // 48 , 10, + { 0x0428, 0x0a32 }, // 50 , 10, + { 0x0429, 0x0a34 }, // 52 , 10, + { 0x042A, 0x0a36 }, // 54 , 10, + { 0x042B, 0x0a38 }, // 56 , 10, + { 0x042C, 0x0a3a }, // 58 , 10, + { 0x042D, 0x0a3c }, // 60 , 10, + { 0x042E, 0x0a3e }, // 62 , 10, + { 0x042F, 0x0a40 }, // 64 , 10, + { 0x0430, 0x0a01 }, // 1 , 10, + { 0x0431, 0x0a03 }, // 3 , 10, + { 0x0432, 0x0a05 }, // 5 , 10, + { 0x0433, 0x0a07 }, // 7 , 10, + { 0x0434, 0x0a09 }, // 9 , 10, + { 0x0435, 0x0a0b }, // 11 , 10, + { 0x0436, 0x0a0f }, // 15 , 10, + { 0x0437, 0x0a11 }, // 17 , 10, + { 0x0438, 0x0a13 }, // 19 , 10, + { 0x0439, 0x0a15 }, // 21 , 10, + { 0x043A, 0x0a17 }, // 23 , 10, + { 0x043B, 0x0a19 }, // 25 , 10, + { 0x043C, 0x0a1b }, // 27 , 10, + { 0x043D, 0x0a1d }, // 29 , 10, + { 0x043E, 0x0a1f }, // 31 , 10, + { 0x043F, 0x0a21 }, // 33 , 10, + { 0x0440, 0x0a23 }, // 35 , 10, + { 0x0441, 0x0a25 }, // 37 , 10, + { 0x0442, 0x0a27 }, // 39 , 10, + { 0x0443, 0x0a29 }, // 41 , 10, + { 0x0444, 0x0a2b }, // 43 , 10, + { 0x0445, 0x0a2d }, // 45 , 10, + { 0x0446, 0x0a2f }, // 47 , 10, + { 0x0447, 0x0a31 }, // 49 , 10, + { 0x0448, 0x0a33 }, // 51 , 10, + { 0x0449, 0x0a35 }, // 53 , 10, + { 0x044A, 0x0a37 }, // 55 , 10, + { 0x044B, 0x0a39 }, // 57 , 10, + { 0x044C, 0x0a3b }, // 59 , 10, + { 0x044D, 0x0a3d }, // 61 , 10, + { 0x044E, 0x0a3f }, // 63 , 10, + { 0x044F, 0x0a41 }, // 65 , 10, + { 0x0451, 0x0a0d }, // 13 , 10, + { 0x0452, 0x0a4b }, // 75 , 10, + { 0x0453, 0x0a45 }, // 69 , 10, + { 0x0454, 0x0a4f }, // 79 , 10, + { 0x0455, 0x0a53 }, // 83 , 10, + { 0x0456, 0x0a59 }, // 89 , 10, + { 0x0457, 0x0a5b }, // 91 , 10, + { 0x0458, 0x0a5f }, // 95 , 10, + { 0x0459, 0x0a69 }, // 105, 10, + { 0x045A, 0x0a6d }, // 109, 10, + { 0x045B, 0x0a73 }, // 115, 10, + { 0x045C, 0x0a61 }, // 97 , 10, + { 0x045E, 0x0a75 }, // 117, 10, + { 0x045F, 0x0a87 }, // 135, 10, + { 0x0460, 0x0a70 }, // 112, 10, + { 0x0461, 0x0a71 }, // 113, 10, + { 0x0462, 0x0a8e }, // 142, 10, + { 0x0463, 0x0a8f }, // 143, 10, + { 0x0466, 0x0a90 }, // 144, 10, + { 0x0467, 0x0a91 }, // 145, 10, + { 0x046A, 0x0a92 }, // 146, 10, + { 0x046B, 0x0a93 }, // 147, 10, + { 0x046E, 0x0a94 }, // 148, 10, + { 0x046F, 0x0a95 }, // 149, 10, + { 0x0470, 0x0a96 }, // 150, 10, + { 0x0471, 0x0a97 }, // 151, 10, + { 0x0472, 0x0a98 }, // 152, 10, + { 0x0473, 0x0a99 }, // 153, 10, + { 0x0474, 0x0a9a }, // 154, 10, + { 0x0475, 0x0a9b }, // 155, 10, + { 0x047A, 0x0a6e }, // 110, 10, + { 0x047B, 0x0a6f }, // 111, 10, + { 0x047E, 0x0a84 }, // 132, 10, + { 0x047F, 0x0a85 }, // 133, 10, + { 0x0490, 0x0a46 }, // 70 , 10, + { 0x0491, 0x0a47 }, // 71 , 10, + { 0x0492, 0x0a48 }, // 72 , 10, + { 0x0493, 0x0a49 }, // 73 , 10, + { 0x0496, 0x0a50 }, // 80 , 10, + { 0x0497, 0x0a51 }, // 81 , 10, + { 0x049A, 0x0a62 }, // 98 , 10, + { 0x049B, 0x0a63 }, // 99 , 10, + { 0x049C, 0x0a66 }, // 102, 10, + { 0x049D, 0x0a67 }, // 103, 10, + { 0x04A2, 0x0a6a }, // 106, 10, + { 0x04A3, 0x0a6b }, // 107, 10, + { 0x04AE, 0x0a78 }, // 120, 10, + { 0x04AF, 0x0a79 }, // 121, 10, + { 0x04B0, 0x0a7a }, // 122, 10, + { 0x04B1, 0x0a7b }, // 123, 10, + { 0x04B2, 0x0a7e }, // 126, 10, + { 0x04B3, 0x0a7f }, // 127, 10, + { 0x04B6, 0x0a88 }, // 136, 10, + { 0x04B7, 0x0a89 }, // 137, 10, + { 0x04B8, 0x0a8a }, // 138, 10, + { 0x04B9, 0x0a8b }, // 139, 10, + { 0x04BA, 0x0a82 }, // 130, 10, + { 0x04BB, 0x0a83 }, // 131, 10, + { 0x04D8, 0x0a42 }, // 66 , 10, + { 0x04D9, 0x0a43 }, // 67 , 10, + { 0x04EE, 0x0a76 }, // 118, 10, + { 0x04EF, 0x0a77 }, // 119, 10, + { 0x05B0, 0x0920 }, // 32 , 9, + { 0x05B1, 0x0921 }, // 33 , 9, + { 0x05B2, 0x0922 }, // 34 , 9, + { 0x05B3, 0x0923 }, // 35 , 9, + { 0x05B4, 0x0924 }, // 36 , 9, + { 0x05B5, 0x0925 }, // 37 , 9, + { 0x05B6, 0x0926 }, // 38 , 9, + { 0x05B7, 0x0927 }, // 39 , 9, + { 0x05B8, 0x0928 }, // 40 , 9, + { 0x05B9, 0x0929 }, // 41 , 9, + { 0x05BB, 0x092b }, // 43 , 9, + { 0x05BC, 0x092c }, // 44 , 9, + { 0x05BD, 0x092d }, // 45 , 9, + { 0x05BF, 0x092e }, // 46 , 9, + { 0x05C0, 0x091c }, // 28 , 9, + { 0x05C3, 0x091d }, // 29 , 9, + { 0x05D0, 0x0900 }, // 0 , 9, + { 0x05D1, 0x0901 }, // 1 , 9, + { 0x05D2, 0x0902 }, // 2 , 9, + { 0x05D3, 0x0903 }, // 3 , 9, + { 0x05D4, 0x0904 }, // 4 , 9, + { 0x05D5, 0x0905 }, // 5 , 9, + { 0x05D6, 0x0906 }, // 6 , 9, + { 0x05D7, 0x0907 }, // 7 , 9, + { 0x05D8, 0x0908 }, // 8 , 9, + { 0x05D9, 0x0909 }, // 9 , 9, + { 0x05DA, 0x090a }, // 10 , 9, + { 0x05DB, 0x090b }, // 11 , 9, + { 0x05DC, 0x090c }, // 12 , 9, + { 0x05DD, 0x090d }, // 13 , 9, + { 0x05DE, 0x090e }, // 14 , 9, + { 0x05DF, 0x090f }, // 15 , 9, + { 0x05E0, 0x0910 }, // 16 , 9, + { 0x05E1, 0x0911 }, // 17 , 9, + { 0x05E2, 0x0912 }, // 18 , 9, + { 0x05E3, 0x0913 }, // 19 , 9, + { 0x05E4, 0x0914 }, // 20 , 9, + { 0x05E5, 0x0915 }, // 21 , 9, + { 0x05E6, 0x0916 }, // 22 , 9, + { 0x05E7, 0x0917 }, // 23 , 9, + { 0x05E8, 0x0918 }, // 24 , 9, + { 0x05E9, 0x0919 }, // 25 , 9, + { 0x05EA, 0x091a }, // 26 , 9, + { 0x05F0, 0x0931 }, // 49 , 9, + { 0x05F1, 0x0932 }, // 50 , 9, + { 0x05F2, 0x0933 }, // 51 , 9, + { 0x05F3, 0x091e }, // 30 , 9, + { 0x05F4, 0x091f }, // 31 , 9, + { 0x060C, 0x0d26 }, // 38 , 13, + { 0x061B, 0x0d27 }, // 39 , 13, + { 0x061F, 0x0d28 }, // 40 , 13, + { 0x0621, 0x0da4 }, // 164, 13, + { 0x0622, 0x0db1 }, // 177, 13, + { 0x0623, 0x0da5 }, // 165, 13, + { 0x0624, 0x0da9 }, // 169, 13, + { 0x0625, 0x0da7 }, // 167, 13, + { 0x0626, 0x0dab }, // 171, 13, + { 0x0627, 0x0d3a }, // 58 , 13, + { 0x0628, 0x0d3c }, // 60 , 13, + { 0x0629, 0x0d98 }, // 152, 13, + { 0x062A, 0x0d40 }, // 64 , 13, + { 0x062B, 0x0d44 }, // 68 , 13, + { 0x062C, 0x0d48 }, // 72 , 13, + { 0x062D, 0x0d4c }, // 76 , 13, + { 0x062E, 0x0d50 }, // 80 , 13, + { 0x062F, 0x0d54 }, // 84 , 13, + { 0x0630, 0x0d56 }, // 86 , 13, + { 0x0631, 0x0d58 }, // 88 , 13, + { 0x0632, 0x0d5a }, // 90 , 13, + { 0x0633, 0x0d5c }, // 92 , 13, + { 0x0634, 0x0d60 }, // 96 , 13, + { 0x0635, 0x0d64 }, // 100, 13, + { 0x0636, 0x0d68 }, // 104, 13, + { 0x0637, 0x0d6c }, // 108, 13, + { 0x0638, 0x0d70 }, // 112, 13, + { 0x0639, 0x0d74 }, // 116, 13, + { 0x063A, 0x0d78 }, // 120, 13, + { 0x0640, 0x0dc2 }, // 194, 13, + { 0x0641, 0x0d7c }, // 124, 13, + { 0x0642, 0x0d80 }, // 128, 13, + { 0x0643, 0x0d84 }, // 132, 13, + { 0x0644, 0x0d88 }, // 136, 13, + { 0x0645, 0x0d8c }, // 140, 13, + { 0x0646, 0x0d90 }, // 144, 13, + { 0x0647, 0x0d94 }, // 148, 13, + { 0x0648, 0x0d9a }, // 154, 13, + { 0x0649, 0x0da0 }, // 160, 13, + { 0x064A, 0x0d9c }, // 156, 13, + { 0x064B, 0x0d10 }, // 16 , 13, + { 0x064C, 0x0d11 }, // 17 , 13, + { 0x064E, 0x0d0a }, // 10 , 13, + { 0x064F, 0x0d0c }, // 12 , 13, + { 0x0650, 0x0d0e }, // 14 , 13, + { 0x0651, 0x0d16 }, // 22 , 13, + { 0x0652, 0x0d14 }, // 20 , 13, + { 0x0660, 0x0d38 }, // 56 , 13, + { 0x0661, 0x0d2f }, // 47 , 13, + { 0x0662, 0x0d30 }, // 48 , 13, + { 0x0663, 0x0d31 }, // 49 , 13, + { 0x0664, 0x0d32 }, // 50 , 13, + { 0x0665, 0x0d33 }, // 51 , 13, + { 0x0666, 0x0d34 }, // 52 , 13, + { 0x0667, 0x0d35 }, // 53 , 13, + { 0x0668, 0x0d36 }, // 54 , 13, + { 0x0669, 0x0d37 }, // 55 , 13, + { 0x066A, 0x0d2a }, // 42 , 13, + { 0x0671, 0x0db3 }, // 179, 13, + { 0x0674, 0x0d24 }, // 36 , 13, + { 0x0679, 0x0e3c }, // 60 , 14, + { 0x067A, 0x0e4c }, // 76 , 14, + { 0x067B, 0x0e30 }, // 48 , 14, + { 0x067C, 0x0e40 }, // 64 , 14, + { 0x067D, 0x0e48 }, // 72 , 14, + { 0x067E, 0x0e38 }, // 56 , 14, + { 0x067F, 0x0e44 }, // 68 , 14, + { 0x0680, 0x0e34 }, // 52 , 14, + { 0x0681, 0x0e64 }, // 100, 14, + { 0x0683, 0x0e54 }, // 84 , 14, + { 0x0684, 0x0e50 }, // 80 , 14, + { 0x0685, 0x0e60 }, // 96 , 14, + { 0x0686, 0x0e58 }, // 88 , 14, + { 0x0687, 0x0e5c }, // 92 , 14, + { 0x0688, 0x0e68 }, // 104, 14, + { 0x0689, 0x0e6a }, // 106, 14, + { 0x068A, 0x0e70 }, // 112, 14, + { 0x068C, 0x0e6c }, // 108, 14, + { 0x068D, 0x0e72 }, // 114, 14, + { 0x068E, 0x0e6e }, // 110, 14, + { 0x0691, 0x0e76 }, // 118, 14, + { 0x0692, 0x0e7C }, // 124, 14, + { 0x0693, 0x0e74 }, // 116, 14, + { 0x0695, 0x0e7a }, // 122, 14, + { 0x0696, 0x0e80 }, // 128, 14, + { 0x0698, 0x0e7e }, // 126, 14, + { 0x0699, 0x0e78 }, // 120, 14, + { 0x069A, 0x0e84 }, // 132, 14, + { 0x06A0, 0x0e88 }, // 136, 14, + { 0x06A4, 0x0e8c }, // 140, 14, + { 0x06A6, 0x0e90 }, // 144, 14, + { 0x06A9, 0x0e94 }, // 148, 14, + { 0x06AA, 0x0e9c }, // 156, 14, + { 0x06AB, 0x0ea8 }, // 168, 14, + { 0x06AF, 0x0ea0 }, // 160, 14, + { 0x06B1, 0x0eac }, // 172, 14, + { 0x06B3, 0x0eb0 }, // 176, 14, + { 0x06B5, 0x0eb4 }, // 180, 14, + { 0x06BA, 0x0eba }, // 186, 14, + { 0x06BB, 0x0ec2 }, // 194, 14, + { 0x06BC, 0x0ebe }, // 190, 14, + { 0x06C0, 0x0eda }, // 218, 14, + { 0x06C6, 0x0ec6 }, // 198, 14, + { 0x06CA, 0x0ec8 }, // 200, 14, + { 0x06CE, 0x0ed0 }, // 208, 14, + { 0x06D1, 0x0ed6 }, // 214, 14, + { 0x06D2, 0x0ed4 }, // 212, 14, + { 0x06D6, 0x0d25 }, // 37 , 13, + { 0x06E4, 0x0d22 }, // 34 , 13, + { 0x06F4, 0x0e29 }, // 41 , 14, + { 0x06F5, 0x0e2b }, // 43 , 14, + { 0x06F6, 0x0e2c }, // 44 , 14, + { 0x06F7, 0x0e2e }, // 46 , 14, + { 0x06F8, 0x0e2f }, // 47 , 14, + { 0x10D0, 0x0ad2 }, // 210, 10, + { 0x10D1, 0x0ad3 }, // 211, 10, + { 0x10D2, 0x0ad4 }, // 212, 10, + { 0x10D3, 0x0ad5 }, // 213, 10, + { 0x10D4, 0x0ad6 }, // 214, 10, + { 0x10D5, 0x0ad7 }, // 215, 10, + { 0x10D6, 0x0ad8 }, // 216, 10, + { 0x10D7, 0x0ada }, // 218, 10, + { 0x10D8, 0x0adb }, // 219, 10, + { 0x10D9, 0x0adc }, // 220, 10, + { 0x10DA, 0x0add }, // 221, 10, + { 0x10DB, 0x0ade }, // 222, 10, + { 0x10DC, 0x0adf }, // 223, 10, + { 0x10DD, 0x0ae1 }, // 225, 10, + { 0x10DE, 0x0ae2 }, // 226, 10, + { 0x10DF, 0x0ae3 }, // 227, 10, + { 0x10E0, 0x0ae4 }, // 228, 10, + { 0x10E1, 0x0ae5 }, // 229, 10, + { 0x10E2, 0x0ae6 }, // 230, 10, + { 0x10E3, 0x0ae7 }, // 231, 10, + { 0x10E4, 0x0ae9 }, // 233, 10, + { 0x10E5, 0x0aea }, // 234, 10, + { 0x10E6, 0x0aeb }, // 235, 10, + { 0x10E7, 0x0aec }, // 236, 10, + { 0x10E8, 0x0aed }, // 237, 10, + { 0x10E9, 0x0aee }, // 238, 10, + { 0x10EA, 0x0aef }, // 239, 10, + { 0x10EB, 0x0af0 }, // 240, 10, + { 0x10EC, 0x0af1 }, // 241, 10, + { 0x10ED, 0x0af2 }, // 242, 10, + { 0x10EE, 0x0af3 }, // 243, 10, + { 0x10EF, 0x0af5 }, // 245, 10, + { 0x10F0, 0x0af6 }, // 246, 10, + { 0x10F1, 0x0ad9 }, // 217, 10, + { 0x10F2, 0x0ae0 }, // 224, 10, + { 0x10F3, 0x0ae8 }, // 232, 10, + { 0x10F4, 0x0af4 }, // 244, 10, + { 0x10F5, 0x0af7 }, // 247, 10, + { 0x10F6, 0x0af8 }, // 248, 10, + { 0x1F00, 0x0873 }, // 115, 8, + { 0x1F01, 0x087b }, // 123, 8, + { 0x1F02, 0x0875 }, // 117, 8, + { 0x1F03, 0x087d }, // 125, 8, + { 0x1F04, 0x0874 }, // 116, 8, + { 0x1F05, 0x087c }, // 124, 8, + { 0x1F10, 0x0884 }, // 132, 8, + { 0x1F11, 0x0887 }, // 135, 8, + { 0x1F12, 0x0886 }, // 134, 8, + { 0x1F13, 0x0889 }, // 137, 8, + { 0x1F14, 0x0885 }, // 133, 8, + { 0x1F15, 0x0888 }, // 136, 8, + { 0x1F20, 0x0890 }, // 144, 8, + { 0x1F21, 0x0898 }, // 152, 8, + { 0x1F22, 0x0892 }, // 146, 8, + { 0x1F23, 0x089a }, // 154, 8, + { 0x1F24, 0x0891 }, // 145, 8, + { 0x1F25, 0x0899 }, // 153, 8, + { 0x1F30, 0x08a4 }, // 164, 8, + { 0x1F31, 0x08a8 }, // 168, 8, + { 0x1F32, 0x08a6 }, // 166, 8, + { 0x1F33, 0x08aa }, // 170, 8, + { 0x1F34, 0x08a5 }, // 165, 8, + { 0x1F35, 0x08a9 }, // 169, 8, + { 0x1F40, 0x08ad }, // 173, 8, + { 0x1F41, 0x08b0 }, // 176, 8, + { 0x1F42, 0x08af }, // 175, 8, + { 0x1F43, 0x08b2 }, // 178, 8, + { 0x1F44, 0x08ae }, // 174, 8, + { 0x1F45, 0x08b1 }, // 177, 8, + { 0x1F50, 0x08b9 }, // 185, 8, + { 0x1F51, 0x08bd }, // 189, 8, + { 0x1F52, 0x08bb }, // 187, 8, + { 0x1F53, 0x08bf }, // 191, 8, + { 0x1F54, 0x08ba }, // 186, 8, + { 0x1F55, 0x08be }, // 190, 8, + { 0x1F60, 0x08c7 }, // 199, 8, + { 0x1F61, 0x08cf }, // 207, 8, + { 0x1F62, 0x08c9 }, // 201, 8, + { 0x1F63, 0x08d1 }, // 209, 8, + { 0x1F64, 0x08c8 }, // 200, 8, + { 0x1F65, 0x08d0 }, // 208, 8, + { 0x1F70, 0x086d }, // 109, 8, + { 0x1F72, 0x0883 }, // 131, 8, + { 0x1F74, 0x088a }, // 138, 8, + { 0x1F76, 0x08a0 }, // 160, 8, + { 0x1F78, 0x08ac }, // 172, 8, + { 0x1F7A, 0x08b5 }, // 181, 8, + { 0x1F7C, 0x08c1 }, // 193, 8, + { 0x1F80, 0x0877 }, // 119, 8, + { 0x1F81, 0x087f }, // 127, 8, + { 0x1F82, 0x0879 }, // 121, 8, + { 0x1F83, 0x0881 }, // 129, 8, + { 0x1F84, 0x0878 }, // 120, 8, + { 0x1F85, 0x0880 }, // 128, 8, + { 0x1F90, 0x0894 }, // 148, 8, + { 0x1F91, 0x089c }, // 156, 8, + { 0x1F92, 0x0896 }, // 150, 8, + { 0x1F93, 0x089e }, // 158, 8, + { 0x1F94, 0x0895 }, // 149, 8, + { 0x1F95, 0x089d }, // 157, 8, + { 0x1FA0, 0x08cb }, // 203, 8, + { 0x1FA1, 0x08d3 }, // 211, 8, + { 0x1FA2, 0x08cd }, // 205, 8, + { 0x1FA3, 0x08d5 }, // 213, 8, + { 0x1FA4, 0x08cc }, // 204, 8, + { 0x1FA5, 0x08d4 }, // 212, 8, + { 0x1FB2, 0x0871 }, // 113, 8, + { 0x1FB3, 0x086f }, // 111, 8, + { 0x1FB4, 0x0870 }, // 112, 8, + { 0x1FC2, 0x088e }, // 142, 8, + { 0x1FC3, 0x088c }, // 140, 8, + { 0x1FC4, 0x088d }, // 141, 8, + { 0x1FCD, 0x085e }, // 94 , 8, + { 0x1FCE, 0x085c }, // 92 , 8, + { 0x1FDD, 0x085f }, // 95 , 8, + { 0x1FDE, 0x085d }, // 93 , 8, + { 0x1FE4, 0x08B4 }, // 180, 8, + { 0x1FE5, 0x08B3 }, // 179, 8, + { 0x1FF2, 0x08c5 }, // 197, 8, + { 0x1FF3, 0x08c3 }, // 195, 8, + { 0x1FF4, 0x08c4 }, // 196, 8, + { 0x2007, 0x0517 }, // 23 , 5, + { 0x2012, 0x0432 }, // 50 , 4, + { 0x2013, 0x0421 }, // 33 , 4, + { 0x2014, 0x0422 }, // 34 , 4, + { 0x2017, 0x022f }, // 47 , 2, + { 0x2018, 0x041d }, // 29 , 4, + { 0x2019, 0x041c }, // 28 , 4, + { 0x201A, 0x043e }, // 62 , 4, + { 0x201B, 0x041b }, // 27 , 4, + { 0x201C, 0x0420 }, // 32 , 4, + { 0x201D, 0x041f }, // 31 , 4, + { 0x201E, 0x043f }, // 63 , 4, + { 0x201F, 0x041e }, // 30 , 4, + { 0x2020, 0x0427 }, // 39 , 4, + { 0x2021, 0x0428 }, // 40 , 4, + { 0x2022, 0x0403 }, // 3 , 4, + { 0x2026, 0x0438 }, // 56 , 4, + { 0x2030, 0x044b }, // 75 , 4, + { 0x2033, 0x0580 }, // 128, 5, + { 0x2034, 0x0671 }, // 113, 6, + { 0x2036, 0x057f }, // 127, 5, + { 0x2039, 0x0423 }, // 35 , 4, + { 0x203A, 0x0424 }, // 36 , 4, + { 0x203C, 0x050d }, // 13 , 5, + { 0x203E, 0x0626 }, // 38 , 6, + { 0x207F, 0x0415 }, // 21 , 4, + { 0x20A0, 0x043c }, // 60 , 4, + { 0x20A2, 0x043b }, // 59 , 4, + { 0x20A3, 0x043a }, // 58 , 4, + { 0x20A4, 0x043d }, // 61 , 4, + { 0x20A6, 0x0457 }, // 87 , 4, + { 0x20A7, 0x040d }, // 13 , 4, + { 0x20A8, 0x0458 }, // 88 , 4, + { 0x20A9, 0x0456 }, // 86 , 4, + { 0x20AA, 0x097A }, // 122, 9, + { 0x20AC, 0x0466 }, // 102, 4, + { 0x20DD, 0x066d }, // 109, 6, + { 0x20E1, 0x06e1 }, // 225, 6, + { 0x2102, 0x06d5 }, // 213, 6, + { 0x2104, 0x0515 }, // 21 , 5, + { 0x2105, 0x0449 }, // 73 , 4, + { 0x2106, 0x044a }, // 74 , 4, + { 0x210C, 0x06e9 }, // 233, 6, + { 0x210F, 0x0632 }, // 50 , 6, + { 0x2111, 0x0633 }, // 51 , 6, + { 0x2112, 0x0669 }, // 105, 6, + { 0x2113, 0x0631 }, // 49 , 6, + { 0x2115, 0x06d7 }, // 215, 6, + { 0x2116, 0x044c }, // 76 , 4, + { 0x2118, 0x0635 }, // 53 , 6, + { 0x211C, 0x0634 }, // 52 , 6, + { 0x211D, 0x06d8 }, // 216, 6, + { 0x211E, 0x042b }, // 43 , 4, + { 0x2120, 0x042a }, // 42 , 4, + { 0x2122, 0x0429 }, // 41 , 4, + { 0x2127, 0x06a7 }, // 167, 6, + { 0x2128, 0x066b }, // 107, 6, + { 0x212B, 0x0623 }, // 35 , 6, + { 0x212D, 0x066a }, // 106, 6, + { 0x212F, 0x0630 }, // 48 , 6, + { 0x2130, 0x06d3 }, // 211, 6, + { 0x2131, 0x06d4 }, // 212, 6, + { 0x2153, 0x0440 }, // 64 , 4, + { 0x2154, 0x0441 }, // 65 , 4, + { 0x215B, 0x0442 }, // 66 , 4, + { 0x215C, 0x0443 }, // 67 , 4, + { 0x215D, 0x0444 }, // 68 , 4, + { 0x215E, 0x0445 }, // 69 , 4, + { 0x2190, 0x0590 }, // 144, 5, + { 0x2191, 0x0617 }, // 23 , 6, + { 0x2192, 0x05d5 }, // 213, 5, + { 0x2193, 0x0618 }, // 24 , 6, + { 0x2194, 0x05d6 }, // 214, 5, + { 0x2195, 0x05d7 }, // 215, 5, + { 0x2196, 0x0640 }, // 64 , 6, + { 0x2197, 0x063e }, // 62 , 6, + { 0x2198, 0x063f }, // 63 , 6, + { 0x2199, 0x0641 }, // 65 , 6, + { 0x219D, 0x0690 }, // 144, 6, + { 0x21A3, 0x0693 }, // 147, 6, + { 0x21A8, 0x050f }, // 15 , 5, + { 0x21A9, 0x0691 }, // 145, 6, + { 0x21AA, 0x0692 }, // 146, 6, + { 0x21B5, 0x0514 }, // 20 , 5, + { 0x21BC, 0x0694 }, // 148, 6, + { 0x21BD, 0x0695 }, // 149, 6, + { 0x21BE, 0x069b }, // 155, 6, + { 0x21BF, 0x069a }, // 154, 6, + { 0x21C0, 0x0696 }, // 150, 6, + { 0x21C1, 0x0697 }, // 151, 6, + { 0x21C2, 0x069d }, // 157, 6, + { 0x21C3, 0x069c }, // 156, 6, + { 0x21C4, 0x0636 }, // 54 , 6, + { 0x21C6, 0x0637 }, // 55 , 6, + { 0x21C7, 0x069f }, // 159, 6, + { 0x21C9, 0x069e }, // 158, 6, + { 0x21CB, 0x0699 }, // 153, 6, + { 0x21CC, 0x0698 }, // 152, 6, + { 0x21D0, 0x0639 }, // 57 , 6, + { 0x21D1, 0x063a }, // 58 , 6, + { 0x21D2, 0x0638 }, // 56 , 6, + { 0x21D3, 0x063b }, // 59 , 6, + { 0x21D4, 0x063c }, // 60 , 6, + { 0x21D5, 0x063d }, // 61 , 6, + { 0x21E6, 0x0597 }, // 151, 5, + { 0x21E8, 0x0596 }, // 150, 5, + { 0x2200, 0x067a }, // 122, 6, + { 0x2202, 0x062c }, // 44 , 6, + { 0x2203, 0x0679 }, // 121, 6, + { 0x2204, 0x06d0 }, // 208, 6, + { 0x2205, 0x0648 }, // 72 , 6, + { 0x2207, 0x062b }, // 43 , 6, + { 0x2208, 0x060f }, // 15 , 6, + { 0x2209, 0x06d1 }, // 209, 6, + { 0x220B, 0x06db }, // 219, 6, + { 0x220D, 0x0647 }, // 71 , 6, + { 0x220F, 0x0629 }, // 41 , 6, + { 0x2210, 0x0672 }, // 114, 6, + { 0x2211, 0x0612 }, // 18 , 6, + { 0x2212, 0x0600 }, // 0 , 6, + { 0x2213, 0x062a }, // 42 , 6, + { 0x2214, 0x06ae }, // 174, 6, + { 0x2215, 0x0606 }, // 6 , 6, + { 0x2216, 0x0607 }, // 7 , 6, + { 0x2218, 0x0621 }, // 33 , 6, + { 0x2219, 0x0622 }, // 34 , 6, + { 0x221A, 0x0704 }, // 4 , 7, + { 0x221D, 0x0604 }, // 4 , 6, + { 0x221E, 0x0613 }, // 19 , 6, + { 0x221F, 0x06da }, // 218, 6, + { 0x2220, 0x064f }, // 79 , 6, + { 0x2221, 0x06a8 }, // 168, 6, + { 0x2222, 0x06a9 }, // 169, 6, + { 0x2223, 0x0609 }, // 9 , 6, + { 0x2224, 0x06ce }, // 206, 6, + { 0x2225, 0x0611 }, // 17 , 6, + { 0x2226, 0x06cd }, // 205, 6, + { 0x2227, 0x0655 }, // 85 , 6, + { 0x2228, 0x0656 }, // 86 , 6, + { 0x2229, 0x0610 }, // 16 , 6, + { 0x222A, 0x0642 }, // 66 , 6, + { 0x222B, 0x0628 }, // 40 , 6, + { 0x222E, 0x0668 }, // 104, 6, + { 0x2234, 0x0666 }, // 102, 6, + { 0x2235, 0x0665 }, // 101, 6, + { 0x2237, 0x0667 }, // 103, 6, + { 0x223C, 0x060c }, // 12 , 6, + { 0x2241, 0x06bd }, // 189, 6, + { 0x2243, 0x0673 }, // 115, 6, + { 0x2244, 0x06be }, // 190, 6, + { 0x2245, 0x0674 }, // 116, 6, + { 0x2247, 0x06bf }, // 191, 6, + { 0x2248, 0x060d }, // 13 , 6, + { 0x2249, 0x06c0 }, // 192, 6, + { 0x224D, 0x06b3 }, // 179, 6, + { 0x224E, 0x06b2 }, // 178, 6, + { 0x2250, 0x06af }, // 175, 6, + { 0x2252, 0x06b0 }, // 176, 6, + { 0x2253, 0x06b1 }, // 177, 6, + { 0x225F, 0x06d9 }, // 217, 6, + { 0x2260, 0x0663 }, // 99 , 6, + { 0x2261, 0x060e }, // 14 , 6, + { 0x2262, 0x0664 }, // 100, 6, + { 0x2264, 0x0602 }, // 2 , 6, + { 0x2265, 0x0603 }, // 3 , 6, + { 0x226A, 0x064d }, // 77 , 6, + { 0x226B, 0x064e }, // 78 , 6, + { 0x226C, 0x06b6 }, // 182, 6, + { 0x226D, 0x06cf }, // 207, 6, + { 0x226E, 0x06b9 }, // 185, 6, + { 0x226F, 0x06bb }, // 187, 6, + { 0x2270, 0x06ba }, // 186, 6, + { 0x2271, 0x06bc }, // 188, 6, + { 0x2272, 0x06eb }, // 235, 6, + { 0x2273, 0x06ec }, // 236, 6, + { 0x227A, 0x0675 }, // 117, 6, + { 0x227B, 0x0677 }, // 119, 6, + { 0x227C, 0x0676 }, // 118, 6, + { 0x227D, 0x0678 }, // 120, 6, + { 0x2280, 0x06c1 }, // 193, 6, + { 0x2281, 0x06c3 }, // 195, 6, + { 0x2282, 0x0643 }, // 67 , 6, + { 0x2283, 0x0644 }, // 68 , 6, + { 0x2284, 0x06c5 }, // 197, 6, + { 0x2285, 0x06c6 }, // 198, 6, + { 0x2286, 0x0645 }, // 69 , 6, + { 0x2287, 0x0646 }, // 70 , 6, + { 0x2288, 0x06c7 }, // 199, 6, + { 0x2289, 0x06c8 }, // 200, 6, + { 0x228A, 0x067e }, // 126, 6, + { 0x228B, 0x067f }, // 127, 6, + { 0x228E, 0x067d }, // 125, 6, + { 0x228F, 0x0682 }, // 130, 6, + { 0x2290, 0x0685 }, // 133, 6, + { 0x2291, 0x0683 }, // 131, 6, + { 0x2292, 0x0686 }, // 134, 6, + { 0x2293, 0x0680 }, // 128, 6, + { 0x2294, 0x0681 }, // 129, 6, + { 0x2295, 0x0651 }, // 81 , 6, + { 0x2296, 0x0652 }, // 82 , 6, + { 0x2297, 0x0650 }, // 80 , 6, + { 0x2299, 0x0654 }, // 84 , 6, + { 0x229A, 0x06a4 }, // 164, 6, + { 0x229B, 0x06a5 }, // 165, 6, + { 0x229D, 0x06a6 }, // 166, 6, + { 0x22A2, 0x065b }, // 91 , 6, + { 0x22A3, 0x065c }, // 92 , 6, + { 0x22A4, 0x0658 }, // 88 , 6, + { 0x22A5, 0x0659 }, // 89 , 6, + { 0x22A8, 0x06b4 }, // 180, 6, + { 0x22BB, 0x0657 }, // 87 , 6, + { 0x22C5, 0x061f }, // 31 , 6, + { 0x22C6, 0x0670 }, // 112, 6, + { 0x22C8, 0x068c }, // 140, 6, + { 0x22D0, 0x06a2 }, // 162, 6, + { 0x22D1, 0x06a3 }, // 163, 6, + { 0x22D2, 0x06a1 }, // 161, 6, + { 0x22D3, 0x06a0 }, // 160, 6, + { 0x22D8, 0x067b }, // 123, 6, + { 0x22D9, 0x067c }, // 124, 6, + { 0x22E0, 0x06c2 }, // 194, 6, + { 0x22E1, 0x06c4 }, // 196, 6, + { 0x22E2, 0x06cb }, // 203, 6, + { 0x22E3, 0x06cc }, // 204, 6, + { 0x22E4, 0x0684 }, // 132, 6, + { 0x22E5, 0x0687 }, // 135, 6, + { 0x22EE, 0x06de }, // 222, 6, + { 0x22EF, 0x06dc }, // 220, 6, + { 0x22F1, 0x06df }, // 223, 6, + { 0x2302, 0x050c }, // 12 , 5, + { 0x2308, 0x0649 }, // 73 , 6, + { 0x2309, 0x064a }, // 74 , 6, + { 0x230A, 0x064b }, // 75 , 6, + { 0x230B, 0x064c }, // 76 , 6, + { 0x2310, 0x0510 }, // 16 , 5, + { 0x2312, 0x065a }, // 90 , 6, + { 0x2319, 0x0511 }, // 17 , 5, + { 0x231A, 0x051f }, // 31 , 5, + { 0x231B, 0x0520 }, // 32 , 5, + { 0x2320, 0x0700 }, // 0 , 7, + { 0x2321, 0x0701 }, // 1 , 7, + { 0x2322, 0x068e }, // 142, 6, + { 0x2323, 0x068d }, // 141, 6, + { 0x2329, 0x060a }, // 10 , 6, + { 0x232A, 0x060b }, // 11 , 6, + { 0x2409, 0x044f }, // 79 , 4, + { 0x240A, 0x0452 }, // 82 , 4, + { 0x240B, 0x0454 }, // 84 , 4, + { 0x240C, 0x0450 }, // 80 , 4, + { 0x240D, 0x0451 }, // 81 , 4, + { 0x2424, 0x0453 }, // 83 , 4, + { 0x24C2, 0x0446 }, // 70 , 4, + { 0x24C5, 0x0447 }, // 71 , 4, + { 0x24CA, 0x0448 }, // 72 , 4, + { 0x2500, 0x0308 }, // 8 , 3, + { 0x2502, 0x0309 }, // 9 , 3, + { 0x250C, 0x030a }, // 10 , 3, + { 0x2510, 0x030b }, // 11 , 3, + { 0x2514, 0x030d }, // 13 , 3, + { 0x2518, 0x030c }, // 12 , 3, + { 0x251C, 0x030e }, // 14 , 3, + { 0x251E, 0x033e }, // 62 , 3, + { 0x251F, 0x033c }, // 60 , 3, + { 0x2521, 0x033f }, // 63 , 3, + { 0x2522, 0x033d }, // 61 , 3, + { 0x2524, 0x0310 }, // 16 , 3, + { 0x2526, 0x0345 }, // 69 , 3, + { 0x2527, 0x0344 }, // 68 , 3, + { 0x2529, 0x0347 }, // 71 , 3, + { 0x252A, 0x0346 }, // 70 , 3, + { 0x252C, 0x030f }, // 15 , 3, + { 0x252D, 0x0342 }, // 66 , 3, + { 0x252E, 0x0340 }, // 64 , 3, + { 0x2531, 0x0343 }, // 67 , 3, + { 0x2532, 0x0341 }, // 65 , 3, + { 0x2534, 0x0311 }, // 17 , 3, + { 0x2535, 0x034a }, // 74 , 3, + { 0x2536, 0x0348 }, // 72 , 3, + { 0x2539, 0x034b }, // 75 , 3, + { 0x253A, 0x0349 }, // 73 , 3, + { 0x253C, 0x0312 }, // 18 , 3, + { 0x253D, 0x0352 }, // 82 , 3, + { 0x253E, 0x034e }, // 78 , 3, + { 0x2540, 0x034f }, // 79 , 3, + { 0x2541, 0x034c }, // 76 , 3, + { 0x2543, 0x0355 }, // 85 , 3, + { 0x2544, 0x0350 }, // 80 , 3, + { 0x2545, 0x0353 }, // 83 , 3, + { 0x2546, 0x034d }, // 77 , 3, + { 0x2547, 0x0357 }, // 87 , 3, + { 0x2548, 0x0354 }, // 84 , 3, + { 0x2549, 0x0356 }, // 86 , 3, + { 0x254A, 0x0351 }, // 81 , 3, + { 0x2550, 0x0313 }, // 19 , 3, + { 0x2551, 0x0314 }, // 20 , 3, + { 0x2552, 0x031e }, // 30 , 3, + { 0x2553, 0x0322 }, // 34 , 3, + { 0x2554, 0x0315 }, // 21 , 3, + { 0x2555, 0x031f }, // 31 , 3, + { 0x2556, 0x0323 }, // 35 , 3, + { 0x2557, 0x0316 }, // 22 , 3, + { 0x2558, 0x0321 }, // 33 , 3, + { 0x2559, 0x0325 }, // 37 , 3, + { 0x255A, 0x0318 }, // 24 , 3, + { 0x255B, 0x0320 }, // 32 , 3, + { 0x255C, 0x0324 }, // 36 , 3, + { 0x255D, 0x0317 }, // 23 , 3, + { 0x255E, 0x0326 }, // 38 , 3, + { 0x255F, 0x032a }, // 42 , 3, + { 0x2560, 0x0319 }, // 25 , 3, + { 0x2561, 0x0328 }, // 40 , 3, + { 0x2562, 0x032c }, // 44 , 3, + { 0x2563, 0x031b }, // 27 , 3, + { 0x2564, 0x032b }, // 43 , 3, + { 0x2565, 0x0327 }, // 39 , 3, + { 0x2566, 0x031a }, // 26 , 3, + { 0x2567, 0x032d }, // 45 , 3, + { 0x2568, 0x0329 }, // 41 , 3, + { 0x2569, 0x031c }, // 28 , 3, + { 0x256A, 0x032f }, // 47 , 3, + { 0x256B, 0x032e }, // 46 , 3, + { 0x256C, 0x031d }, // 29 , 3, + { 0x2574, 0x0330 }, // 48 , 3, + { 0x2575, 0x0331 }, // 49 , 3, + { 0x2576, 0x0332 }, // 50 , 3, + { 0x2577, 0x0333 }, // 51 , 3, + { 0x2578, 0x0334 }, // 52 , 3, + { 0x2579, 0x0335 }, // 53 , 3, + { 0x257A, 0x0336 }, // 54 , 3, + { 0x257B, 0x0337 }, // 55 , 3, + { 0x257C, 0x0338 }, // 56 , 3, + { 0x257D, 0x033a }, // 58 , 3, + { 0x257E, 0x0339 }, // 57 , 3, + { 0x257F, 0x033b }, // 59 , 3, + { 0x2580, 0x0305 }, // 5 , 3, + { 0x2584, 0x0307 }, // 7 , 3, + { 0x2588, 0x0303 }, // 3 , 3, + { 0x258C, 0x0304 }, // 4 , 3, + { 0x2590, 0x0306 }, // 6 , 3, + { 0x2591, 0x0300 }, // 0 , 3, + { 0x2592, 0x0301 }, // 1 , 3, + { 0x2593, 0x0302 }, // 2 , 3, + { 0x25A0, 0x0402 }, // 2 , 4, + { 0x25A1, 0x0426 }, // 38 , 4, + { 0x25AA, 0x042f }, // 47 , 4, + { 0x25AB, 0x0431 }, // 49 , 4, + { 0x25AC, 0x050b }, // 11 , 5, + { 0x25B2, 0x0573 }, // 115, 5, + { 0x25B3, 0x0688 }, // 136, 6, + { 0x25B4, 0x061d }, // 29 , 6, + { 0x25B5, 0x06ac }, // 172, 6, + { 0x25B8, 0x061b }, // 27 , 6, + { 0x25B9, 0x068b }, // 139, 6, + { 0x25BC, 0x0574 }, // 116, 5, + { 0x25BD, 0x0689 }, // 137, 6, + { 0x25BE, 0x061e }, // 30 , 6, + { 0x25BF, 0x06ad }, // 173, 6, + { 0x25C2, 0x061c }, // 28 , 6, + { 0x25C3, 0x068a }, // 138, 6, + { 0x25C6, 0x0575 }, // 117, 5, + { 0x25C7, 0x066f }, // 111, 6, + { 0x25CA, 0x065f }, // 95 , 6, + { 0x25CB, 0x0401 }, // 1 , 4, + { 0x25CF, 0x0400 }, // 0 , 4, + { 0x25D6, 0x059e }, // 158, 5, + { 0x25D7, 0x0577 }, // 119, 5, + { 0x25D8, 0x0512 }, // 18 , 5, + { 0x25D9, 0x0513 }, // 19 , 5, + { 0x25E6, 0x042d }, // 45 , 4, + { 0x2605, 0x0548 }, // 72, 5, + { 0x260E, 0x051e }, // 30 , 5, + { 0x2610, 0x0518 }, // 24 , 5, + { 0x2612, 0x0519 }, // 25 , 5, + { 0x261B, 0x052a }, // 42 , 5, + { 0x261C, 0x0516 }, // 22 , 5, + { 0x261E, 0x052b }, // 43 , 5, + { 0x2639, 0x051a }, // 26 , 5, + { 0x263A, 0x0507 }, // 7 , 5, + { 0x263B, 0x0508 }, // 8 , 5, + { 0x263C, 0x0506 }, // 6 , 5, + { 0x2640, 0x0505 }, // 5 , 5, + { 0x2642, 0x0504 }, // 4 , 5, + { 0x2660, 0x05ab }, // 171, 5, + { 0x2661, 0x0500 }, // 0 , 5, + { 0x2662, 0x0501 }, // 1 , 5, + { 0x2663, 0x05a8 }, // 168, 5, + { 0x2664, 0x0503 }, // 3 , 5, + { 0x2665, 0x05aa }, // 170, 5, + { 0x2666, 0x05a9 }, // 169, 5, + { 0x2667, 0x0502 }, // 2 , 5, + { 0x266A, 0x0509 }, // 9 , 5, + { 0x266C, 0x050a }, // 10 , 5, + { 0x266D, 0x051c }, // 28 , 5, + { 0x266E, 0x051d }, // 29 , 5, + { 0x266F, 0x051b }, // 27 , 5, + { 0x2701, 0x0521 }, // 33 , 5, + { 0x2702, 0x0522 }, // 34 , 5, + { 0x2703, 0x0523 }, // 35 , 5, + { 0x2704, 0x0524 }, // 36 , 5, + { 0x2706, 0x0526 }, // 38 , 5, + { 0x2707, 0x0527 }, // 39 , 5, + { 0x2708, 0x0528 }, // 40 , 5, + { 0x2709, 0x0529 }, // 41 , 5, + { 0x270C, 0x052c }, // 44 , 5, + { 0x270D, 0x052d }, // 45 , 5, + { 0x270E, 0x052e }, // 46 , 5, + { 0x270F, 0x052f }, // 47 , 5, + { 0x2710, 0x0530 }, // 48 , 5, + { 0x2711, 0x0531 }, // 49 , 5, + { 0x2712, 0x0532 }, // 50 , 5, + { 0x2713, 0x0533 }, // 51 , 5, + { 0x2714, 0x0534 }, // 52 , 5, + { 0x2715, 0x0535 }, // 53 , 5, + { 0x2716, 0x0536 }, // 54 , 5, + { 0x2717, 0x0537 }, // 55 , 5, + { 0x2718, 0x0538 }, // 56 , 5, + { 0x2719, 0x0539 }, // 57 , 5, + { 0x271A, 0x053a }, // 58 , 5, + { 0x271B, 0x053b }, // 59 , 5, + { 0x271C, 0x053c }, // 60 , 5, + { 0x271D, 0x053d }, // 61 , 5, + { 0x271E, 0x053e }, // 62 , 5, + { 0x271F, 0x053f }, // 63 , 5, + { 0x2720, 0x0540 }, // 64 , 5, + { 0x2721, 0x0541 }, // 65 , 5, + { 0x2722, 0x0542 }, // 66 , 5, + { 0x2723, 0x0543 }, // 67 , 5, + { 0x2724, 0x0544 }, // 68 , 5, + { 0x2725, 0x0545 }, // 69 , 5, + { 0x2726, 0x0546 }, // 70 , 5, + { 0x2727, 0x0547 }, // 71 , 5, + { 0x2729, 0x0549 }, // 73 , 5, + { 0x272A, 0x054a }, // 74 , 5, + { 0x272B, 0x054b }, // 75 , 5, + { 0x272C, 0x054c }, // 76 , 5, + { 0x272D, 0x054d }, // 77 , 5, + { 0x272E, 0x054e }, // 78 , 5, + { 0x272F, 0x054f }, // 79 , 5, + { 0x2730, 0x0550 }, // 80 , 5, + { 0x2731, 0x0551 }, // 81 , 5, + { 0x2732, 0x0552 }, // 82 , 5, + { 0x2733, 0x0553 }, // 83 , 5, + { 0x2734, 0x0554 }, // 84 , 5, + { 0x2735, 0x0555 }, // 85 , 5, + { 0x2736, 0x0556 }, // 86 , 5, + { 0x2737, 0x0557 }, // 87 , 5, + { 0x2738, 0x0558 }, // 88 , 5, + { 0x2739, 0x0559 }, // 89 , 5, + { 0x273A, 0x055a }, // 90 , 5, + { 0x273B, 0x055b }, // 91 , 5, + { 0x273C, 0x055c }, // 92 , 5, + { 0x273D, 0x055d }, // 93 , 5, + { 0x273E, 0x055e }, // 94 , 5, + { 0x273F, 0x055f }, // 95 , 5, + { 0x2740, 0x0560 }, // 96 , 5, + { 0x2741, 0x0561 }, // 97 , 5, + { 0x2742, 0x0562 }, // 98 , 5, + { 0x2743, 0x0563 }, // 99 , 5, + { 0x2744, 0x0564 }, // 100, 5, + { 0x2745, 0x0565 }, // 101, 5, + { 0x2746, 0x0566 }, // 102, 5, + { 0x2747, 0x0567 }, // 103, 5, + { 0x2748, 0x0568 }, // 104, 5, + { 0x2749, 0x0569 }, // 105, 5, + { 0x274A, 0x056a }, // 106, 5, + { 0x274B, 0x056b }, // 107, 5, + { 0x274D, 0x056d }, // 109, 5, + { 0x274F, 0x056f }, // 111, 5, + { 0x2750, 0x0570 }, // 112, 5, + { 0x2751, 0x0571 }, // 113, 5, + { 0x2752, 0x0572 }, // 114, 5, + { 0x2756, 0x0576 }, // 118, 5, + { 0x2758, 0x0578 }, // 120, 5, + { 0x2759, 0x0579 }, // 121, 5, + { 0x275A, 0x057a }, // 122, 5, + { 0x275B, 0x057b }, // 123, 5, + { 0x275C, 0x057c }, // 124, 5, + { 0x275D, 0x057d }, // 125, 5, + { 0x275E, 0x057e }, // 126, 5, + { 0x2761, 0x05a1 }, // 161, 5, + { 0x2762, 0x05a2 }, // 162, 5, + { 0x2763, 0x05a3 }, // 163, 5, + { 0x2764, 0x05a4 }, // 164, 5, + { 0x2765, 0x05a5 }, // 165, 5, + { 0x2766, 0x05a6 }, // 166, 5, + { 0x2767, 0x05a7 }, // 167, 5, + { 0x2776, 0x05b6 }, // 182, 5, + { 0x2777, 0x05b7 }, // 183, 5, + { 0x2778, 0x05b8 }, // 184, 5, + { 0x2779, 0x05b9 }, // 185, 5, + { 0x277A, 0x05ba }, // 186, 5, + { 0x277B, 0x05bb }, // 187, 5, + { 0x277C, 0x05bc }, // 188, 5, + { 0x277D, 0x05bd }, // 189, 5, + { 0x277E, 0x05be }, // 190, 5, + { 0x277F, 0x05bf }, // 191, 5, + { 0x2780, 0x05c0 }, // 192, 5, + { 0x2781, 0x05c1 }, // 193, 5, + { 0x2782, 0x05c2 }, // 194, 5, + { 0x2783, 0x05c3 }, // 195, 5, + { 0x2784, 0x05c4 }, // 196, 5, + { 0x2785, 0x05c5 }, // 197, 5, + { 0x2786, 0x05c6 }, // 198, 5, + { 0x2787, 0x05c7 }, // 199, 5, + { 0x2788, 0x05c8 }, // 200, 5, + { 0x2789, 0x05c9 }, // 201, 5, + { 0x278A, 0x05ca }, // 202, 5, + { 0x278B, 0x05cb }, // 203, 5, + { 0x278C, 0x05cc }, // 204, 5, + { 0x278D, 0x05cd }, // 205, 5, + { 0x278E, 0x05ce }, // 206, 5, + { 0x278F, 0x05cf }, // 207, 5, + { 0x2790, 0x05d0 }, // 208, 5, + { 0x2791, 0x05d1 }, // 209, 5, + { 0x2792, 0x05d2 }, // 210, 5, + { 0x2793, 0x05d3 }, // 211, 5, + { 0x2794, 0x05d4 }, // 212, 5, + { 0x2798, 0x05d8 }, // 216, 5, + { 0x2799, 0x05d9 }, // 217, 5, + { 0x279A, 0x05da }, // 218, 5, + { 0x279B, 0x05db }, // 219, 5, + { 0x279C, 0x05dc }, // 220, 5, + { 0x279D, 0x05dd }, // 221, 5, + { 0x279E, 0x05de }, // 222, 5, + { 0x279F, 0x05df }, // 223, 5, + { 0x27A0, 0x05e0 }, // 224, 5, + { 0x27A1, 0x05e1 }, // 225, 5, + { 0x27A2, 0x05e2 }, // 226, 5, + { 0x27A3, 0x05e3 }, // 227, 5, + { 0x27A4, 0x05e4 }, // 228, 5, + { 0x27A5, 0x05e5 }, // 229, 5, + { 0x27A6, 0x05e6 }, // 230, 5, + { 0x27A7, 0x05e7 }, // 231, 5, + { 0x27A8, 0x05e8 }, // 232, 5, + { 0x27A9, 0x05e9 }, // 233, 5, + { 0x27AA, 0x05ea }, // 234, 5, + { 0x27AB, 0x05eb }, // 235, 5, + { 0x27AC, 0x05ec }, // 236, 5, + { 0x27AD, 0x05ed }, // 237, 5, + { 0x27AE, 0x05ee }, // 238, 5, + { 0x27AF, 0x05ef }, // 239, 5, + { 0x27B1, 0x05f1 }, // 241, 5, + { 0x27B2, 0x05f2 }, // 242, 5, + { 0x27B3, 0x05f3 }, // 243, 5, + { 0x27B4, 0x05f4 }, // 244, 5, + { 0x27B5, 0x05f5 }, // 245, 5, + { 0x27B6, 0x05f6 }, // 246, 5, + { 0x27B7, 0x05f7 }, // 247, 5, + { 0x27B8, 0x05f8 }, // 248, 5, + { 0x27B9, 0x05f9 }, // 249, 5, + { 0x27BA, 0x05fa }, // 250, 5, + { 0x27BB, 0x05fb }, // 251, 5, + { 0x27BC, 0x05fc }, // 252, 5, + { 0x27BD, 0x05fd }, // 253, 5, + { 0x27BE, 0x05fe }, // 254, 5, + { 0xFB00, 0x0433 }, // 51 , 4, + { 0xFB01, 0x0436 }, // 54 , 4, + { 0xFB02, 0x0437 }, // 55 , 4, + { 0xFB03, 0x0434 }, // 52 , 4, + { 0xFB04, 0x0435 }, // 53 , 4, + { 0xFB1E, 0x0930 }, // 48 , 9, + { 0xFF61, 0x0b00 }, // 0 , 11, + { 0xFF62, 0x0b01 }, // 1 , 11, + { 0xFF63, 0x0b02 }, // 2 , 11, + { 0xFF64, 0x0b03 }, // 3 , 11, + { 0xFF65, 0x0b04 }, // 4 , 11, + { 0xFF66, 0x0b05 }, // 5 , 11, + { 0xFF67, 0x0b06 }, // 6 , 11, + { 0xFF68, 0x0b07 }, // 7 , 11, + { 0xFF69, 0x0b08 }, // 8 , 11, + { 0xFF6A, 0x0b09 }, // 9 , 11, + { 0xFF6B, 0x0b0a }, // 10 , 11, + { 0xFF6C, 0x0b0b }, // 11 , 11, + { 0xFF6D, 0x0b0c }, // 12 , 11, + { 0xFF6E, 0x0b0d }, // 13 , 11, + { 0xFF6F, 0x0b0e }, // 14 , 11, + { 0xFF70, 0x0b0f }, // 15 , 11, + { 0xFF71, 0x0b10 }, // 16 , 11, + { 0xFF72, 0x0b11 }, // 17 , 11, + { 0xFF73, 0x0b12 }, // 18 , 11, + { 0xFF74, 0x0b13 }, // 19 , 11, + { 0xFF75, 0x0b14 }, // 20 , 11, + { 0xFF76, 0x0b15 }, // 21 , 11, + { 0xFF77, 0x0b16 }, // 22 , 11, + { 0xFF78, 0x0b17 }, // 23 , 11, + { 0xFF79, 0x0b18 }, // 24 , 11, + { 0xFF7A, 0x0b19 }, // 25 , 11, + { 0xFF7B, 0x0b1a }, // 26 , 11, + { 0xFF7C, 0x0b1b }, // 27 , 11, + { 0xFF7D, 0x0b1c }, // 28 , 11, + { 0xFF7E, 0x0b1d }, // 29 , 11, + { 0xFF7F, 0x0b1e }, // 30 , 11, + { 0xFF80, 0x0b1f }, // 31 , 11, + { 0xFF81, 0x0b20 }, // 32 , 11, + { 0xFF82, 0x0b21 }, // 33 , 11, + { 0xFF83, 0x0b22 }, // 34 , 11, + { 0xFF84, 0x0b23 }, // 35 , 11, + { 0xFF85, 0x0b24 }, // 36 , 11, + { 0xFF86, 0x0b25 }, // 37 , 11, + { 0xFF87, 0x0b26 }, // 38 , 11, + { 0xFF88, 0x0b27 }, // 39 , 11, + { 0xFF89, 0x0b28 }, // 40 , 11, + { 0xFF8A, 0x0b29 }, // 41 , 11, + { 0xFF8B, 0x0b2a }, // 42 , 11, + { 0xFF8C, 0x0b2b }, // 43 , 11, + { 0xFF8D, 0x0b2c }, // 44 , 11, + { 0xFF8E, 0x0b2d }, // 45 , 11, + { 0xFF8F, 0x0b2e }, // 46 , 11, + { 0xFF90, 0x0b2f }, // 47 , 11, + { 0xFF91, 0x0b30 }, // 48 , 11, + { 0xFF92, 0x0b31 }, // 49 , 11, + { 0xFF93, 0x0b32 }, // 50 , 11, + { 0xFF94, 0x0b33 }, // 51 , 11, + { 0xFF95, 0x0b34 }, // 52 , 11, + { 0xFF96, 0x0b35 }, // 53 , 11, + { 0xFF97, 0x0b36 }, // 54 , 11, + { 0xFF98, 0x0b37 }, // 55 , 11, + { 0xFF99, 0x0b38 }, // 56 , 11, + { 0xFF9A, 0x0b39 }, // 57 , 11, + { 0xFF9B, 0x0b3a }, // 58 , 11, + { 0xFF9C, 0x0b3b }, // 59 , 11, + { 0xFF9D, 0x0b3c }, // 60 , 11, + { 0xFF9E, 0x0b3d }, // 61 , 11, + { 0xFF9F, 0x0b3e } // 62 , 11 +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Multinational 1 +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI1[] = +{ + 0x0300, 0x00B7, 0x0303, 0x0302, 0x0335, + 0x0338, 0x0301, 0x0308, 0x0304, 0x0313, + 0x0315, 0x02BC, 0x0326, 0x0315, 0x030A, + 0x0307, 0x030B, 0x0327, 0x0328, 0x030C, + 0x0337, 0x0305, 0x0306, 0x00DF, 0x0138, + 0xF801, 0x00C1, 0x00E1, 0x00C2, 0x00E2, + 0x00C4, 0x00E4, 0x00C0, 0x00E0, 0x00C5, + 0x00E5, 0x00C6, 0x00E6, 0x00C7, 0x00E7, + 0x00C9, 0x00E9, 0x00CA, 0x00EA, 0x00CB, + 0x00EB, 0x00C8, 0x00E8, 0x00CD, 0x00ED, + 0x00CE, 0x00EE, 0x00CF, 0x00EF, 0x00CC, + 0x00EC, 0x00D1, 0x00F1, 0x00D3, 0x00F3, + 0x00D4, 0x00F4, 0x00D6, 0x00F6, 0x00D2, + 0x00F2, 0x00DA, 0x00FA, 0x00DB, 0x00FB, + 0x00DC, 0x00FC, 0x00D9, 0x00F9, 0x0178, + 0x00FF, 0x00C3, 0x00E3, 0x0110, 0x0111, + 0x00D8, 0x00F8, 0x00D5, 0x00F5, 0x00DD, + 0x00FD, 0x00D0, 0x00F0, 0x00DE, 0x00FE, + 0x0102, 0x0103, 0x0100, 0x0101, 0x0104, + 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, + 0x0108, 0x0109, 0x010A, 0x010B, 0x010E, + 0x010F, 0x011A, 0x011B, 0x0116, 0x0117, + 0x0112, 0x0113, 0x0118, 0x0119, 0x0047, + 0x0067, 0x011E, 0x011F, 0x0047, 0x0067, + 0x0122, 0x0123, 0x011C, 0x011D, 0x0120, + 0x0121, 0x0124, 0x0125, 0x0126, 0x0127, + 0x0130, 0x0069, 0x012A, 0x012B, 0x012E, + 0x012F, 0x0128, 0x0129, 0x0132, 0x0133, + 0x0134, 0x0135, 0x0136, 0x0137, 0x0139, + 0x013A, 0x013D, 0x013E, 0x013B, 0x013C, + 0x013F, 0x0140, 0x0141, 0x0142, 0x0143, + 0x0144, 0xF802, 0x0149, 0x0147, 0x0148, + 0x0145, 0x0146, 0x0150, 0x0151, 0x014C, + 0x014D, 0x0152, 0x0153, 0x0154, 0x0155, + 0x0158, 0x0159, 0x0156, 0x0157, 0x015A, + 0x015B, 0x0160, 0x0161, 0x015E, 0x015F, + 0x015C, 0x015D, 0x0164, 0x0165, 0x0162, + 0x0163, 0x0166, 0x0167, 0x016C, 0x016D, + 0x0170, 0x0171, 0x016A, 0x016B, 0x0172, + 0x0173, 0x016E, 0x016F, 0x0168, 0x0169, + 0x0174, 0x0175, 0x0176, 0x0177, 0x0179, + 0x017A, 0x017D, 0x017E, 0x017B, 0x017C, + 0x014A, 0x014B, 0xF000, 0xF001, 0xF002, + 0xF003, 0xF004, 0xF005, 0xF006, 0xF007, + 0xF008, 0xF009, 0xF00A, 0xF00B, 0xF00C, + 0xF00D, 0xF00E, 0xF00F, 0x010E, 0x010F, + 0x01A0, 0x01A1, 0x01AF, 0x01B0, 0x0114, + 0x0115, 0x012C, 0x012D, 0x0049, 0x0131, + 0x014E, 0x014F +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Standard Phonetic +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI2[] = +{ + 0x02B9, 0x02BA, 0x02BB, 0xF813, 0x02BD, + 0x02BC, 0xF814, 0x02BE, 0x02BF, 0x0310, + 0x02D0, 0x02D1, 0x0306, 0x032E, 0x0329, + 0x02C8, 0x02CC, 0x02C9, 0x02CA, 0x02CB, + 0x02CD, 0x02CE, 0x02CF, 0x02C6, 0x02C7, + 0x02DC, 0x0325, 0x02DA, 0x032D, 0x032C, + 0x0323, 0x0308, 0x0324, 0x031C, 0x031D, + 0x031E, 0x031F, 0x0320, 0x0321, 0x0322, + 0x032A, 0x032B, 0x02D2, 0x02D3, 0xF815, + 0xF816, 0x005F, 0x2017, 0x033E, 0x02DB, + 0x0327, 0x02DE, 0x02C8, 0x02B0, 0x02B6, + 0x0250, 0x0251, 0x0252, 0x0253, 0x0299, + 0x0254, 0x0255, 0x0297, 0x0256, 0x0257, + 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, + 0x025D, 0x029A, 0x025E, 0x025F, 0x0278, + 0x0261, 0x0260, 0x0262, 0x029B, 0x0263, + 0x0264, 0x0265, 0x0266, 0x0267, 0x029C, + 0x0268, 0x026A, 0x0269, 0x029D, 0x029E, + 0x026B, 0x026C, 0x026D, 0x029F, 0x026E, + 0x028E, 0x026F, 0x0270, 0x0271, 0x0272, + 0x0273, 0x0274, 0x0276, 0x0277, 0x02A0, + 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, + 0x027E, 0x027F, 0x0280, 0x0281, 0x0282, + 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, + 0x0288, 0x0275, 0x0289, 0x028A, 0x028C, + 0x028B, 0x028D, 0x0058, 0x028F, 0x0290, + 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, + 0x0296, 0x02A1, 0x02A2, 0x0298, 0x02A3, + 0x02A4, 0x02A5, 0x02A6, 0x02A7, 0x02A8 +}; + +/**************************************************************************** +Desc: WP51/WP60 to Unicode - Box Drawing +****************************************************************************/ +FLMUINT16 WPCH_WPUNI3[] = +{ + 0x2591, 0x2592, 0x2593, 0x2588, 0x258C, + 0x2580, 0x2590, 0x2584, 0x2500, 0x2502, + 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, + 0x252C, 0x2524, 0x2534, 0x253C, 0x2550, + 0x2551, 0x2554, 0x2557, 0x255D, 0x255A, + 0x2560, 0x2566, 0x2563, 0x2569, 0x256C, + 0x2552, 0x2555, 0x255B, 0x2558, 0x2553, + 0x2556, 0x255C, 0x2559, 0x255E, 0x2565, + 0x2561, 0x2568, 0x255F, 0x2564, 0x2562, + 0x2567, 0x256B, 0x256A, 0x2574, 0x2575, + 0x2576, 0x2577, 0x2578, 0x2579, 0x257A, + 0x257B, 0x257C, 0x257E, 0x257D, 0x257F, + 0x251F, 0x2522, 0x251E, 0x2521, 0x252E, + 0x2532, 0x252D, 0x2531, 0x2527, 0x2526, + 0x252A, 0x2529, 0x2536, 0x253A, 0x2535, + 0x2539, 0x2541, 0x2546, 0x253E, 0x2540, + 0x2544, 0x254A, 0x253D, 0x2545, 0x2548, + 0x2543, 0x2549, 0x2547 +}; + +/**************************************************************************** +Desc: WP51/WP60 to Unicode - Typographic Symbols +****************************************************************************/ +FLMUINT16 WPCH_WPUNI4[] = +{ + 0x25CF, 0x25CB, 0x25A0, 0x2022, 0xF817, + 0x00B6, 0x00A7, 0x00A1, 0x00BF, 0x00AB, + 0x00BB, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00AA, 0x00BA, 0x00BD, 0x00BC, 0x00A2, + 0x00B2, 0x207F, 0x00AE, 0x00A9, 0x00A4, + 0x00BE, 0x00B3, 0x201B, 0x2019, 0x2018, + 0x201F, 0x201D, 0x201C, 0x2013, 0x2014, + 0x2039, 0x203A, 0x25CB, 0x25A1, 0x2020, + 0x2021, 0x2122, 0x2120, 0x211E, 0x25CF, + 0x25E6, 0x25A0, 0x25AA, 0x25A1, 0x25AB, + 0x2012, 0xFB00, 0xFB03, 0xFB04, 0xFB01, + 0xFB02, 0x2026, 0x0024, 0x20A3, 0x20A2, + 0x20A0, 0x20A4, 0x201A, 0x201E, 0x2153, + 0x2154, 0x215B, 0x215C, 0x215D, 0x215E, + 0x24C2, 0x24C5, 0x24CA, 0x2105, 0x2106, + 0x2030, 0x2116, 0xF818, 0x00B9, 0x2409, + 0x240C, 0x240D, 0x240A, 0x2424, 0x240B, + 0xF819, 0x20A9, 0x20A6, 0x20A8, 0xF81A, + 0xF81B, 0xF81C, 0xF81D, 0xF81E, 0xF81F, + 0xF820, 0xF821, 0xF822, 0xF823, 0xF824, + 0xF825, 0xF826, 0x20AC +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Iconic Symbols +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI5[] = +{ + 0x2661, 0x2662, 0x2667, 0x2664, 0x2642, + 0x2640, 0x263C, 0x263A, 0x263B, 0x266A, + 0x266C, 0x25AC, 0x2302, 0x203C, 0x221A, + 0x21A8, 0x2310, 0x2319, 0x25D8, 0x25D9, + 0x21B5, 0x2104, 0x261C, 0x2007, 0x2610, + 0x2612, 0x2639, 0x266F, 0x266D, 0x266E, + 0x260E, 0x231A, 0x231B, 0x2701, 0x2702, + 0x2703, 0x2704, 0x260E, 0x2706, 0x2707, + 0x2708, 0x2709, 0x261B, 0x261E, 0x270C, + 0x270D, 0x270E, 0x270F, 0x2710, 0x2711, + 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, + 0x2717, 0x2718, 0x2719, 0x271A, 0x271B, + 0x271C, 0x271D, 0x271E, 0x271F, 0x2720, + 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, + 0x2726, 0x2727, 0x2605, 0x2729, 0x272A, + 0x272B, 0x272C, 0x272D, 0x272E, 0x272F, + 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, + 0x2735, 0x2736, 0x2737, 0x2738, 0x2739, + 0x273A, 0x273B, 0x273C, 0x273D, 0x273E, + 0x273F, 0x2740, 0x2741, 0x2742, 0x2743, + 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, + 0x2749, 0x274A, 0x274B, 0x25CF, 0x274D, + 0x25A0, 0x274F, 0x2750, 0x2751, 0x2752, + 0x25B2, 0x25BC, 0x25C6, 0x2756, 0x25D7, + 0x2758, 0x2759, 0x275A, 0x275B, 0x275C, + 0x275D, 0x275E, 0x2036, 0x2033, 0xF827, + 0xF828, 0xF829, 0xF82A, 0x2329, 0x232A, + 0x005B, 0x005D, 0xF82B, 0xF82C, 0xF82D, + 0xF82E, 0xF82F, 0xF830, 0xF831, 0x2190, + 0xF832, 0xF833, 0xF834, 0xF835, 0xF836, + 0x21E8, 0x21E6, 0x2794, 0xF838, 0xF839, + 0xF83A, 0xF83B, 0xF83C, 0x25D6, 0xF83D, + 0xF83E, 0x2761, 0x2762, 0x2763, 0x2764, + 0x2765, 0x2766, 0x2767, 0x2663, 0x2666, + 0x2665, 0x2660, 0x2780, 0x2781, 0x2782, + 0x2783, 0x2784, 0x2785, 0x2786, 0x2787, + 0x2788, 0x2789, 0x2776, 0x2777, 0x2778, + 0x2779, 0x277A, 0x277B, 0x277C, 0x277D, + 0x277E, 0x277F, 0x2780, 0x2781, 0x2782, + 0x2783, 0x2784, 0x2785, 0x2786, 0x2787, + 0x2788, 0x2789, 0x278A, 0x278B, 0x278C, + 0x278D, 0x278E, 0x278F, 0x2790, 0x2791, + 0x2792, 0x2793, 0x2794, 0x2192, 0x2194, + 0x2195, 0x2798, 0x2799, 0x279A, 0x279B, + 0x279C, 0x279D, 0x279E, 0x279F, 0x27A0, + 0x27A1, 0x27A2, 0x27A3, 0x27A4, 0x27A5, + 0x27A6, 0x27A7, 0x27A8, 0x27A9, 0x27AA, + 0x27AB, 0x27AC, 0x27AD, 0x27AE, 0x27AF, + 0xF83F, 0x27B1, 0x27B2, 0x27B3, 0x27B4, + 0x27B5, 0x27B6, 0x27B7, 0x27B8, 0x27B9, + 0x27BA, 0x27BB, 0x27BC, 0x27BD, 0x27BE +}; + +/**************************************************************************** +Desc: WP51/WP60 to Unicode - Math/Scientific +****************************************************************************/ +FLMUINT16 WPCH_WPUNI6[] = +{ + 0x2212, 0x00B1, 0x2264, 0x2265, 0x221D, + 0x01C0, 0x2215, 0x2216, 0x00F7, 0x2223, + 0x2329, 0x232A, 0x223C, 0x2248, 0x2261, + 0x2208, 0x2229, 0x2225, 0x2211, 0x221E, + 0x00AC, 0x2192, 0x2190, 0x2191, 0x2193, + 0x2194, 0x2195, 0x25B8, 0x25C2, 0x25B4, + 0x25BE, 0x22C5, 0xF850, 0x2218, 0x2219, + 0x212B, 0x00B0, 0x00B5, 0x203E, 0x00D7, + 0x222B, 0x220F, 0x2213, 0x2207, 0x2202, + 0x02B9, 0x02BA, 0x2192, 0x212F, 0x2113, + 0x210F, 0x2111, 0x211C, 0x2118, 0x21C4, + 0x21C6, 0x21D2, 0x21D0, 0x21D1, 0x21D3, + 0x21D4, 0x21D5, 0x2197, 0x2198, 0x2196, + 0x2199, 0x222A, 0x2282, 0x2283, 0x2286, + 0x2287, 0x220D, 0x2205, 0x2308, 0x2309, + 0x230A, 0x230B, 0x226A, 0x226B, 0x2220, + 0x2297, 0x2295, 0x2296, 0xF851, 0x2299, + 0x2227, 0x2228, 0x22BB, 0x22A4, 0x22A5, + 0x2312, 0x22A2, 0x22A3, 0x25A1, 0x25A0, + 0x25CA, 0xF852, 0xF853, 0xF854, 0x2260, + 0x2262, 0x2235, 0x2234, 0x2237, 0x222E, + 0x2112, 0x212D, 0x2128, 0x2118, 0x20DD, + 0xF855, 0x25C7, 0x22C6, 0x2034, 0x2210, + 0x2243, 0x2245, 0x227A, 0x227C, 0x227B, + 0x227D, 0x2203, 0x2200, 0x22D8, 0x22D9, + 0x228E, 0x228A, 0x228B, 0x2293, 0x2294, + 0x228F, 0x2291, 0x22E4, 0x2290, 0x2292, + 0x22E5, 0x25B3, 0x25BD, 0x25C3, 0x25B9, + 0x22C8, 0x2323, 0x2322, 0xF856, 0x219D, + 0x21A9, 0x21AA, 0x21A3, 0x21BC, 0x21BD, + 0x21C0, 0x21C1, 0x21CC, 0x21CB, 0x21BF, + 0x21BE, 0x21C3, 0x21C2, 0x21C9, 0x21C7, + 0x22D3, 0x22D2, 0x22D0, 0x22D1, 0x229A, + 0x229B, 0x229D, 0x2127, 0x2221, 0x2222, + 0x25C3, 0x25B9, 0x25B5, 0x25BF, 0x2214, + 0x2250, 0x2252, 0x2253, 0x224E, 0x224D, + 0x22A8, 0xF857, 0x226C, 0x0285, 0x2605, + 0x226E, 0x2270, 0x226F, 0x2271, 0x2241, + 0x2244, 0x2247, 0x2249, 0x2280, 0x22E0, + 0x2281, 0x22E1, 0x2284, 0x2285, 0x2288, + 0x2289, 0xF858, 0xF859, 0x22E2, 0x22E3, + 0x2226, 0x2224, 0x226D, 0x2204, 0x2209, + 0xF85A, 0x2130, 0x2131, 0x2102, 0xF85B, + 0x2115, 0x211D, 0x225F, 0x221F, 0x220B, + 0x22EF, 0xF85C, 0x22EE, 0x22F1, 0xF85D, + 0x20E1, 0x002B, 0x002D, 0x003D, 0x002A, + 0xF85E, 0xF85F, 0xF860, 0x210C, 0x2118, + 0x2272, 0x2273, 0xF861 +}; + +/**************************************************************************** +Desc: WP51/WP60 to Unicode - Math/Science Extension +****************************************************************************/ +FLMUINT16 WPCH_WPUNI7[] = +{ + 0x2320, 0x2321, 0xF702, 0xF703, 0x221A, + 0xF705, 0xF706, 0xF707, 0xF708, 0xF709, + 0xF70A, 0xF70B, 0xF70C, 0xF70D, 0xF70E, + 0xF70F, 0xF710, 0xF711, 0xF712, 0xF713, + 0xF714, 0xF715, 0xF716, 0xF717, 0xF718, + 0xF719, 0xF71A, 0xF71B, 0xF71C, 0xF71D, + 0xF71E, 0xF71F, 0xF720, 0xF721, 0xF722, + 0xF723, 0xF724, 0xF725, 0xF726, 0xF727, + 0xF728, 0xF729, 0xF72A, 0xF72B, 0xF72C, + 0xF72D, 0xF72E, 0xF72F, 0xF730, 0xF731, + 0xF732, 0xF733, 0xF734, 0xF735, 0xF736, + 0xF737, 0xF738, 0xF739, 0xF73A, 0xF73B, + 0xF73C, 0xF73D, 0xF73E, 0xF73F, 0xF740, + 0xF741, 0xF742, 0xF743, 0xF744, 0xF745, + 0xF746, 0xF747, 0xF748, 0xF749, 0xF74A, + 0xF74B, 0xF74C, 0xF74D, 0xF74E, 0xF74F, + 0xF750, 0xF751, 0xF752, 0xF753, 0xF754, + 0xF755, 0xF756, 0xF757, 0xF758, 0xF759, + 0xF75A, 0xF75B, 0xF75C, 0xF75D, 0xF75E, + 0xF75F, 0xF760, 0xF761, 0xF762, 0xF763, + 0xF764, 0xF765, 0xF766, 0xF767, 0xF768, + 0xF769, 0xF76A, 0xF76B, 0xF76C, 0xF76D, + 0xF76E, 0xF76F, 0xF770, 0xF771, 0xF772, + 0xF773, 0xF774, 0xF775, 0xF776, 0xF777, + 0xF778, 0xF779, 0xF77A, 0xF77B, 0xF77C, + 0xF77D, 0xF77E, 0xF77F, 0xF780, 0xF781, + 0xF782, 0xF783, 0xF784, 0xF785, 0xF786, + 0xF787, 0xF788, 0xF789, 0xF78A, 0xF78B, + 0xF78C, 0xF78D, 0xF78E, 0xF78F, 0xF790, + 0xF791, 0xF792, 0xF793, 0xF794, 0xF795, + 0xF796, 0xF797, 0xF798, 0xF799, 0xF79A, + 0xF79B, 0xF79C, 0xF79D, 0xF79E, 0xF79F, + 0xF7A0, 0xF7A1, 0xF7A2, 0xF7A3, 0xF7A4, + 0xF7A5, 0xF7A6, 0xF7A7, 0xF7A8, 0xF7A9, + 0xF7AA, 0xF7AB, 0xF7AC, 0xF7AD, 0xF7AE, + 0xF7AF, 0xF7B0, 0xF7B1, 0xF7B2, 0xF7B3, + 0xF7B4, 0xF7B5, 0xF7B6, 0xF7B7, 0xF7B8, + 0xF7B9, 0xF7BA, 0xF7BB, 0xF7BC, 0xF7BD, + 0xF7BE, 0xF7BF, 0xF7C0, 0xF7C1, 0xF7C2, + 0xF7C3, 0xF7C4, 0xF7C5, 0xF7C6, 0xF7C7, + 0xF7C8, 0xF7C9, 0xF7CA, 0xF7CB, 0xF7CC, + 0xF7CD, 0xF7CE, 0xF7CF, 0xF7D0, 0xF7D1, + 0xF7D2, 0xF7D3, 0xF7D4, 0xF7D5, 0xF7D6, + 0xF7D7, 0xF7D8, 0xF7D9, 0xF7DA, 0xF7DB, + 0xF7DC, 0xF7DD, 0xF7DE, 0xF7DF, 0xF7E0, + 0xF7E1, 0xF7E2, 0xF7E3, 0xF7E4 +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Greek +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI8[] = +{ + 0x0391, 0x03B1, 0x0392, 0x03B2, 0x0392, + 0x03D0, 0x0393, 0x03B3, 0x0394, 0x03B4, + 0x0395, 0x03B5, 0x0396, 0x03B6, 0x0397, + 0x03B7, 0x0398, 0x03B8, 0x0399, 0x03B9, + 0x039A, 0x03BA, 0x039B, 0x03BB, 0x039C, + 0x03BC, 0x039D, 0x03BD, 0x039E, 0x03BE, + 0x039F, 0x03BF, 0x03A0, 0x03C0, 0x03A1, + 0x03C1, 0x03A3, 0x03C3, 0x03A3, 0x03C2, + 0x03A4, 0x03C4, 0x03A5, 0x03C5, 0x03A6, + 0x03C6, 0x03A7, 0x03C7, 0x03A8, 0x03C8, + 0x03A9, 0x03C9, 0xF106, 0x03AC, 0xF107, + 0x03AD, 0xF108, 0x03AE, 0xF109, 0x03AF, + 0x03AA, 0x03CA, 0xF10A, 0x03CC, 0xF10B, + 0x03CD, 0x03AB, 0x03CB, 0xF10C, 0x03CE, + 0x03B5, 0x03D1, 0x03F0, 0x03D6, 0x03F1, + 0x03DB, 0x03D2, 0x03D5, 0x03D6, 0x03D7, + 0x00B7, 0x0374, 0x0375, 0x0301, 0x0308, + 0xF216, 0xF217, 0x0300, 0x0311, 0x0313, + 0x0314, 0x0345, 0x1FCE, 0x1FDE, 0x1FCD, + 0x1FDD, 0xF200, 0xF201, 0xF022, 0xF021, + 0xF202, 0xF203, 0xF204, 0xF300, 0xF301, + 0xF302, 0xF303, 0xF304, 0xF305, 0x1F70, + 0xF100, 0x1FB3, 0x1FB4, 0x1FB2, 0xF205, + 0x1F00, 0x1F04, 0x1F02, 0xF206, 0x1F80, + 0x1F84, 0x1F82, 0xF306, 0x1F01, 0x1F05, + 0x1F03, 0xF207, 0x1F81, 0x1F85, 0x1F83, + 0xF307, 0x1F72, 0x1F10, 0x1F14, 0x1F12, + 0x1F11, 0x1F15, 0x1F13, 0x1F74, 0xF101, + 0x1FC3, 0x1FC4, 0x1FC2, 0xF208, 0x1F20, + 0x1F24, 0x1F22, 0xF209, 0x1F90, 0x1F94, + 0x1F92, 0xF308, 0x1F21, 0x1F25, 0x1F23, + 0xF20A, 0x1F91, 0x1F95, 0x1F93, 0xF309, + 0x1F76, 0xF102, 0xF20B, 0xF20C, 0x1F30, + 0x1F34, 0x1F32, 0xF20D, 0x1F31, 0x1F35, + 0x1F33, 0xF20E, 0x1F78, 0x1F40, 0x1F44, + 0x1F42, 0x1F41, 0x1F45, 0x1F43, 0x1FE5, + 0x1FE4, 0x1F7A, 0xF103, 0xF20F, 0xF210, + 0x1F50, 0x1F54, 0x1F52, 0xF211, 0x1F51, + 0x1F55, 0x1F53, 0xF212, 0x1F7C, 0xF104, + 0x1FF3, 0x1FF4, 0x1FF2, 0xF213, 0x1F60, + 0x1F64, 0x1F62, 0xF214, 0x1FA0, 0x1FA4, + 0x1FA2, 0xF30A, 0x1F61, 0x1F65, 0x1F63, + 0xF215, 0x1FA1, 0x1FA5, 0x1FA3, 0xF30B, + 0x03DA, 0x03DC, 0x03DE, 0x03E0 +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Hebrew +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI9[] = +{ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, + 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, + 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, + 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, + 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, + 0x05E9, 0x05EA, 0xF862, 0x05C0, 0x05C3, + 0x05F3, 0x05F4, 0x05B0, 0x05B1, 0x05B2, + 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x05B9, 0x05BB, 0x05BC, + 0x05BD, 0x05BF, 0x05B7, 0xFB1E, 0x05F0, + 0x05F1, 0x05F2, 0xF114, 0xF8B0, 0xF863, + 0xF864, 0xF865, 0xF866, 0xF867, 0xF868, + 0xF869, 0xF86A, 0xF86B, 0xF86C, 0xF86D, + 0xF86E, 0xF86F, 0xF870, 0xF871, 0xF872, + 0xF873, 0xF874, 0x05F3, 0x05F3, 0x05F4, + 0xF876, 0xF877, 0xF878, 0xF879, 0xF87A, + 0xF87B, 0xF87C, 0xF87D, 0xF87E, 0xF115, + 0xF116, 0xF87F, 0xF117, 0xF118, 0xF119, + 0xF11A, 0xF11B, 0xF11C, 0xF11D, 0xF11E, + 0xF11F, 0xF120, 0xF121, 0xF122, 0xF123, + 0xF124, 0xF125, 0xF126, 0xF127, 0xF218, + 0xF128, 0xF129, 0xF12A, 0xF12B, 0xF12C, + 0xF12D, 0xF880, 0xF12E, 0xF12F, 0xF130, + 0xF219, 0x05E9, 0xF131, 0xF132, 0xF140, + 0xF141, 0xF142, 0x20AA +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Cyrillic/Georgian +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI10[] = +{ + 0x0410, 0x0430, 0x0411, 0x0431, 0x0412, + 0x0432, 0x0413, 0x0433, 0x0414, 0x0434, + 0x0415, 0x0435, 0x0401, 0x0451, 0x0416, + 0x0436, 0x0417, 0x0437, 0x0418, 0x0438, + 0x0419, 0x0439, 0x041A, 0x043A, 0x041B, + 0x043B, 0x041C, 0x043C, 0x041D, 0x043D, + 0x041E, 0x043E, 0x041F, 0x043F, 0x0420, + 0x0440, 0x0421, 0x0441, 0x0422, 0x0442, + 0x0423, 0x0443, 0x0424, 0x0444, 0x0425, + 0x0445, 0x0426, 0x0446, 0x0427, 0x0447, + 0x0428, 0x0448, 0x0429, 0x0449, 0x042A, + 0x044A, 0x042B, 0x044B, 0x042C, 0x044C, + 0x042D, 0x044D, 0x042E, 0x044E, 0x042F, + 0x044F, 0x04D8, 0x04D9, 0x0403, 0x0453, + 0x0490, 0x0491, 0x0492, 0x0493, 0x0402, + 0x0452, 0x0404, 0x0454, 0x0404, 0x0454, + 0x0496, 0x0497, 0x0405, 0x0455, 0xF159, + 0xF889, 0xF15E, 0xF15F, 0x0406, 0x0456, + 0x0407, 0x0457, 0xF88C, 0xF88D, 0x0408, + 0x0458, 0x040C, 0x045C, 0x049A, 0x049B, + 0xF160, 0xF161, 0x049C, 0x049D, 0x0409, + 0x0459, 0x04A2, 0x04A3, 0x040A, 0x045A, + 0x047A, 0x047B, 0x0460, 0x0461, 0x040B, + 0x045B, 0x040E, 0x045E, 0x04EE, 0x04EF, + 0x04AE, 0x04AF, 0x04B0, 0x04B1, 0x0194, + 0x0263, 0x04B2, 0x04B3, 0xF162, 0xF163, + 0x04BA, 0x04BB, 0x047E, 0x047F, 0x040F, + 0x045F, 0x04B6, 0x04B7, 0x04B8, 0x04B9, + 0xF164, 0xF165, 0x0462, 0x0463, 0x0466, + 0x0467, 0x046A, 0x046B, 0x046E, 0x046F, + 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, + 0x0475, 0xF400, 0xF401, 0xF402, 0xF403, + 0xF404, 0xF405, 0xF406, 0xF407, 0xF408, + 0xF409, 0xF40A, 0xF40B, 0xF40C, 0xF40D, + 0xF40E, 0xF40F, 0xF410, 0xF411, 0xF412, + 0xF413, 0xF414, 0xF415, 0xF416, 0xF417, + 0xF418, 0xF419, 0xF41A, 0xF41B, 0xF41C, + 0xF41D, 0xF41E, 0xF41F, 0xF420, 0xF421, + 0xF422, 0xF423, 0xF424, 0xF425, 0xF426, + 0xF427, 0xF428, 0xF429, 0xF42A, 0xF42B, + 0x0301, 0x0300, 0x0308, 0x0306, 0x0326, + 0x0328, 0x0304, 0xF893, 0x201E, 0x201F, + 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, + 0x10D5, 0x10D6, 0x10F1, 0x10D7, 0x10D8, + 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, + 0x10DD, 0x10DE, 0x10DF, 0x10E0, 0x10E1, + 0x10E2, 0x10E3, 0x10F3, 0x10E4, 0x10E5, + 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, + 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, + 0x10EF, 0x10F0, 0x10F5, 0x10F6, 0xF42C +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Japanese +****************************************************************************/ +FLMUINT16 WPCH_WP60UNI11[] = +{ + 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, + 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, + 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, + 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, + 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, + 0xFF7F, 0xFF80, 0xFF81, 0xFF82, 0xFF83, + 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, + 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, + 0xFF8E, 0xFF8F, 0xFF90, 0xFF91, 0xFF92, + 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, + 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, + 0xFF9D, 0xFF9E, 0xFF9F +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Arabic +****************************************************************************/ +FLMUINT16 WPCH_WPUNI13[] = +{ + 0xF895, 0xF896, 0xF897, 0xF898, 0xF899, + 0xF89A, 0xF89B, 0xF89C, 0xF89D, 0xF89E, + 0x064E, 0x064E, 0x064F, 0x064F, 0x0650, + 0x0650, 0x064B, 0x064C, 0x064C, 0x0650, + 0x0652, 0x0652, 0x0651, 0xF503, 0xF502, + 0xF504, 0xF508, 0xF505, 0xF509, 0xF506, + 0xF50A, 0xF50B, 0xF507, 0xF50C, 0x06E4, + 0x06E4, 0x0674, 0x06D6, 0x060C, 0x061B, + 0x061F, 0x002A, 0x066A, 0x226B, 0x226A, + 0x0029, 0x0028, 0x0661, 0x0662, 0x0663, + 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, + 0x0669, 0x0660, 0x0662, 0x0627, 0x0628, + 0x0628, 0x0628, 0x0628, 0x0628, 0x062A, + 0x062A, 0x062A, 0x062A, 0x062B, 0x062B, + 0x062B, 0x062B, 0x062C, 0x062C, 0x062C, + 0x062C, 0x062D, 0x062D, 0x062D, 0x062D, + 0x062E, 0x062E, 0x062E, 0x062E, 0x062F, + 0x062F, 0x0630, 0x0630, 0x0631, 0x0631, + 0x0632, 0x0632, 0x0633, 0x0633, 0x0633, + 0x0633, 0x0634, 0x0634, 0x0634, 0x0634, + 0x0635, 0x0635, 0x0635, 0x0635, 0x0636, + 0x0636, 0x0636, 0x0636, 0x0637, 0x0637, + 0x0637, 0x0637, 0x0638, 0x0638, 0x0638, + 0x0638, 0x0639, 0x0639, 0x0639, 0x0639, + 0x063A, 0x063A, 0x063A, 0x063A, 0x0641, + 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, + 0x0642, 0x0642, 0x0643, 0x0643, 0x0643, + 0x0643, 0x0644, 0x0644, 0x0644, 0x0644, + 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, + 0x0646, 0x0646, 0x0646, 0x0647, 0x0647, + 0x0647, 0x0647, 0x0629, 0x0629, 0x0648, + 0x0648, 0x064A, 0x064A, 0x064A, 0x064A, + 0x0649, 0x0649, 0x0649, 0x0649, 0x0621, + 0x0623, 0x0623, 0x0625, 0x0625, 0x0624, + 0x0624, 0x0626, 0x0626, 0x0626, 0x0626, + 0xF50D, 0xF50D, 0x0622, 0x0622, 0x0671, + 0x0671, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0640, + 0x0640 +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Arabic Script +****************************************************************************/ +FLMUINT16 WPCH_WPUNI14[] = +{ + 0xF8B6, 0xF8B7, 0xF8B8, 0xF8B9, 0xF8BA, + 0xF8BB, 0xF8BC, 0xF8BD, 0xF8BE, 0xF8BF, + 0xF8C0, 0xF8C1, 0xF8C2, 0xF8C3, 0xF8C4, + 0xF8C5, 0xF8C6, 0xF8C7, 0xF8C8, 0xF8C9, + 0xF8CA, 0xF8CB, 0xF8CC, 0xF8CD, 0xF8CE, + 0xF8CF, 0xF8D0, 0x064E, 0x0652, 0xF8D3, + 0xF8D4, 0xF8D5, 0xF8D6, 0xF8D7, 0xF8D8, + 0xF8D9, 0x0674, 0x064C, 0xF8B2, 0xF8DB, + 0xF8DC, 0x06F4, 0x06F4, 0x06F5, 0x06F6, + 0x06F6, 0x06F7, 0x06F8, 0x067B, 0x067B, + 0x067B, 0x067B, 0x0680, 0x0680, 0x0680, + 0x0680, 0x067E, 0x067E, 0x067E, 0x067E, + 0x0679, 0x0679, 0x0679, 0x0679, 0x067C, + 0x067C, 0x067C, 0x067C, 0x067F, 0x067F, + 0x067F, 0x067F, 0x067D, 0x067D, 0x067D, + 0x067D, 0x067A, 0x067A, 0x067A, 0x067A, + 0x0684, 0x0684, 0x0684, 0x0684, 0x0683, + 0x0683, 0x0683, 0x0683, 0x0686, 0x0686, + 0x0686, 0x0686, 0x0687, 0x0687, 0x0687, + 0x0687, 0x0685, 0x0685, 0x0685, 0x0685, + 0x0681, 0x0681, 0x0681, 0x0681, 0x0688, + 0x0688, 0x0689, 0x0689, 0x068C, 0x068C, + 0x068E, 0x068E, 0x068A, 0x068A, 0x068D, + 0x068D, 0x0693, 0x0693, 0x0691, 0x0691, + 0x0699, 0x0699, 0x0695, 0x0695, 0x0692, + 0x0692, 0x0698, 0x0698, 0x0696, 0x0696, + 0x0696, 0x0696, 0x069A, 0x069A, 0x069A, + 0x069A, 0x06A0, 0x06A0, 0x06A0, 0x06A0, + 0x06A4, 0x06A4, 0x06A4, 0x06A4, 0x06A6, + 0x06A6, 0x06A6, 0x06A6, 0x06A9, 0x06A9, + 0x06A9, 0x06A9, 0x06A9, 0x06A9, 0x06A9, + 0x06A9, 0x06AA, 0x06AA, 0x06AA, 0x06AA, + 0x06AF, 0x06AF, 0x06AF, 0x06AF, 0x06AF, + 0x06AF, 0x06AF, 0x06AF, 0x06AB, 0x06AB, + 0x06AB, 0x06AB, 0x06B1, 0x06B1, 0x06B1, + 0x06B1, 0x06B3, 0x06B3, 0x06B3, 0x06B3, + 0x06B5, 0x06B5, 0x06B5, 0x06B5, 0x0000, + 0x0000, 0x06BA, 0x06BA, 0x06BA, 0x06BA, + 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x06BB, + 0x06BB, 0x06BB, 0x06BB, 0x06C6, 0x06C6, + 0x06CA, 0x06CA, 0x06CA, 0x06CA, 0x0647, + 0x0647, 0x0647, 0x0647, 0x06CE, 0x06CE, + 0x06CE, 0x06CE, 0x06D2, 0x06D2, 0x06D1, + 0x06D1, 0x06D1, 0x06D1, 0x06C0, 0x06C0 +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Multinational 1) +****************************************************************************/ +FLMUINT16 WPCH_CPXTAB1[][5] = +{ + { 0x0044, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0063, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x004C, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x006C, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x004E, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x006E, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0052, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0072, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0053, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0073, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0054, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0074, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0059, 0x0306, 0x0000, 0x0000, 0x0000 }, + { 0x0079, 0x0306, 0x0000, 0x0000, 0x0000 }, + { 0x0059, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0079, 0x0300, 0x0000, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Greek) +****************************************************************************/ +FLMUINT16 WPCH_CPXGREEK[][5] = +{ + { 0x0020, 0x0313, 0x0301, 0x0000, 0x0000 }, + { 0x0020, 0x0314, 0x0301, 0x0000, 0x0000 }, + { 0x0020, 0x0313, 0x0300, 0x0000, 0x0000 }, + { 0x0020, 0x0314, 0x0300, 0x0000, 0x0000 }, + { 0x0020, 0x0313, 0x0302, 0x0000, 0x0000 }, + { 0x0020, 0x0314, 0x0302, 0x0000, 0x0000 }, + { 0x0020, 0x0345, 0x0301, 0x0000, 0x0000 }, + { 0x0020, 0x0345, 0x0300, 0x0000, 0x0000 }, + { 0x0020, 0x0345, 0x0302, 0x0000, 0x0000 }, + { 0x0020, 0x0313, 0x0345, 0x0000, 0x0000 }, + { 0x0020, 0x0314, 0x0345, 0x0000, 0x0000 }, + { 0x0020, 0x0313, 0x0301, 0x0345, 0x0000 }, + { 0x0020, 0x0314, 0x0301, 0x0345, 0x0000 }, + { 0x0020, 0x0313, 0x0300, 0x0345, 0x0000 }, + { 0x0020, 0x0314, 0x0300, 0x0345, 0x0000 }, + { 0x0020, 0x0313, 0x0302, 0x0345, 0x0000 }, + { 0x0020, 0x0302, 0x0345, 0x0314, 0x0000 }, + { 0x03B1, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0302, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0345, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0301, 0x0345, 0x0000, 0x0000 }, + { 0x03B1, 0x0302, 0x0345, 0x0000, 0x0000 }, + { 0x03B1, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03B1, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03B1, 0x0302, 0x0313, 0x0000, 0x0000 }, + { 0x03B1, 0x0345, 0x0313, 0x0000, 0x0000 }, + { 0x03B1, 0x0301, 0x0345, 0x0313, 0x0000 }, + { 0x03B1, 0x0302, 0x0345, 0x0313, 0x0000 }, + { 0x03B1, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03B1, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03B1, 0x0302, 0x0314, 0x0000, 0x0000 }, + { 0x03B1, 0x0345, 0x0314, 0x0000, 0x0000 }, + { 0x03B1, 0x0301, 0x0345, 0x0314, 0x0000 }, + { 0x03B1, 0x0302, 0x0345, 0x0314, 0x0000 }, + { 0x03B5, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03B5, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03B5, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03B5, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03B5, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03B5, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03B5, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03B7, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03B7, 0x0310, 0x0000, 0x0000, 0x0000 }, + { 0x03B7, 0x0345, 0x0000, 0x0000, 0x0000 }, + { 0x03B7, 0x0301, 0x0345, 0x0000, 0x0000 }, + { 0x03B7, 0x0300, 0x0345, 0x0000, 0x0000 }, + { 0x03B7, 0x0302, 0x0345, 0x0000, 0x0000 }, + { 0x03B7, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03B7, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03B7, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03B7, 0x0302, 0x0313, 0x0000, 0x0000 }, + { 0x03B7, 0x0345, 0x0313, 0x0000, 0x0000 }, + { 0x03B7, 0x0301, 0x0345, 0x0313, 0x0000 }, + { 0x03B7, 0x0302, 0x0345, 0x0313, 0x0000 }, + { 0x03B7, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03B7, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03B7, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03B7, 0x0302, 0x0314, 0x0000, 0x0000 }, + { 0x03B7, 0x0345, 0x0314, 0x0000, 0x0000 }, + { 0x03B7, 0x0301, 0x0345, 0x0314, 0x0000 }, + { 0x03B7, 0x0302, 0x0345, 0x0314, 0x0000 }, + { 0x03B9, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03B9, 0x0302, 0x0000, 0x0000, 0x0000 }, + { 0x03B9, 0x0308, 0x0301, 0x0000, 0x0000 }, + { 0x03B9, 0x0308, 0x0300, 0x0000, 0x0000 }, + { 0x03B9, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03B9, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03B9, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03B9, 0x0302, 0x0313, 0x0000, 0x0000 }, + { 0x03B9, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03B9, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03B9, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03B9, 0x0302, 0x0314, 0x0000, 0x0000 }, + { 0x03BF, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03BF, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03BF, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03BF, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03BF, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03BF, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03BF, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03C5, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03C5, 0x0302, 0x0000, 0x0000, 0x0000 }, + { 0x03C5, 0x0308, 0x0301, 0x0000, 0x0000 }, + { 0x03C5, 0x0308, 0x0300, 0x0000, 0x0000 }, + { 0x03C5, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03C5, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03C5, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03C5, 0x0302, 0x0313, 0x0000, 0x0000 }, + { 0x03C5, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03C5, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03C5, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03C5, 0x0302, 0x0314, 0x0000, 0x0000 }, + { 0x03C9, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0302, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0345, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0301, 0x0345, 0x0000, 0x0000 }, + { 0x03C9, 0x0300, 0x0345, 0x0000, 0x0000 }, + { 0x03C9, 0x0302, 0x0345, 0x0000, 0x0000 }, + { 0x03C9, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0301, 0x0313, 0x0000, 0x0000 }, + { 0x03C9, 0x0300, 0x0313, 0x0000, 0x0000 }, + { 0x03C9, 0x0302, 0x0313, 0x0000, 0x0000 }, + { 0x03C9, 0x0345, 0x0313, 0x0000, 0x0000 }, + { 0x03C9, 0x0301, 0x0345, 0x0313, 0x0000 }, + { 0x03C9, 0x0302, 0x0345, 0x0313, 0x0000 }, + { 0x03C9, 0x0314, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0301, 0x0314, 0x0000, 0x0000 }, + { 0x03C9, 0x0300, 0x0314, 0x0000, 0x0000 }, + { 0x03C9, 0x0302, 0x0314, 0x0000, 0x0000 }, + { 0x03C9, 0x0345, 0x0314, 0x0000, 0x0000 }, + { 0x03C9, 0x0301, 0x0345, 0x0314, 0x0000 }, + { 0x03C9, 0x0302, 0x0345, 0x0314, 0x0000 }, + { 0x0391, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0395, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0397, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0399, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x039F, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x03A5, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x03A9, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x03C1, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03B1, 0x0300, 0x0345, 0x0000, 0x0000 }, + { 0x03B1, 0x0300, 0x0313, 0x0345, 0x0000 }, + { 0x03B1, 0x0300, 0x0314, 0x0345, 0x0000 }, + { 0x03B7, 0x0300, 0x0313, 0x0345, 0x0000 }, + { 0x03B7, 0x0300, 0x0314, 0x0345, 0x0000 }, + { 0x03C1, 0x0313, 0x0000, 0x0000, 0x0000 }, + { 0x03C9, 0x0300, 0x0313, 0x0345, 0x0000 }, + { 0x03C9, 0x0300, 0x0314, 0x0345, 0x0000 }, + { 0x0020, 0x0301, 0x0308, 0x0000, 0x0000 }, + { 0x0020, 0x0300, 0x0308, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Hebrew) +****************************************************************************/ +FLMUINT16 WPCH_CPXHEBREW[][5] = { + { 0x05F2, 0x05B7, 0x0000, 0x0000, 0x0000 }, + { 0x05D0, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D1, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D2, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D3, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D4, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D5, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D5, 0x05B9, 0x0000, 0x0000, 0x0000 }, + { 0x05D6, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D7, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D8, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D9, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05D9, 0x05B4, 0x0000, 0x0000, 0x0000 }, + { 0x05DB, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05B0, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05B5, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05B1, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05B7, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05B8, 0x0000, 0x0000, 0x0000 }, + { 0x05DA, 0x05BC, 0x05B8, 0x0000, 0x0000 }, + { 0x05DC, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05DE, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05E0, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05DF, 0x05B8, 0x0000, 0x0000, 0x0000 }, + { 0x05E1, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05E4, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05E6, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05E7, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05E9, 0x05B9, 0x0000, 0x0000, 0x0000 }, + { 0x05E9, 0x05B9, 0x05BC, 0x0000, 0x0000 }, + { 0x05E9, 0x05BC, 0x0000, 0x0000, 0x0000 }, + { 0x05EA, 0x05BC, 0x0000, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Arabic) +****************************************************************************/ +FLMUINT16 WPCH_CPXARABIC[][5] = +{ + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Arabic Script) +****************************************************************************/ +FLMUINT16 WPCH_CPXARABIC2[][5] = +{ + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: WP60 to Unicode - Complex Character Unit Table (Cyrillic / Georgian) +****************************************************************************/ +FLMUINT16 WPCH_CPXCYRILLIC[][5] = { + { 0x0410, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0430, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0415, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0435, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0404, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0454, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0418, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0438, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0406, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0456, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0407, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0457, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x041E, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x043E, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0423, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0443, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x042B, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x044B, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x042D, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x044D, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x042E, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x044E, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x042F, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x044F, 0x0301, 0x0000, 0x0000, 0x0000 }, + { 0x0410, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0430, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0415, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0435, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0401, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0451, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0418, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0438, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x041E, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x043E, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0423, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x0443, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x042B, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x044B, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x042D, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x044D, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x042E, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x044E, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x042F, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x044F, 0x0300, 0x0000, 0x0000, 0x0000 }, + { 0x10E3, 0x0302, 0x0000, 0x0000, 0x0000 }, + { 0x0037, 0x0339, 0x0000, 0x0000, 0x0000 }, + { 0x0428, 0x0329, 0x0000, 0x0000, 0x0000 }, + { 0x0448, 0x0329, 0x0000, 0x0000, 0x0000 }, + { 0x0406, 0x031C, 0x0000, 0x0000, 0x0000 }, + { 0x0446, 0x031C, 0x0000, 0x0000, 0x0000 }, + { 0x0418, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x0438, 0x0304, 0x0000, 0x0000, 0x0000 }, + { 0x041A, 0x0328, 0x0000, 0x0000, 0x0000 }, + { 0x043A, 0x0328, 0x0000, 0x0000, 0x0000 }, + { 0x0425, 0x0328, 0x0000, 0x0000, 0x0000 }, + { 0x0445, 0x0328, 0x0000, 0x0000, 0x0000 }, + { 0x0428, 0x0329, 0x0000, 0x0000, 0x0000 }, + { 0x0448, 0x0329, 0x0000, 0x0000, 0x0000 } +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 * WP60toUni[] = +{ + 0, + WPCH_WP60UNI1, + WPCH_WP60UNI2, + WPCH_WPUNI3, + WPCH_WPUNI4, + WPCH_WP60UNI5, + WPCH_WPUNI6, + WPCH_WPUNI7, + WPCH_WP60UNI8, + WPCH_WP60UNI9, + WPCH_WP60UNI10, + WPCH_WP60UNI11, + 0, + WPCH_WPUNI13, + WPCH_WPUNI14 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 * WP60toCpxUni[] = +{ + 0, + (FLMUINT16 *)WPCH_CPXTAB1, + 0, + 0, + 0, + 0, + 0, + 0, + (FLMUINT16 *)WPCH_CPXGREEK, + (FLMUINT16 *)WPCH_CPXHEBREW, + (FLMUINT16 *)WPCH_CPXCYRILLIC, + (FLMUINT16 *)WPCH_CPXARABIC, + (FLMUINT16 *)WPCH_CPXARABIC2 +}; + +/**************************************************************************** +Desc: Converts a character to upper case (if possible) +****************************************************************************/ +FLMUINT16 flmCh6Upper( + FLMUINT16 ui16WpChar) +{ + if (ui16WpChar < 256) + { + if (ui16WpChar >= ASCII_LOWER_A && ui16WpChar <= ASCII_LOWER_Z) + { + + // Return ASCII upper case + + return (ui16WpChar & 0xdf); + } + } + else + { + FLMBYTE ucCharSet = ui16WpChar >> 8; + + if (ucCharSet == CHSMUL1) + { + FLMBYTE ucChar = ui16WpChar & 0xFF; + + if (ucChar >= flmCaseConvertableRange[(CHSMUL1 - 1) * 2] && + ucChar <= flmCaseConvertableRange[((CHSMUL1 - 1) * 2) + 1]) + { + return (ui16WpChar & 0xFFFE); + } + } + else if (ucCharSet == CHSGREK) + { + if ((ui16WpChar & 0xFF) <= flmCaseConvertableRange[ + ((CHSGREK - 1) * 2) + 1]) + { + return (ui16WpChar & 0xFFFE); + } + } + else if (ucCharSet == CHSCYR) + { + if ((ui16WpChar & 0xFF) <= flmCaseConvertableRange[ + ((CHSCYR - 1) * 2) + 1]) + { + return (ui16WpChar & 0xFFFE); + } + } + else if (ui16WpChar >= Lower_JP_a) + { + + // Possible double byte character set alphabetic character? + + if (ui16WpChar <= Lower_JP_z) + { + + // Japanese? + + ui16WpChar = (ui16WpChar - Lower_JP_a) + Upper_JP_A; + } + else if (ui16WpChar >= Lower_KR_a && ui16WpChar <= Lower_KR_z) + { + + // Korean? + + ui16WpChar = (ui16WpChar - Lower_KR_a) + Upper_KR_A; + } + else if (ui16WpChar >= Lower_CS_a && ui16WpChar <= Lower_CS_z) + { + + // Chinese Simplified? + + ui16WpChar = (ui16WpChar - Lower_CS_a) + Upper_CS_A; + } + else if (ui16WpChar >= Lower_CT_a && ui16WpChar <= Lower_CT_z) + { + + // Chinese Traditional? + + ui16WpChar = (ui16WpChar - Lower_CT_a) + Upper_CT_A; + } + } + } + + // Return original character - original not in lower case. + + return (ui16WpChar); +} + +/**************************************************************************** +Desc: Checks to see if WP character is upper case +****************************************************************************/ +FLMBOOL flmIsUpper( + FLMUINT16 ui16WpChar) +{ + FLMBYTE ucChar; + FLMBYTE ucCharSet; + + // Get character + + ucChar = (FLMBYTE) (ui16WpChar & 0xFF); + + // Test if ASCII character set + + if (!(ui16WpChar & 0xFF00)) + { + return ((ucChar >= ASCII_LOWER_A && ucChar <= ASCII_LOWER_Z) + ? FALSE + : TRUE); + } + + // Get the character set + + ucCharSet = (FLMBYTE) (ui16WpChar >> 8); + + // CHSMUL1 == Multinational 1 character set + // CHSGREK == Greek character set + // CHSCYR == Cyrillic character set + + if ((ucCharSet == CHSMUL1 && ucChar >= 26 && ucChar <= 241) || + (ucCharSet == CHSGREK && ucChar <= 69) || + (ucCharSet == CHSCYR && ucChar <= 199)) + { + return ((ucChar & 1) ? FALSE : TRUE); + } + + // Don't care that double ss is lower + + return (TRUE); +} + +/**************************************************************************** +Desc: Converts a character to lower case (if possible) +****************************************************************************/ +FLMUINT16 flmCh6Lower( + FLMUINT16 ui16WpChar) +{ + if (ui16WpChar < 256) + { + if (ui16WpChar >= ASCII_UPPER_A && ui16WpChar <= ASCII_UPPER_Z) + { + return (ui16WpChar | 0x20); + } + } + else + { + FLMBYTE ucCharSet = ui16WpChar >> 8; + + if (ucCharSet == CHSMUL1) + { + FLMBYTE ucChar = ui16WpChar & 0xFF; + + if (ucChar >= flmCaseConvertableRange[(CHSMUL1 - 1) * 2] && + ucChar <= flmCaseConvertableRange[((CHSMUL1 - 1) * 2) + 1]) + { + return (ui16WpChar | 1); + } + } + else if (ucCharSet == CHSGREK) + { + if ((ui16WpChar & 0xFF) <= flmCaseConvertableRange[ + ((CHSGREK - 1) * 2) + 1]) + { + return (ui16WpChar | 1); + } + } + else if (ucCharSet == CHSCYR) + { + if ((ui16WpChar & 0xFF) <= flmCaseConvertableRange[ + ((CHSCYR - 1) * 2) + 1]) + { + return (ui16WpChar | 1); + } + } + else if (ui16WpChar >= Upper_JP_A) + { + + // Possible double byte character set alphabetic character? + + if (ui16WpChar <= Upper_JP_Z) + { + + // Japanese? + + ui16WpChar = ui16WpChar - Upper_JP_A + Lower_JP_a; + } + else if (ui16WpChar >= Upper_KR_A && ui16WpChar <= Upper_KR_Z) + { + + // Korean? + + ui16WpChar = ui16WpChar - Upper_KR_A + Lower_KR_a; + } + else if (ui16WpChar >= Upper_CS_A && ui16WpChar <= Upper_CS_Z) + { + + // Chinese Simplified? + + ui16WpChar = ui16WpChar - Upper_CS_A + Lower_CS_a; + } + else if (ui16WpChar >= Upper_CT_A && ui16WpChar <= Upper_CT_Z) + { + + // Chinese Traditional? + + ui16WpChar = ui16WpChar - Upper_CT_A + Lower_CT_a; + } + } + } + + // Return original character, original not in upper case + + return (ui16WpChar); +} + +/**************************************************************************** +Desc: Break a WP character into a base and a diacritical char. +Ret: TRUE - if not found FALSE - if found +****************************************************************************/ +FLMBOOL flmCh6Brkcar( + FLMUINT16 ui16WpChar, + FLMUINT16 * pui16BaseChar, + FLMUINT16 * pui16DiacriticChar) +{ + BASE_DIACRIT * pBaseDiacritic; + FLMINT iTableIndex; + + if ((pBaseDiacritic = flm_car60_c[HI( ui16WpChar)]) == 0) + { + return (TRUE); + } + + iTableIndex = ((FLMBYTE) ui16WpChar) - pBaseDiacritic->start_char; + + if (iTableIndex < 0 || + iTableIndex > pBaseDiacritic->char_count || + pBaseDiacritic->table[iTableIndex].base == (FLMBYTE) 0xFF) + { + return (TRUE); + } + + if ((HI( ui16WpChar) != CHSMUL1) || + ((flm_ml1_cb60[((FLMBYTE) ui16WpChar) >> 3] >> + (7 - (ui16WpChar & 0x07))) & 0x01)) + { + + // normal case, same base as same as characters + + *pui16BaseChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table[iTableIndex].base; + + *pui16DiacriticChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table[iTableIndex].diacrit; + } + else + { + + // Multi-national where base is ascii value. + + *pui16BaseChar = pBaseDiacritic->table[iTableIndex].base; + *pui16DiacriticChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table[iTableIndex].diacrit; + } + + return (FALSE); +} + +/**************************************************************************** +Desc: Take a base and a diacritic and compose a WP character. +Ret: TRUE - if not found FALSE - if found +****************************************************************************/ +FLMBOOL flmCh6Cmbcar( + FLMUINT16 * pui16WpChar, + FLMUINT16 ui16BaseChar, + FLMINT16 ui16DiacriticChar) +{ + FLMUINT uiRemaining; + FLMBYTE ucCharSet; + FLMBYTE ucChar; + BASE_DIACRIT * pBaseDiacritic; + BASE_DIACRIT_TABLE * pTable; + + ucCharSet = HI( ui16BaseChar); + if (ucCharSet > NCHSETS) + { + return (TRUE); + } + + // Is base ASCII? If so, look in multinational 1 + + if (!ucCharSet) + { + ucCharSet = CHSMUL1; + } + + if ((pBaseDiacritic = flm_car60_c[ucCharSet]) == 0) + { + return (TRUE); + } + + ucChar = LO( ui16BaseChar); + ui16DiacriticChar = LO( ui16DiacriticChar); + pTable = pBaseDiacritic->table; + for (uiRemaining = pBaseDiacritic->char_count; + uiRemaining; + uiRemaining--, pTable++) + { + + // Same base? + + if (pTable->base == ucChar && + (pTable->diacrit & 0x7F) == ui16DiacriticChar) + { + + // Same diacritic? + + *pui16WpChar = (FLMUINT16) (((FLMUINT16) ucCharSet << 8) + + (pBaseDiacritic->start_char + + (FLMUINT16) (pTable - pBaseDiacritic->table))); + return (FALSE); + } + } + + return (TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT flmCharTypeAnsi7( + FLMUINT16 ui16Char) +{ + if ((ui16Char >= ASCII_LOWER_A && ui16Char <= ASCII_LOWER_Z) || + (ui16Char >= ASCII_UPPER_A && ui16Char <= ASCII_UPPER_Z) || + (ui16Char >= ASCII_ZERO && ui16Char <= ASCII_NINE)) + { + return (SDWD_CHR); + } + + if (ui16Char == 0x27) + { + return (WDJN_CHR); + } + + if (ui16Char <= 0x2B) + { + return (DELI_CHR); + } + + if (ui16Char == ASCII_COMMA || + ui16Char == ASCII_DASH || + ui16Char == ASCII_DOT || + ui16Char == ASCII_SLASH || + ui16Char == ASCII_COLON || + ui16Char == ASCII_AT || + ui16Char == ASCII_BACKSLASH || + ui16Char == ASCII_UNDERSCORE) + { + return (WDJN_CHR); + } + + return (DELI_CHR); +} + +/**************************************************************************** +Desc: Compare two unicode strings. This comparison uses the collation + rules that are defined for the specified language. +****************************************************************************/ +FLMEXP FLMINT FLMAPI FlmStrCmp( + FLMUINT uiCompFlags, + FLMUINT byLang, + const FLMUNICODE * uzStr1, + const FLMUNICODE * uzStr2) +{ + RCODE rc; + FLMINT iCmp; + POOL Pool; + NODE * pNd1; + NODE * pNd2; + + GedPoolInit( &Pool, 256); + + if ((pNd1 = GedNodeMake( &Pool, 1, &rc)) == NULL || + (pNd2 = GedNodeMake( &Pool, 1, &rc)) == NULL) + { + flmAssert( 0); + iCmp = 1; + goto Exit; + } + + if (RC_BAD( rc = GedPutUNICODE( &Pool, pNd1, uzStr1))) + { + flmAssert( RC_OK( rc)); + iCmp = 1; + goto Exit; + } + + if (RC_BAD( rc = GedPutUNICODE( &Pool, pNd2, uzStr2))) + { + flmAssert( RC_OK( rc)); + iCmp = -1; + goto Exit; + } + + // Handle null string cases. + + if (GedValLen( pNd1) == 0) + { + iCmp = 1; + goto Exit; + } + else if (GedValLen( pNd2) == 0) + { + iCmp = -1; + goto Exit; + } + + // VISIT: need to add support for the IGNORE_DASH and IGNORE_SPACE + // options. + + iCmp = flmTextCompare( (FLMBYTE*) GedValPtr( pNd1), GedValLen( pNd1), + (FLMBYTE*) GedValPtr( pNd2), GedValLen( pNd2), + uiCompFlags, byLang); + +Exit: + + GedPoolFree( &Pool); + return (iCmp); +} + +/**************************************************************************** +Desc: Return the next WP or unicode character value and parsing type. +****************************************************************************/ +FLMUINT flmTextGetCharType( + const FLMBYTE * pText, + FLMUINT uiLen, + FLMUINT16 * pui16WPValue, + FLMUNICODE * puzUniValue, + FLMUINT * pType) +{ + FLMUINT uiReturnLen; + FLMUINT16 wpValue; + FLMUNICODE uniValue; + FLMUINT uiCharSet; + + uiReturnLen = flmTextGetValue( pText, uiLen, NULL, FLM_MIN_SPACES, + pui16WPValue, puzUniValue); + wpValue = *pui16WPValue; + uniValue = *puzUniValue; + + if (wpValue) + { + if (wpValue < 0x080) + { + *pType = flmCharTypeAnsi7( wpValue); + goto Exit; + } + + uiCharSet = (FLMUINT) (wpValue >> 8); + + if (uiCharSet == 1 || + uiCharSet == 2 || + (uiCharSet >= 8 && uiCharSet <= 11)) + { + *pType = SDWD_CHR; + goto Exit; + } + + *pType = DELI_CHR; + } + else + { + + // For now all unicode is a delimeter + + *pType = DELI_CHR; + } + +Exit: + + return (uiReturnLen); +} + +/**************************************************************************** +Desc: Return the next WP or unicode character value +Return: Number of bytes formatted to return the character value +****************************************************************************/ +FLMUINT flmTextGetValue( + const FLMBYTE * pText, // [in] Points to current value. + FLMUINT uiLen, // [in] Bytes remaining in text. + FLMUINT * puiWpChar2, // Was there a double character? + FLMUINT uiFlags, // [in] + FLMUINT16 * pui16WPValue, // [out] WP Character value or 0 if unicode. + FLMUNICODE * puzUniValue) // [out] Unicode or OEM value if *pui16WPChar is zero. +{ + FLMUINT uiReturnLength = 0; + FLMUINT uiObjectLength; + FLMUINT16 ui16CurValue; + FLMUNICODE uzUniValue; + + uiReturnLength = 0; + ui16CurValue = 0; + uzUniValue = 0; + + if (puiWpChar2 && *puiWpChar2) + { + ui16CurValue = (FLMUINT16) (*puiWpChar2); + *puiWpChar2 = 0; + uiObjectLength = 0; + goto Check_White_Space; + } + + while (uiLen && !ui16CurValue && !uzUniValue) + { + ui16CurValue = (FLMUINT16) * pText; + + switch (flmTextObjType( ui16CurValue)) + { + case ASCII_CHAR_CODE: // 0nnnnnnn + { + uiObjectLength = 1; + +Check_White_Space: + + // Do all of the bIgnore* stuff here. WHITE SPACE CODE doesn't + // apply. + + if (ui16CurValue == (FLMUINT16) ASCII_UNDERSCORE && + (uiFlags & FLM_NO_UNDERSCORE)) + { + ui16CurValue = (FLMUINT16) ASCII_SPACE; + } + + if (ui16CurValue == (FLMUINT16) ASCII_SPACE) + { + if (uiFlags & FLM_NO_SPACE) + { + ui16CurValue = 0; + } + else if (uiFlags & FLM_MIN_SPACES) + { + + // Eat up the remaining spaces and underscores (if + // NO_UNDERSCORES). + + while ((pText[uiObjectLength] == ASCII_SPACE || + (pText[uiObjectLength] == ASCII_UNDERSCORE && + (uiFlags & FLM_NO_UNDERSCORE))) && + uiObjectLength < uiLen) + { + uiObjectLength++; + } + } + } + else if (ui16CurValue == ASCII_DASH && (uiFlags & FLM_NO_DASH)) + { + ui16CurValue = 0; + } + + break; + } + + case CHAR_SET_CODE: // 10nnnnnn - Character Set | Char + { + uiObjectLength = 2; + ui16CurValue = (FLMUINT16) (((FLMUINT16) (ui16CurValue & + (~CHAR_SET_MASK)) << 8) + + (FLMUINT16) *(pText + 1)); + + break; + } + + case WHITE_SPACE_CODE: // 110nnnnn + { + FLMBYTE ucTmpByte; + + uiObjectLength = 1; + ucTmpByte = *pText & (~WHITE_SPACE_MASK); + + ui16CurValue = ((ucTmpByte == HARD_HYPHEN) || + (ucTmpByte == HARD_HYPHEN_EOL) || + (ucTmpByte == HARD_HYPHEN_EOP)) + ? (FLMUINT16) 0x2D + : (FLMUINT16) 0x20; + break; + } + + case EXT_CHAR_CODE: // Full extended character + { + uiObjectLength = 3; + ui16CurValue = (FLMUINT16) (((FLMUINT16) *(pText + 1) << 8) + + (FLMUINT16) *(pText + 2)); + break; + } + + case UNICODE_CODE: // Unconvertable UNICODE code + { + uiObjectLength = 3; + ui16CurValue = 0; + uzUniValue = (FLMUINT16) (((FLMUINT16) *(pText + 1) << 8) + + (FLMUINT16) *(pText + 2)); + break; + } + + case OEM_CODE: + { + uiObjectLength = 2; // OEM characters are always >= 128 + + // Make this a unicode character + + ui16CurValue = 0; + uzUniValue = (FLMUINT16) * (pText + 1); + break; + } + + // Skip all of the unknown stuff + + case UNK_GT_255_CODE: + { + uiObjectLength = (FLMUINT16) (1 + sizeof(FLMUINT16) + + FB2UW( pText + 1)); + break; + } + + case UNK_LE_255_CODE: + { + uiObjectLength = 2 + (FLMUINT16) * (pText + 1); + break; + } + + case UNK_EQ_1_CODE: + { + uiObjectLength = 2; + break; + } + + default: + { + // Coded to skip remaining data + + ui16CurValue = 0; + uiObjectLength = uiLen; + break; + } + } + + uiReturnLength += uiObjectLength; + pText += uiObjectLength; + uiLen -= uiObjectLength; + } + + *pui16WPValue = ui16CurValue; + *puzUniValue = uzUniValue; + + return (uiReturnLength); +} + +/**************************************************************************** +Desc: Returns the size of buffer needed to hold the unicode string in + FLAIM's storage format. +****************************************************************************/ +FLMEXP FLMUINT FLMAPI FlmGetUnicodeStorageLength( + const FLMUNICODE * puzStr) +{ + FLMBYTE chrSet; + FLMUINT uiStorageLength = 0; + FLMUINT uniLength; + FLMUINT16 wp60Buf[12]; + + flmAssert( puzStr != NULL); + + // Two passes are needed to store a UNICODE string: 1st pass + // determines the storage length (via FlmGetUnicodeStorageLength) 2nd + // pass stores the string into FLAIMs internal text format (via + // FlmUnicode2Storage). + + do + { + if (*puzStr < 0x20) + { + uniLength = 1; + uiStorageLength += 3; + } + else + { + if (*puzStr < 0x7F) + { + uiStorageLength++; + puzStr++; + continue; + } + + uniLength = flmUnicodeToWP( puzStr, wp60Buf); + + if (!uniLength) + { + uiStorageLength += 3; + uniLength = 1; + } + else + { + if ((chrSet = (FLMBYTE) (wp60Buf[0] >> 8)) == 0) + { + uiStorageLength++; + } + else + { + uiStorageLength += (chrSet <= 63) ? 2 : 3; + } + } + } + + puzStr += uniLength; + } while (*puzStr != 0); + + return (uiStorageLength); +} + +/**************************************************************************** +Desc: Copies and formats a Unicode string into FLAIM's storage format. + The Unicode string must be in little endian format. + Unicode values that are not represented as WordPerfect 6.x + characters are preserved as non-WP characters. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmUnicode2Storage( + const FLMUNICODE * puzStr, + FLMUINT * puiBufLength, + FLMBYTE * pBuf) +{ + FLMBYTE chrSet; + FLMUINT16 wp60Buf[12]; + FLMUINT uiStorageLength = 0; + FLMUINT uniLength; + + flmAssert( puzStr != NULL); + flmAssert( pBuf != NULL); + + do + { + if (*puzStr < 0x20) + { + + // Output the character as an unconvertable unicode character. + + *pBuf++ = UNICODE_CODE; + *pBuf++ = *puzStr >> 8; + *pBuf++ = (FLMBYTE) *puzStr; + uniLength = 1; + uiStorageLength += 3; + } + else + { + if (*puzStr < 0x7F) + { + uiStorageLength++; + *pBuf++ = (FLMBYTE) *puzStr++; + continue; + } + + uniLength = flmUnicodeToWP( puzStr, wp60Buf); + + if (!uniLength) + { + *pBuf++ = UNICODE_CODE; + *pBuf++ = *puzStr >> 8; + *pBuf++ = (FLMBYTE) *puzStr; + uniLength = 1; + uiStorageLength += 3; + } + else + { + chrSet = wp60Buf[0] >> 8; + + if (chrSet == 0) + { + *pBuf++ = (FLMBYTE) wp60Buf[0]; + uiStorageLength++; + } + else if (chrSet <= 63) + { + *pBuf++ = CHAR_SET_CODE | chrSet; + *pBuf++ = (FLMBYTE) wp60Buf[0]; + uiStorageLength += 2; + } + else + { + *pBuf++ = EXT_CHAR_CODE; + *pBuf++ = chrSet; + *pBuf++ = (FLMBYTE) wp60Buf[0]; + uiStorageLength += 3; + } + } + } + + puzStr += uniLength; + + // Make sure input buffer was large enough + + if (*puiBufLength < uiStorageLength) + { + return (RC_SET( FERR_CONV_DEST_OVERFLOW)); + } + } while (*puzStr != 0); + + *puiBufLength = uiStorageLength; + return (FERR_OK); +} + +/**************************************************************************** +Desc: Convert from Unicode to 1 and only 1 WP60 character +Ret: Conversion count - 0 means Unicode character could not be converted +****************************************************************************/ +FLMUINT flmUnicodeToWP( + const FLMUNICODE * pUniStr, + FLMUINT16 * pWPChr) +{ + FLMUINT uiReturnLen = 1; + FLMUNICODE uzUniChar = *pUniStr; + FLMINT16 max; + FLMINT16 min; + FLMINT16 temp; + FLMUINT16 * tablePtr; + FLMUINT16 tblChr; + + if (uzUniChar < 127) + { + *pWPChr = uzUniChar; + goto Exit; + } + + tablePtr = (FLMUINT16 *) WP_UTOWP60; + + // Value we should use ... max = UTOWP60_ENTRIES - 1; + // Bug introduced before Nov99 where UTOWP60_ENTRIES is actually 1502 + // and the value of 2042 was used. Through debugging, all values in the + // table from 1021 to 1502 were never converted to WP character. So, in + // order to search correctly on these values we must preserve the WRONG + // conversion of these characters (Unicode x222E on). The new max table + // size is 1021 so max will be set to 1020 to work correctly. + + max = 1020; + min = 0; + + do + { + temp = (min + max) >> 1; + tblChr = *(tablePtr + (temp * 2)); + + if (tblChr < uzUniChar) + { + min = temp + 1; + } + else if (tblChr > uzUniChar) + { + max = temp - 1; + } + else + { + *pWPChr = *(tablePtr + (temp * 2) + 1); + goto Exit; + } + } while (min <= max); + + uiReturnLen = 0; + +Exit: + + return (uiReturnLen); +} + +/**************************************************************************** +Desc: Converts storage formats to UNICODE. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmStorage2Unicode( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + FLMUINT * puiStrBufLen, + FLMUNICODE * puzStrBuf) +{ + FLMUNICODE * tablePtr; + FLMBYTE c; + FLMUINT bytesProcessed = 0; + FLMUINT bytesOutput = 0; + FLMUINT outputData; + FLMUINT maxOutLen; + FLMBYTE objType; + FLMUINT objLength = 0; + FLMBYTE tempBuf[80]; + FLMBYTE chrSet; + FLMBYTE chrVal; + FLMUNICODE newChrVal; + RCODE rc = FERR_OK; + + // If the value is a number, convert to text first + + if (uiValueType != FLM_TEXT_TYPE) + { + if (pucValue == NULL) + { + uiValueLength = 0; + } + else + { + if (uiValueType == FLM_NUMBER_TYPE) + { + uiValueLength = sizeof(tempBuf); + rc = GedNumToText( pucValue, tempBuf, &uiValueLength); + } + else + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + pucValue = &tempBuf[0]; + } + } + + maxOutLen = *puiStrBufLen; + outputData = ((puzStrBuf != NULL) && (maxOutLen > 1)); + + if (outputData) + { + maxOutLen -= 2; + } + + // Parse through the string outputting data to the buffer as we go + + while (bytesProcessed < uiValueLength) + { + + // Determine what we are pointing at + + c = *pucValue; + objType = flmTextObjType( c); + + switch (objType) + { + case ASCII_CHAR_CODE: + { + objLength = 1; + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = c; + } + + bytesOutput += 2; + break; + } + + case CHAR_SET_CODE: + { + objLength = 2; + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + // Convert WP to UNICODE + + chrSet = c & 0x3F; + chrVal = *(pucValue + 1); + + goto ConvertWPToUni; + } + + bytesOutput += 2; + break; + } + + case WHITE_SPACE_CODE: + { + objLength = 1; + + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + if (c == (WHITE_SPACE_CODE | NATIVE_TAB)) + { + *puzStrBuf = (FLMUNICODE) 9; + } + else if (c == (WHITE_SPACE_CODE | NATIVE_LINEFEED)) + { + *puzStrBuf = (FLMUNICODE) 10; + } + else if (c == (WHITE_SPACE_CODE | HARD_RETURN)) + { + *puzStrBuf = (FLMUNICODE) 13; + } + else + { + *puzStrBuf = (FLMUNICODE) 0x20; + } + + puzStrBuf++; + } + + bytesOutput += 2; + break; + } + + case EXT_CHAR_CODE: + { + objLength = 3; + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + // Convert back from WP to UNICODE + + chrSet = *(pucValue + 1); + chrVal = *(pucValue + 2); + +ConvertWPToUni: + + if ((chrSet < WP60toUni_MAX) && + ((tablePtr = WP60toUni[chrSet]) != 0)) + { + FLMUNICODE * pCpxUniStr; + + newChrVal = tablePtr[chrVal]; + + if ((newChrVal & WPCH_LOMASK) == 0xF000) + { + + // Does character convert to many Unicode chars? + + pCpxUniStr = WP60toCpxUni[chrSet]; + pCpxUniStr += (newChrVal & WPCH_HIMASK) * WPCH_MAX_COMPLEX; + + while (*pCpxUniStr) + { + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = *pCpxUniStr++; + } + + bytesOutput += 2; + } + } + else + { + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = newChrVal; + } + + bytesOutput += 2; + } + } + else + { + + // Big extended WP char + + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = 0x03; + } + + bytesOutput += 2; + } + } + break; + } + + case OEM_CODE: + { + + // We always just skip OEM codes + + objLength = 2; + break; + } + + case UNICODE_CODE: + { + objLength = 3; + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = (*(pucValue + 1) << 8) + *(pucValue + 2); + } + + bytesOutput += 2; + break; + } + + case UNK_EQ_1_CODE: + { + objLength = 2; + if (outputData) + { + if ((maxOutLen < 2) || (bytesOutput > maxOutLen - 2)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto GedGetUNICODE_Output; + } + + *puzStrBuf++ = *(pucValue + 1); + } + + bytesOutput += 2; + break; + } + + default: + { + flmAssert( 0); + bytesProcessed = uiValueLength; + break; + } + } + + pucValue += objLength; + bytesProcessed += objLength; + } + + // Add TWO terminating NULL characters, but DO NOT increment the + // bytesOutput counter. + +GedGetUNICODE_Output: + + if (outputData) + { + *puzStrBuf = 0; + } + + *puiStrBufLen = bytesOutput; + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Returns the size of buffer needed to hold the native string in + FLAIM's storage format. +****************************************************************************/ +FLMEXP FLMUINT FLMAPI FlmGetNativeStorageLength( + const char * pszStr) +{ + RCODE rc; + FLMUINT uiStorageLength; + + rc = FlmNative2Storage( pszStr, 0, &uiStorageLength, NULL); + flmAssert( rc == FERR_OK); + + return (uiStorageLength); +} + +/**************************************************************************** +Desc: Copies and formats a native 8-bit null terminated string into a + caller supplied buffer, It converts the string into an internal FLAIM + TEXT string. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmNative2Storage( + const char * pszNativeString, + FLMUINT uiStringLength, + FLMUINT * puiStorageLen, + FLMBYTE * pStorageBuffer) +{ + FLMBOOL bGetLengthPass; + const char * pCurChar; + const char * pEnd = NULL; + FLMBYTE ucAsciiChar; + FLMUINT uiStorageLen = 0; + + // Are we determining the needed length or converting the data + + bGetLengthPass = pStorageBuffer ? FALSE : TRUE; + + if (uiStringLength) + { + pEnd = &pszNativeString[uiStringLength]; + } + + // Parse through the string + + pCurChar = pszNativeString; + + for (;;) + { + if (*pCurChar == 0 || (pEnd && pCurChar >= pEnd)) + { + + // We have reached end of the string, return the storage length + + *puiStorageLen = uiStorageLen; + return (FERR_OK); + } + + // Put the character in a local variable for speed + + ucAsciiChar = f_toascii( *pCurChar); + + if (ucAsciiChar < ASCII_SPACE) + { + + // If it is a tab, carriage return, or linefeed, output a + // whitespace code for indexing and each word purposes + + if (ucAsciiChar == ASCII_TAB) + { + if (bGetLengthPass) + { + uiStorageLen++; + } + else + { + *pStorageBuffer++ = WHITE_SPACE_CODE | NATIVE_TAB; + } + } + else if (ucAsciiChar == ASCII_NEWLINE) + { + if (bGetLengthPass) + { + uiStorageLen++; + } + else + { + *pStorageBuffer++ = WHITE_SPACE_CODE | NATIVE_LINEFEED; + } + } + else if (ucAsciiChar == ASCII_CR) + { + if (bGetLengthPass) + { + uiStorageLen++; + } + else + { + *pStorageBuffer++ = WHITE_SPACE_CODE | HARD_RETURN; + } + } + else + { + if (bGetLengthPass) + { + uiStorageLen += 2; + } + else + { + + // Output the character as an unknown byte if no WP char + // found + + *pStorageBuffer++ = UNK_EQ_1_CODE | NATIVE_TYPE; + *pStorageBuffer++ = ucAsciiChar; + } + } + } + else if (ucAsciiChar < 127) + { + + // For now assume < 127 means character set zero. Value 127 is + // very sacred in WP land and is really an extended character + + if (bGetLengthPass) + { + uiStorageLen++; + } + else + { + *pStorageBuffer++ = ucAsciiChar; + } + } + else + { + if (bGetLengthPass) + { + uiStorageLen += 2; + } + else + { + *pStorageBuffer++ = OEM_CODE; + *pStorageBuffer++ = ucAsciiChar; + } + } + + pCurChar++; + } +} + +/**************************************************************************** +Desc: Convert a storage text string into a native string +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmStorage2Native( + FLMUINT uiValueType, + FLMUINT uiValueLength, + const FLMBYTE * pucValue, + FLMUINT * puiOutBufLenRV, + char * pOutBuffer) +{ + RCODE rc = FERR_OK; + const FLMBYTE * ptr = pucValue; + char * outPtr; + FLMBYTE c; + FLMUINT bytesProcessed; + FLMUINT bytesOutput; + FLMUINT valLength = uiValueLength; + FLMUINT outputData; + FLMUINT maxOutLen = 0; + FLMBYTE objType; + FLMUINT objLength = 0; + FLMBYTE TempBuf[80]; + FLMUINT length; + + // If the input is not a TEXT or a NUMBER node, return an error for now + + if ((uiValueType == FLM_BINARY_TYPE) || (uiValueType == FLM_CONTEXT_TYPE)) + { + rc = RC_SET( FERR_CONV_ILLEGAL); + goto Exit; + } + + // If the node is a number, convert to text first + + if (uiValueType != FLM_TEXT_TYPE) + { + if (ptr == NULL) + { + valLength = 0; + } + else + { + valLength = sizeof(TempBuf); + flmAssert( uiValueType == FLM_NUMBER_TYPE); + if (RC_BAD( rc = GedNumToText( ptr, TempBuf, &valLength))) + { + goto Exit; + } + + ptr = &TempBuf[0]; + } + } + + outputData = ((pOutBuffer != NULL) && (*puiOutBufLenRV)); + if (outputData) + { + maxOutLen = *puiOutBufLenRV - 1; + } + + bytesProcessed = 0; + bytesOutput = 0; + outPtr = pOutBuffer; + + while (bytesProcessed < valLength) + { + c = *ptr; + objType = (FLMBYTE) flmTextObjType( c); + + switch (objType) + { + case ASCII_CHAR_CODE: + { + objLength = 1; + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = f_tonative( c); + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + case CHAR_SET_CODE: + { + objLength = 2; + if (outputData) + { + if (bytesOutput < maxOutLen) + { + if ((c & (~objType)) == 0) + { + *outPtr++ = f_tonative( *(ptr + 1)); + } + else + { + *outPtr++ = NON_DISPLAYABLE_CHAR; + } + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + case WHITE_SPACE_CODE: + { + objLength = 1; + + // ALWAYS OUTPUT A SPACE WHEN WE SEE A WHITE_SPACE_CODE ; + // UNLESS IT IS A HYPHEN + + if (outputData) + { + if (bytesOutput < maxOutLen) + { + c &= (~WHITE_SPACE_MASK); + if ((c == HARD_HYPHEN) || + (c == HARD_HYPHEN_EOL) || + (c == HARD_HYPHEN_EOP)) + { + c = ASCII_DASH; + } + else if (c == NATIVE_TAB) + { + c = ASCII_TAB; + } + else if (c == NATIVE_LINEFEED) + { + c = ASCII_NEWLINE; + } + else if (c == HARD_RETURN) + { + c = ASCII_CR; + } + else + { + c = ASCII_SPACE; + } + + *outPtr++ = f_tonative( c); + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + case UNK_GT_255_CODE: + case UNK_LE_255_CODE: + { + if (objType == UNK_GT_255_CODE) + { + length = FB2UW( ptr + 1); + objLength = (FLMUINT16) (1 + sizeof(FLMUINT16) + length); + } + else + { + length = (FLMUINT16) * (ptr + 1); + objLength = (FLMUINT16) (2 + length); + } + + // Skip it if it is not a NATIVE code + + if ((c & (~objType)) == NATIVE_TYPE) + { + if (outputData) + { + if ((maxOutLen < length) || (bytesOutput > maxOutLen - length)) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + + if (objType == UNK_LE_255_CODE) + { + f_memcpy( outPtr, ptr + 2, length); + } + else + { + f_memcpy( outPtr, ptr + 1 + sizeof(FLMUINT16), length); + } + + outPtr += length; + } + + bytesOutput += length; + } + + break; + } + + case UNK_EQ_1_CODE: + { + objLength = 2; + + // Skip it if it is not a NATIVE code + + if ((c & (~objType)) == NATIVE_TYPE) + { + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = f_tonative( *(ptr + 1)); + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + } + break; + } + + case EXT_CHAR_CODE: + { + objLength = 3; + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr += NON_DISPLAYABLE_CHAR; + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + case OEM_CODE: + { + objLength = 2; + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = f_tonative( *(ptr + 1)); + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + case UNICODE_CODE: + { + objLength = 3; + if (outputData) + { + if (bytesOutput < maxOutLen) + { + *outPtr++ = UNICODE_UNCONVERTABLE_CHAR; + } + else + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + + bytesOutput++; + break; + } + + default: + { + break; + } + } + + ptr += objLength; + bytesProcessed += objLength; + } + + // Add a terminating NULL character, but DO NOT increment the ; + // bytesOutput counter! + +Native_Output: + + if (outputData) + { + *outPtr = 0; + } + + *puiOutBufLenRV = bytesOutput; + +Exit: + + return (rc); +} diff --git a/flaim/src/ftksem.h b/flaim/src/ftksem.h new file mode 100644 index 0000000..3091c25 --- /dev/null +++ b/flaim/src/ftksem.h @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------- +// Desc: Cross platform toolkit for mutexes and semaphores - definitions. +// Tabs: 3 +// +// Copyright (c) 2000-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: ftksem.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#ifndef FTKSEM_H +#define FTKSEM_H + +#endif diff --git a/flaim/src/ftrans.cpp b/flaim/src/ftrans.cpp new file mode 100644 index 0000000..2b56dc3 --- /dev/null +++ b/flaim/src/ftrans.cpp @@ -0,0 +1,1745 @@ +//------------------------------------------------------------------------- +// Desc: Begin transaction +// Tabs: 3 +// +// Copyright (c) 1991,1994-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: fltrbeg.cpp 12266 2006-01-19 14:45:33 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Starts a transaction. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbTransBegin( + HFDB hDb, + FLMUINT uiTransType, + FLMUINT uiMaxLockWait, + FLMBYTE * pucHeader) +{ + RCODE rc = FERR_OK; + FLMBOOL bIgnore; + FLMUINT uiFlags = FLM_GET_TRANS_FLAGS( uiTransType); + FDB * pDb = (FDB *)hDb; + + uiTransType = FLM_GET_TRANS_TYPE( uiTransType); + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + FCL_WIRE Wire( pDb->pCSContext, pDb); + + if (!pDb->pCSContext->bConnectionGood) + { + rc = RC_SET( FERR_BAD_SERVER_CONNECTION); + } + else + { + if( RC_BAD( rc = Wire.doTransOp( + FCS_OP_TRANSACTION_BEGIN, uiTransType, uiFlags, + uiMaxLockWait, pucHeader))) + { + goto Exit; + } + } + + goto Exit; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, + FDB_TRANS_GOING_OK, 0, &bIgnore))) + { + goto Exit; + } + + // Verify the transaction type. + + if (( uiTransType != FLM_UPDATE_TRANS) && + ( uiTransType != FLM_READ_TRANS)) + { + rc = RC_SET( FERR_ILLEGAL_TRANS); + goto Exit; + } + + // Verify the transaction flags + + if( (uiFlags & FLM_DONT_KILL_TRANS) && uiTransType != FLM_READ_TRANS) + { + rc = RC_SET( FERR_ILLEGAL_TRANS); + goto Exit; + } + + // Can't start an update transaction on a database that + // is locked in shared mode. + + if ((uiTransType == FLM_UPDATE_TRANS) && + (pDb->uiFlags & FDB_FILE_LOCK_SHARED)) + { + rc = RC_SET( FERR_PERMISSION); + goto Exit; + } + + // If the database has an invisible transaction going, abort it + // before going any further - we don't want application transactions + // to be nested under invisible transactions. Application transactions + // take precedence over invisible transactions. + + if ((pDb->uiTransType != FLM_NO_TRANS) && + (pDb->uiFlags & FDB_INVISIBLE_TRANS)) + { + if (RC_BAD( rc = flmAbortDbTrans( pDb))) + { + goto Exit; + } + } + + // If the database is not running a transaction, start one. + // Otherwise, start a nested transaction - first verifying that + // the transation type matches. + + if (pDb->uiTransType == FLM_NO_TRANS) + { + FLMUINT uiBytesRead; + + if( pucHeader) + { + if( RC_BAD( rc = pDb->pSFileHdl->ReadHeader( + 0, 2048, pucHeader, &uiBytesRead))) + { + goto Exit; + } + } + + if (RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType, + uiMaxLockWait, uiFlags, + pucHeader ? &pucHeader [16] : NULL))) + { + goto Exit; + } + pDb->bHadUpdOper = FALSE; + } + else + { + // Cannot nest transactions. + + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + +Exit: + + flmExit( FLM_DB_TRANS_BEGIN, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc: Commits an active transaction. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbTransCommit( + HFDB hDb, + FLMBOOL * pbEmpty) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FLMBOOL bIgnore; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + 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, 0, 0, 0); + } + goto Exit; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, + FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore))) + { + goto Exit; + } + + // If there is an invisible transaction going, it should not be + // commitable by an application. + + if ((pDb->uiTransType == FLM_NO_TRANS) || + (pDb->uiFlags & FDB_INVISIBLE_TRANS)) + { + rc = RC_SET( FERR_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if( RC_BAD( pDb->AbortRc)) + { + rc = RC_SET( FERR_ABORT_TRANS); + goto Exit; + } + + if (pbEmpty) + { + *pbEmpty = FALSE; + } + rc = flmCommitDbTrans( pDb, 0, FALSE, pbEmpty); + +Exit: + + if( RC_OK( rc)) + { + rc = flmCheckDatabaseState( pDb); + } + + flmExit( FLM_DB_TRANS_COMMIT, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc: Aborts an active transaction. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbTransAbort( + HFDB hDb) +{ + RCODE rc; + FDB * pDb = (FDB *)hDb; + FLMBOOL bIgnore; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + FCL_WIRE Wire( pDb->pCSContext, pDb); + if (!pDb->pCSContext->bConnectionGood) + { + rc = RC_SET( FERR_BAD_SERVER_CONNECTION); + } + else + { + rc = Wire.doTransOp( FCS_OP_TRANSACTION_ABORT, 0, 0, 0); + } + goto Exit; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, + FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore))) + { + goto Exit; + } + + // If there is an invisible transaction going, it should not be + // abortable by an application. + + if ((pDb->uiTransType == FLM_NO_TRANS) || + (pDb->uiFlags & FDB_INVISIBLE_TRANS)) + { + rc = RC_SET( FERR_NO_TRANS_ACTIVE); + goto Exit; + } + + rc = flmAbortDbTrans( pDb); + +Exit: + + if( RC_OK( rc)) + { + rc = flmCheckDatabaseState( pDb); + } + + flmExit( FLM_DB_TRANS_ABORT, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc : Returns the type of the current database transaction. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbGetTransType( + HFDB hDb, + FLMUINT * puiTransTypeRV) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + + CS_CONTEXT * pCSContext = pDb->pCSContext; + FCL_WIRE Wire( pDb->pCSContext, pDb); + + // Send a request to get the transaction type. + + if( RC_BAD( rc = Wire.sendOp( + FCS_OPCLASS_TRANS, FCS_OP_TRANSACTION_GET_TYPE))) + { + goto Exit; + } + + if (RC_BAD( rc = Wire.sendTerminate())) + { + goto Transmission_Error; + } + + // Read the response. + + if (RC_BAD( rc = Wire.read())) + { + goto Transmission_Error; + } + + *puiTransTypeRV = Wire.getTransType(); + rc = Wire.getRCode(); + goto Exit; + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto Exit; + } + + if (!pDb) + { + rc = RC_SET( FERR_BAD_HDL); + goto Exit; + } + + fdbUseCheck( pDb); + pDb->uiInitNestLevel++; + (void)flmResetDiag( pDb); + + // If the transaction is an internal transaction that is invisible to + // the application, return FLM_NO_TRANS. Application is not supposed + // see invisible transactions. + + *puiTransTypeRV = (FLMUINT)(((pDb->uiTransType == FLM_NO_TRANS) || + (pDb->uiFlags & FDB_INVISIBLE_TRANS)) + ? (FLMUINT)FLM_NO_TRANS + : pDb->uiTransType); + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + +Exit: + + flmExit( FLM_DB_GET_TRANS_TYPE, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc : Retrieves the current transaction number of a database +Notes: This routine should only be called only from within an update + transaction since read transactions are not assigned a transaction + number. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbGetTransId( + HFDB hDb, + FLMUINT * puiTrNumRV) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FLMBOOL bIgnore; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + + CS_CONTEXT * pCSContext = pDb->pCSContext; + FCL_WIRE Wire( pCSContext, pDb); + + // Send a request to get the transaction ID. + + if (RC_BAD( rc = Wire.sendOp( + FCS_OPCLASS_DATABASE, FCS_OP_GET_TRANS_ID))) + { + goto Exit; + } + + if (RC_BAD( rc = Wire.sendTerminate())) + { + goto Transmission_Error; + } + + // Read the response + + if (RC_BAD( rc = Wire.read())) + { + goto Transmission_Error; + } + *puiTrNumRV = Wire.getTransId(); + + rc = Wire.getRCode(); + goto Exit; + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto Exit; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, + FDB_TRANS_GOING_OK, 0, &bIgnore))) + { + goto Exit; + } + + *puiTrNumRV = pDb->LogHdr.uiCurrTransID; + +Exit: + + flmExit( FLM_DB_GET_TRANS_ID, pDb, rc); + return( rc); +} + + +/**************************************************************************** +Desc: Retrieves the last commit sequence number of a database. +Notes: Whenever a transaction is committed, FLAIM increments the commit + sequence number to indicate that the database has been modified. + An application may use this routine to determine if the database + has been modified. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbGetCommitCnt( + HFDB hDb, + FLMUINT * puiCommitCount) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FLMBOOL bIgnore; + + if (IsInCSMode( hDb)) + { + fdbInitCS( pDb); + + CS_CONTEXT * pCSContext = pDb->pCSContext; + FCL_WIRE Wire( pCSContext, pDb); + + // Send a request to get the commit count + + if (RC_BAD( rc = Wire.sendOp( + FCS_OPCLASS_DATABASE, FCS_OP_GET_COMMIT_CNT))) + { + goto Exit; + } + + if (RC_BAD( rc = Wire.sendTerminate())) + { + goto Transmission_Error; + } + + // Read the response. + + if (RC_BAD( rc = Wire.read())) + { + goto Transmission_Error; + } + *puiCommitCount = (FLMUINT)Wire.getCount(); + + rc = Wire.getRCode(); + goto ExitCS; + +Transmission_Error: + + pCSContext->bConnectionGood = FALSE; + goto ExitCS; + } + + if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, + FDB_TRANS_GOING_OK, 0, &bIgnore))) + { + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (pDb->uiTransType != FLM_NO_TRANS) + { + if (flmCheckBadTrans( pDb)) + { + rc = RC_SET( FERR_ABORT_TRANS); + goto Exit; + } + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + *puiCommitCount = (FLMUINT)FB2UD( + &pDb->pFile->ucLastCommittedLogHdr [LOG_COMMIT_COUNT]); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + +Exit: +ExitCS: + + flmExit( FLM_DB_GET_COMMIT_CNT, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc: Forces a checkpoint on the database. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbCheckpoint( + HFDB hDb, + FLMUINT uiTimeout) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + FLMBOOL bStartedTrans; + + bStartedTrans = FALSE; + + if (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_CHECKPOINT))) + { + goto Exit; + } + + if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiTimeout))) + { + 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; + } + + // Start an update transaction. Must not already be one going. + + if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, + 0, uiTimeout | FLM_AUTO_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Commit the transaction, forcing it to be checkpointed. + + bStartedTrans = FALSE; + pDb->bHadUpdOper = FALSE; + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + +Exit: + + if (bStartedTrans) + { + (void)flmAbortDbTrans( pDb); + } + + flmExit( FLM_DB_CHECKPOINT, pDb, rc); + return( rc); +} + +/**************************************************************************** +Desc: This routine unlinks an FDB from a transaction's list of FDBs. +****************************************************************************/ +void flmUnlinkDbFromTrans( + FDB * pDb, + FLMBOOL bCommitting) +{ + FFILE * pFile = pDb->pFile; + + flmAssert( pDb->pIxdFixups == NULL); + if( pDb->uiTransType != FLM_NO_TRANS) + { + if (pDb->uiFlags & FDB_HAS_WRITE_LOCK) + { + + // If this is a commit operation and we have a commit callback, + // call the callback function before unlocking the DIB. + + if( bCommitting && pDb->fnCommit) + { + FLMBOOL bSavedInvisTrans; + + CB_ENTER( pDb, &bSavedInvisTrans); + pDb->fnCommit( (HFDB)pDb, pDb->pvCommitData); + CB_EXIT( pDb, bSavedInvisTrans); + } + + dbUnlock( pDb); + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + if (pDb->pDict) + { + flmUnlinkFdbFromDict( pDb); + } + + // Unlink the transaction from the FFILE if it is a read + // transaction + + if( pDb->uiTransType == FLM_READ_TRANS) + { + if (pDb->pNextReadTrans) + { + pDb->pNextReadTrans->pPrevReadTrans = pDb->pPrevReadTrans; + } + else if (!pDb->uiKilledTime) + { + pFile->pLastReadTrans = pDb->pPrevReadTrans; + } + if (pDb->pPrevReadTrans) + { + pDb->pPrevReadTrans->pNextReadTrans = pDb->pNextReadTrans; + } + else if (pDb->uiKilledTime) + { + pFile->pFirstKilledTrans = pDb->pNextReadTrans; + } + else + { + pFile->pFirstReadTrans = pDb->pNextReadTrans; + } + + // Zero out so it will be zero for next transaction begin. + + pDb->uiKilledTime = 0; + } + else + { + // Reset to NULL or zero for next update transaction. + + pDb->pBlobList = NULL; + pDb->pIxStartList = pDb->pIxStopList = NULL; + flmAssert( pDb->pIxdFixups == NULL); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + pDb->uiTransType = FLM_NO_TRANS; + pDb->uiFlags &= (~(FDB_UPDATED_DICTIONARY | + FDB_INVISIBLE_TRANS | + FDB_DONT_KILL_TRANS | + FDB_DONT_POISON_CACHE)); + } +} + +/**************************************************************************** +Desc: This routine starts a transaction for the specified database. The + transaction may be part of an overall larger transaction. +****************************************************************************/ +RCODE flmBeginDbTrans( + FDB * pDb, + FLMUINT uiTransType, + FLMUINT uiMaxLockWait, + FLMUINT uiFlags, + FLMBYTE * pucLogHdr) +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + FLMBOOL bMutexLocked = FALSE; + FLMBYTE * pucLastCommittedLogHdr; + DB_STATS * pDbStats = pDb->pDbStats; + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + // Initialize a few things - as few as is necessary to avoid + // unnecessary overhead. + + pDb->eAbortFuncId = FLM_UNKNOWN_FUNC; + pDb->AbortRc = FERR_OK; + pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; + pDb->KrefCntrl.bKrefSetup = FALSE; + pDb->uiTransType = uiTransType; + pDb->uiThreadId = (FLMUINT)f_threadId(); + pDb->uiTransCount++; + + // Link the FDB to the file's most current FDICT structure, + // if there is one. + // + // Also, if it is a read transaction, link the FDB + // into the list of read transactions off of + // the FFILE structure. + + f_mutexLock( gv_FlmSysData.hShareMutex); + bMutexLocked = TRUE; + if (pFile->pDictList) + { + + // Link the FDB to the FDICT. + + flmLinkFdbToDict( pDb, pFile->pDictList); + } + + // If it is a read transaction, link into the list of + // read transactions off of the FFILE structure. Until we + // get the log header transaction ID below, we set uiCurrTransID + // to zero and link this transaction in at the beginning of the + // list. + + if (uiTransType == FLM_READ_TRANS) + { + flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); + + // Link in at the end of the transaction list. + + pDb->pNextReadTrans = NULL; + if ((pDb->pPrevReadTrans = pFile->pLastReadTrans) != NULL) + { + + // Make sure transaction IDs are always in ascending order. They + // should be at this point. + + flmAssert( pFile->pLastReadTrans->LogHdr.uiCurrTransID <= + pDb->LogHdr.uiCurrTransID); + pFile->pLastReadTrans->pNextReadTrans = pDb; + } + else + { + pFile->pFirstReadTrans = pDb; + } + pFile->pLastReadTrans = pDb; + pDb->uiInactiveTime = 0; + + if( uiFlags & FLM_DONT_KILL_TRANS) + { + pDb->uiFlags |= FDB_DONT_KILL_TRANS; + } + else + { + pDb->uiFlags &= ~FDB_DONT_KILL_TRANS; + } + + if (pucLogHdr) + { + f_memcpy( pucLogHdr, &pDb->pFile->ucLastCommittedLogHdr[0], + LOG_HEADER_SIZE); + } + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + bMutexLocked = FALSE; + + if( uiFlags & FLM_DONT_POISON_CACHE) + { + pDb->uiFlags |= FDB_DONT_POISON_CACHE; + } + else + { + pDb->uiFlags &= ~FDB_DONT_POISON_CACHE; + } + + // Put an exclusive lock on the database if we are not in a read + // transaction. Read transactions require no lock. + + if (uiTransType != FLM_READ_TRANS) + { + flmAssert( pDb->pIxStats == NULL); + + // Set the bHadUpdOper to TRUE for all transactions to begin with. + // Many calls to flmBeginDbTrans are internal, and we WANT the + // normal behavior at the end of the transaction when it is + // committed or aborted. The only time this flag will be set + // to FALSE is when the application starts the transaction as + // opposed to an internal starting of the transaction. + + pDb->bHadUpdOper = TRUE; + + // Initialize the count of blocks changed to be 0 + + pDb->uiBlkChangeCnt = 0; + + if (RC_BAD( rc = dbLock( pDb, uiMaxLockWait))) + { + goto Exit; + } + + // If there was a problem with the RFL volume, we must wait + // for a checkpoint to be completed before continuing. + // The checkpoint thread looks at this same flag and forces + // a checkpoint. If it completes one successfully, it will + // reset this flag. + // + // Also, if the last forced checkpoint had a problem + // (pFile->CheckpointRc != FERR_OK), we don't want to + // start up a new update transaction until it is resolved. + + if (!pFile->pRfl->seeIfRflVolumeOk() || + RC_BAD( pFile->CheckpointRc)) + { + rc = RC_SET( FERR_MUST_WAIT_CHECKPOINT); + goto Exit; + } + + // Set the first log block address to zero. + + pFile->uiFirstLogBlkAddress = 0; + + // Header must be read before opening roll forward log file to make + // sure we have the most current log file and log options. + + f_memcpy( pFile->ucUncommittedLogHdr, pucLastCommittedLogHdr, + LOG_HEADER_SIZE); + flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); + + // Need to increment the current checkpoint for update transactions + // so that it will be correct when we go to mark cache blocks. + + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + // During recovery we need to set the transaction ID to the + // transaction ID that was logged. + + pDb->LogHdr.uiCurrTransID = pFile->pRfl->getCurrTransID(); + } + else + { + pDb->LogHdr.uiCurrTransID++; + } + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Link FDB to the most current local dictionary, if there + // is one. + + if (pFile->pDictList != pDb->pDict && pFile->pDictList) + { + flmLinkFdbToDict( pDb, pFile->pDictList); + } + pFile->uiUpdateTransID = pDb->LogHdr.uiCurrTransID; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Set the transaction EOF to the current file EOF + + pDb->uiTransEOF = pDb->LogHdr.uiLogicalEOF; + + // Put the transaction ID into the uncommitted log header. + + UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, + &pFile->ucUncommittedLogHdr [LOG_CURR_TRANS_ID]); + + if (pucLogHdr) + { + f_memcpy( pucLogHdr, &pDb->pFile->ucUncommittedLogHdr [0], + LOG_HEADER_SIZE); + } + } + + if (pDbStats) + { + f_timeGetTimeStamp( &pDb->TransStartTime); + } + + // If we do not have a dictionary, read it in from disk. + // NOTE: This should only happen when we are first opening + // the database. + + if (!pDb->pDict) + { + flmAssert( pDb->pFile->uiFlags & DBF_BEING_OPENED); + + if (RC_BAD( rc = fdictRebuild( pDb))) + { + if (pDb->pDict) + { + flmFreeDict( pDb->pDict); + pDb->pDict = NULL; + } + + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // At this point, we will not yet have opened the database for + // general use, so there is no way that any other thread can have + // created a dictionary yet. + + flmAssert( pDb->pFile->pDictList == NULL); + + // Link the new local dictionary to its file structure. + + flmLinkDictToFile( pDb->pFile, pDb->pDict); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + if (uiTransType != FLM_READ_TRANS) + { + if (RC_OK( rc)) + { + rc = pFile->pRfl->logBeginTransaction( pDb); + } +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, + 0, 0, rc, "TBeg"); +#endif + } + + if( uiTransType == FLM_UPDATE_TRANS && + gv_FlmSysData.UpdateEvents.pEventCBList) + { + flmTransEventCallback( F_EVENT_BEGIN_TRANS, (HFDB)pDb, rc, + (FLMUINT)(RC_OK( rc) + ? pDb->LogHdr.uiCurrTransID + : (FLMUINT)0)); + } + + if (RC_BAD( rc)) + { + // If there was an error, unlink the database from the transaction + // structure as well as from the FDICT structure. + + flmUnlinkDbFromTrans( pDb, FALSE); + + if (pDb->pStats) + { + (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine aborts an active transaction for a particular + database. If the database is open via a server, a message is + sent to the server to abort the transaction. Otherwise, the + transaction is rolled back locally. +****************************************************************************/ +RCODE flmAbortDbTrans( + FDB * pDb, + FLMBOOL bOkToLogAbort) +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + FLMUINT uiTransType; + FLMBYTE * pucLastCommittedLogHdr; + FLMBYTE * pucUncommittedLogHdr; + FLMBOOL bDumpedCache = FALSE; + DB_STATS * pDbStats = pDb->pDbStats; + FLMBOOL bKeepAbortedTrans; + FLMUINT uiTransId; + FLMBOOL bInvisibleTrans; + + // Get transaction type + + if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS) + { + goto Exit; + } + + // No recovery required if it is a read transaction. + + if (uiTransType == FLM_READ_TRANS) + { + + if( pDb->KrefCntrl.bKrefSetup) + { + // KrefCntrlFree could be called w/o checking bKrefSetup because + // it checks the flag, but it is more optimal to check the + // flag before making the call because most of the time it will + // be false. + + KrefCntrlFree( pDb); + } + + goto Unlink_From_Trans; + } + +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, + 0, 0, FERR_OK, "TAbrt"); +#endif + + pFile->pRfl->clearLogHdrs(); + + // If the transaction had no update operations, restore it + // to its pre-transaction state - make it appear that no + // transaction ever happened. + + pucLastCommittedLogHdr = &pFile->ucLastCommittedLogHdr [0]; + pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; + uiTransId = pDb->LogHdr.uiCurrTransID; + + // Free up all keys associated with this database. This is done even + // if we didn't have any update operations because the KREF may + // have been initialized by key generation operations performed + // by cursors, etc. + + KrefCntrlFree( pDb); + + // Free any index counts we may have allocated. + + FSFreeIxCounts( pDb); + + if (pDb->bHadUpdOper) + { + // Dump any BLOB structures that should be aborted. + + FBListAfterAbort( pDb); + + // Dump any start and stop indexing stubs that should be aborted. + + flmIndexingAfterAbort( pDb); + + // Log the abort record to the rfl file, or throw away the logged + // records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL + // flag. If the RFL volume is bad, we will not attempt to keep this + // transaction in the RFL. + + if (!pFile->pRfl->seeIfRflVolumeOk()) + { + bKeepAbortedTrans = FALSE; + } + else + { + bKeepAbortedTrans = + (pucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL]) + ? TRUE + : FALSE; + } + } + else + { + bKeepAbortedTrans = FALSE; + } + + // Log an abort transaction record to the roll-forward log or + // throw away the entire transaction, depending on the + // bKeepAbortedTrans flag. + + // If the transaction is being "dumped" because of a failed commit, + // don't log anything to the RFL. + + if( bOkToLogAbort) + { + flmAssert( pDb->LogHdr.uiCurrTransID == pFile->pRfl->getCurrTransID()); + if (RC_BAD( rc = pFile->pRfl->logEndTransaction( + RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans))) + { + goto Exit1; + } + } +#ifdef FLM_DEBUG + else + { + // If bOkToLogAbort is FALSE, this always means that either a + // commit failed while trying to log an end transaction packet or a + // commit packet was logged and the transaction commit subsequently + // failed for some other reason. In either case, the RFL should be + // in a good state, with its current transaction ID reset to 0. If + // not, either bOkToLogAbort is being used incorrectly by the caller + // or there is a bug in the RFL logic. + + flmAssert( pFile->pRfl->getCurrTransID() == 0); + } +#endif + + // If there were no operations in the transaction, restore + // everything as if the transaction never happened. + + if (!pDb->bHadUpdOper) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + pFile->uiUpdateTransID = 0; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Pretend we dumped cache - shouldn't be any to worry about at + // this point. + + bDumpedCache = TRUE; + goto Exit1; + } + + // Dump ALL modified cache blocks associated with the DB. + // NOTE: This needs to be done BEFORE the call to flmGetLogHdrInfo + // below, because that call will change pDb->LogHdr.uiCurrTransID, + // and that value is used by flmRcaAbortTrans. + + ScaFreeModifiedBlocks( pDb); + flmRcaAbortTrans( pDb); + bDumpedCache = TRUE; + + // Reset the LogHdr from the last committed log header in pFile. + + flmGetLogHdrInfo( pucLastCommittedLogHdr, &pDb->LogHdr); + if (RC_BAD( rc = flmPhysRollback( pDb, + (FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_ROLLBACK_EOF]), + pFile->uiFirstLogBlkAddress, FALSE, 0))) + { + goto Exit1; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Put the new transaction ID into the log header even though + // we are not committing. We want to keep the transaction IDs + // incrementing even though we aborted. + + UD2FBA( (FLMUINT32)uiTransId, + &pucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); + + // Preserve where we are at in the roll-forward log. Even though + // the transaction aborted, we may have kept it in the RFL instead of + // throw it away. + + f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_FILE_NUM], + &pucUncommittedLogHdr [LOG_RFL_FILE_NUM], 4); + f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET], + &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET], 4); + f_memcpy( &pucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + f_memcpy( &pucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + + // The following items tell us where we are at in the roll-back log. + // During a transaction we may log blocks for the checkpoint or for + // read transactions. So, even though we are aborting this transaction, + // there may be other things in the roll-back log that we don't want + // to lose. These items should not be reset until we do a checkpoint, + // which is when we know it is safe to throw away the entire roll-back log. + + f_memcpy( &pucLastCommittedLogHdr [LOG_ROLLBACK_EOF], + &pucUncommittedLogHdr [LOG_ROLLBACK_EOF], 4); + f_memcpy( &pucLastCommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], + &pucUncommittedLogHdr [LOG_PL_FIRST_CP_BLOCK_ADDR], 4); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + pFile->pRfl->commitLogHdrs( pucLastCommittedLogHdr, + pFile->ucCheckpointLogHdr); + +Exit1: + + // Dump cache, if not done above. + + if (!bDumpedCache) + { + ScaFreeModifiedBlocks( pDb); + flmRcaAbortTrans( pDb); + bDumpedCache = TRUE; + } + + // Throw away IXD_FIXUPs + + if (pDb->pIxdFixups) + { + IXD_FIXUP * pIxdFixup; + IXD_FIXUP * pDeleteIxdFixup; + + pIxdFixup = pDb->pIxdFixups; + while (pIxdFixup) + { + pDeleteIxdFixup = pIxdFixup; + pIxdFixup = pIxdFixup->pNext; + f_free( &pDeleteIxdFixup); + } + pDb->pIxdFixups = NULL; + } + + if (uiTransType != FLM_READ_TRANS && + gv_FlmSysData.UpdateEvents.pEventCBList) + { + flmTransEventCallback( F_EVENT_ABORT_TRANS, (HFDB)pDb, rc, + uiTransId); + } + +Unlink_From_Trans: + + bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE; + if (pDb->uiFlags & FDB_HAS_WRITE_LOCK) + { + RCODE tmpRc; + + if (RC_BAD( tmpRc = pFile->pRfl->completeTransWrites( pDb, FALSE, FALSE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + } + } + + // Unlink the database from the transaction + // structure as well as from the FLDICT structure. + + flmUnlinkDbFromTrans( pDb, FALSE); + + if (pDbStats) + { + FLMUINT64 ui64ElapMilli = 0; + + flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli); + pDbStats->bHaveStats = TRUE; + if (uiTransType == FLM_READ_TRANS) + { + pDbStats->ReadTransStats.AbortedTrans.ui64Count++; + pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli += + ui64ElapMilli; + if (bInvisibleTrans) + { + pDbStats->ReadTransStats.InvisibleTrans.ui64Count++; + pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + else + { + pDbStats->UpdateTransStats.AbortedTrans.ui64Count++; + pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + + if (pDb->pStats) + { + (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine commits an active transaction for a particular + database. If the database is open via a server, a message is + sent to the server to commit the transaction. Otherwise, the + transaction is committed locally. +****************************************************************************/ +RCODE flmCommitDbTrans( + FDB * pDb, + FLMUINT uiNewLogicalEOF, // New logical end-of-file. This is only + // set by the FlmDbReduceSize function when + // it is truncating the file. + FLMBOOL bForceCheckpoint, // Force a checkpoint? + FLMBOOL * pbEmpty) // May be NULL +{ + RCODE rc = FERR_OK; + FLMBYTE * pucUncommittedLogHdr; + FFILE * pFile = pDb->pFile; + FLMUINT uiCPFileNum = 0; + FLMUINT uiCPOffset = 0; + FLMUINT uiTransId = 0; + FLMBOOL bTransEndLogged; + FLMBOOL bForceCloseOnError = FALSE; + FLMBOOL bOkToLogAbort = TRUE; + DB_STATS * pDbStats = pDb->pDbStats; + FLMUINT uiTransType; + FLMBOOL bInvisibleTrans = FALSE; + FLMBOOL bIndexAfterCommit = FALSE; + + pDb->uiFlags |= FDB_COMMITTING_TRANS; + + // See if we even have a transaction going. + + if ((uiTransType = pDb->uiTransType) == FLM_NO_TRANS) + { + goto Exit; // Will return FERR_OK. + } + + // See if we have a transaction going which should be aborted. + + if (flmCheckBadTrans( pDb)) + { + rc = RC_SET( FERR_ABORT_TRANS); + goto Exit; + } + + // If we are in a read transaction we can skip most of the stuff + // below because no updates would have occurred. This will help + // improve performance. + + if (uiTransType == FLM_READ_TRANS) + { + + if( pDb->KrefCntrl.bKrefSetup) + { + // KrefCntrlFree could be called w/o checking bKrefSetup because + // it checks the flag, but it is more optimal to check the + // flag before making the call because most of the time it will + // be false. + + KrefCntrlFree( pDb); + } + goto Exit1; + } + + // At this point, we know we have an update transaction. + + pFile->pRfl->clearLogHdrs(); + +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( pFile->uiFFileId, pDb->LogHdr.uiCurrTransID, + 0, 0, FERR_OK, "TCmit"); +#endif + uiTransId = pDb->LogHdr.uiCurrTransID; + + // If the transaction had no update operations, restore it + // to its pre-transaction state - make it appear that no + // transaction ever happened. + + if (!pDb->bHadUpdOper) + { + bOkToLogAbort = FALSE; + rc = pFile->pRfl->logEndTransaction( RFL_TRNS_COMMIT_PACKET, TRUE); + + // Even though we didn't have any update operations, there may have + // been operations during the transaction (i.e., query operations) + // that initialized the KREF in order to generate keys. + + KrefCntrlFree( pDb); + + // Restore everything as if the transaction never happened. + + f_mutexLock( gv_FlmSysData.hShareMutex); + pFile->uiUpdateTransID = 0; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + if (pbEmpty) + { + *pbEmpty = TRUE; + } + goto Exit1; + } + + // Log commit record to roll-forward log + + bOkToLogAbort = FALSE; + if (RC_BAD( rc = pFile->pRfl->logEndTransaction( + RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged))) + { + goto Exit1; + } + bForceCloseOnError = TRUE; + + // Commit any keys in the KREF buffers. + + if (RC_BAD( rc = KYKeysCommit( pDb, TRUE))) + { + flmLogError( rc, "calling KYKeysCommit from flmCommitDbTrans"); + goto Exit1; + } + + if (RC_BAD( rc = FSCommitIxCounts( pDb))) + { + flmLogError( rc, "calling FSCommitIxCounts from flmCommitDbTrans"); + goto Exit1; + } + + // Reinitialize the log header. If the local dictionary was updated + // during the transaction, increment the local dictionary ID so that + // other concurrent users will know that it has been modified and + // that they need to re-read it into memory. + + // If we are in recovery mode, see if we need to force + // a checkpoint with what we have so far. We force a + // checkpoint on one of two conditions: + + // 1. If it appears that we have a buildup of dirty cache + // blocks. We force a checkpoint on this condition + // because it will be more efficient than replacing + // cache blocks one at a time. + // We check for this condition by looking to see if + // our LRU block is not used and it is dirty. That is + // a pretty good indicator that we have a buildup + // of dirty cache blocks. + // 2. We are at the end of the roll-forward log. We + // want to force a checkpoint here to complete the + // recovery phase. + + if ( pDb->uiFlags & FDB_REPLAYING_RFL) + { + // If we are in the middle of upgrading, and are forcing + // a checkpoint, use the file number and offset that were + // set in the FDB. + + if ((pDb->uiFlags & FDB_UPGRADING) && bForceCheckpoint) + { + uiCPFileNum = pDb->uiUpgradeCPFileNum; + uiCPOffset = pDb->uiUpgradeCPOffset; + } + else + { + SCACHE * pTmpSCache; + F_Rfl * pRfl = pFile->pRfl; + + f_mutexLock( gv_FlmSysData.hShareMutex); + pTmpSCache = gv_FlmSysData.SCacheMgr.pLRUCache; + + // Test for buildup of dirty cache blocks. + + if (((pTmpSCache) && + (!pTmpSCache->uiUseCount) && + (pTmpSCache->ui16Flags & + (CA_DIRTY | CA_LOG_FOR_CP | CA_WRITE_TO_LOG))) + + || // Test for end of roll-forward log. + + pRfl->atEndOfLog() + || + bForceCheckpoint) + { + bForceCheckpoint = TRUE; + uiCPFileNum = pRfl->getCurrFileNum(); + uiCPOffset = pRfl->getCurrReadOffset(); + } + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + } + + // Move information collected in the pDb->LogHdr into the + // uncommitted log header. Other things that need to be + // set have already been set in the uncommitted log header + // at various places in the code. + + // Mutex does not have to be locked while we do this because + // the update transaction is the only one that ever accesses + // the uncommitted log header buffer. + + pucUncommittedLogHdr = &pFile->ucUncommittedLogHdr [0]; + + // Set the new logical EOF if passed in. + + if (uiNewLogicalEOF) + { + pDb->LogHdr.uiLogicalEOF = uiNewLogicalEOF; + } + UD2FBA( (FLMUINT32)pDb->LogHdr.uiLogicalEOF, + &pucUncommittedLogHdr [LOG_LOGICAL_EOF]); + + // Increment the commit counter. + + flmIncrUint( &pucUncommittedLogHdr [LOG_COMMIT_COUNT], 1); + + // Set the last committed transaction ID + + if( (bTransEndLogged || (pDb->uiFlags & FDB_REPLAYING_COMMIT)) && + pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) + { + UD2FBA( (FLMUINT32)uiTransId, + &pucUncommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]); + } + + // Write the header + + pFile->pRfl->commitLogHdrs( pucUncommittedLogHdr, + pFile->ucCheckpointLogHdr); + + // Commit any record cache. + + flmRcaCommitTrans( pDb); + + // Push the IXD_FIXUP values back into the IXD + + if (pDb->pIxdFixups) + { + IXD_FIXUP * pIxdFixup; + IXD_FIXUP * pDeleteIxdFixup; + IXD * pIxd; + + pIxdFixup = pDb->pIxdFixups; + while (pIxdFixup) + { + if( RC_BAD( fdictGetIndex( + pDb->pDict, pDb->pFile->bInLimitedMode, + pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE))) + { + flmAssert( 0); + pIxd = NULL; + } + + if( pIxd) + { + pIxd->uiLastContainerIndexed = pIxdFixup->uiLastContainerIndexed; + pIxd->uiLastDrnIndexed = pIxdFixup->uiLastDrnIndexed; + } + pDeleteIxdFixup = pIxdFixup; + pIxdFixup = pIxdFixup->pNext; + f_free( &pDeleteIxdFixup); + } + pDb->pIxdFixups = NULL; + } + + // Set the update transaction ID back to zero only + // AFTER we know the transaction has safely committed. + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_memcpy( pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, + LOG_HEADER_SIZE); + pFile->uiUpdateTransID = 0; + ScaReleaseLogBlocks( pFile); + if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) + { + // Link the new local dictionary to its file. + // Since the new local dictionary will be linked at the head + // of the list of FDICT structures, see if the FDICT currently + // at the head of the list is unused and can be unlinked. + + if ((pFile->pDictList) && (!pFile->pDictList->uiUseCount)) + { + flmUnlinkDict( pFile->pDictList); + } + flmLinkDictToFile( pFile, pDb->pDict); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + +Exit1: + + // If the local dictionary was updated during this transaction, + // link the new local dictionary structures to their file - or free + // them if there was an error. + + if (pDb->uiFlags & FDB_UPDATED_DICTIONARY) + { + if( RC_BAD( rc) && pDb->pDict) + { + // Unlink the FDB from the FDICT. - Shouldn't have + // to lock semaphore, because the DICT is NOT linked + // to the FFILE. + + flmAssert( pDb->pDict->pFile == NULL); + flmUnlinkFdbFromDict( pDb); + } + } + + if (RC_BAD( rc)) + { + + // Since we failed to commit, do an abort. We are purposely not + // checking the return code from flmAbortDbTrans because we already + // have an error return code. If we attempted to log the transaction + // to the RFL and failed, we don't want to try to log an abort packet. + // The RFL code has already reset the log back to the starting point + // of the transaction, thereby discarding all operations. + + pDb->uiFlags &= ~FDB_COMMITTING_TRANS; + (void)flmAbortDbTrans( pDb, bOkToLogAbort); + uiTransType = FLM_NO_TRANS; + + // Do we need to force all handles to close? + + if( bForceCloseOnError) + { + + // Since the commit packet has already been logged to the RFL, + // we must have failed when trying to write the log header. The + // database is in a bad state and must be closed. + + // Set the "must close" flag on all FDBs linked to the FFILE + // and set the FFILE's "must close" flag. This will cause any + // subsequent operations on the database to fail until all + // handles have been closed. + + flmSetMustCloseFlags( pFile, rc, FALSE); + } + } + else + { + bInvisibleTrans = (pDb->uiFlags & FDB_INVISIBLE_TRANS) ? TRUE : FALSE; + if (uiTransType == FLM_UPDATE_TRANS) + { + if (gv_FlmSysData.UpdateEvents.pEventCBList) + { + flmTransEventCallback( F_EVENT_COMMIT_TRANS, (HFDB)pDb, rc, + uiTransId); + } + + // Do the BLOB and indexing work before we unlock the db. + + FBListAfterCommit( pDb); + + if (pDb->pIxStopList || pDb->pIxStartList) + { + + // Must not call flmIndexingAfterCommit until after + // completeTransWrites. Otherwise, there is a potential + // deadlock condition where flmIndexingAfterCommit is + // waiting on an indexing thread to quit, but that + // thread is waiting to be signaled by this thread that + // writes are completed. However, flmIndexingAfterCommit + // also must only be called while the database is still + // locked. If we were to leave the database locked for + // every call to completeTransWrites, however, we would + // lose the group commit capability. Hence, we opt to + // only lose it when there are actual indexing operations + // to start or stop - which should be very few transactions. + // That is what the bIndexAfterCommit flag is for. + + bIndexAfterCommit = TRUE; + } + } + } + + // Unlock the database, if the update transaction is still going. + // NOTE: We check uiTransType because it may have been reset + // to FLM_NO_TRANS up above if flmAbortDbTrans was called. + + if (uiTransType == FLM_UPDATE_TRANS) + { + if (RC_BAD( rc)) + { + + // SHOULD NEVER HAPPEN - because it would have been taken + // care of above - flmAbortDbTrans would have been called and + // uiTransType would no longer be FLM_UPDATE_TRANS. + + flmAssert( 0); + (void)pFile->pRfl->completeTransWrites( pDb, FALSE, TRUE); + } + else if (!bForceCheckpoint) + { + if (bIndexAfterCommit) + { + rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE); + flmIndexingAfterCommit( pDb); + flmUnlinkDbFromTrans( pDb, TRUE); + } + else + { + rc = pFile->pRfl->completeTransWrites( pDb, TRUE, TRUE); + } + } + else + { + + // Do checkpoint, if forcing. Before doing the checkpoint + // we have to make sure the roll-forward log writes + // complete. We don't want to unlock the DB while the + // writes are happening in this case - thus, the FALSE + // parameter to completeTransWrites. + + if (RC_OK( rc = pFile->pRfl->completeTransWrites( pDb, TRUE, FALSE))) + { + bForceCloseOnError = FALSE; + rc = ScaDoCheckpoint( pDbStats, pDb->pSFileHdl, pFile, + (pDb->uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE, + TRUE, CP_TIME_INTERVAL_REASON, + uiCPFileNum, uiCPOffset); + } + if (bIndexAfterCommit) + { + flmIndexingAfterCommit( pDb); + } + flmUnlinkDbFromTrans( pDb, TRUE); + } + + if (RC_BAD( rc) && bForceCloseOnError) + { + + // Since the commit packet has already been logged to the RFL, + // we must have failed when trying to write the log header. The + // database is in a bad state and must be closed. + + // Set the "must close" flag on all FDBs linked to the FFILE + // and set the FFILE's "must close" flag. This will cause any + // subsequent operations on the database to fail until all + // handles have been closed. + + flmSetMustCloseFlags( pFile, rc, FALSE); + } + } + else + { + + // Unlink the database from the transaction + // structure as well as from the FDICT structure. + + flmUnlinkDbFromTrans( pDb, FALSE); + } + + if (pDbStats && uiTransType != FLM_NO_TRANS) + { + FLMUINT64 ui64ElapMilli = 0; + + flmAddElapTime( &pDb->TransStartTime, &ui64ElapMilli); + pDbStats->bHaveStats = TRUE; + if (uiTransType == FLM_READ_TRANS) + { + pDbStats->ReadTransStats.CommittedTrans.ui64Count++; + pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli += + ui64ElapMilli; + if (bInvisibleTrans) + { + pDbStats->ReadTransStats.InvisibleTrans.ui64Count++; + pDbStats->ReadTransStats.InvisibleTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + else + { + pDbStats->UpdateTransStats.CommittedTrans.ui64Count++; + pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + + // Update stats + + if (pDb->pStats) + { + (void)flmStatUpdate( &gv_FlmSysData.Stats, &pDb->Stats); + } + +Exit: + + pDb->uiFlags &= ~FDB_COMMITTING_TRANS; + return( rc); +}