//------------------------------------------------------------------------------ // Desc: Routines for reading records from FLXIM 4.x databases // // Tabs: 3 // // Copyright (c) 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: frecread.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" static FLMBYTE gv_ucMaxBcdINT32[] = {0x21, 0x47, 0x48, 0x36, 0x47}; static FLMBYTE gv_ucMinBcdINT32[] = {0xB2, 0x14, 0x74, 0x83, 0x64, 0x8F}; static FLMBYTE gv_ucMaxBcdUINT32[] = {0x42, 0x94, 0x96, 0x72, 0x95}; typedef struct { const char * pszTagName; FLMUINT uiTagNum; FLMUINT uiFieldType; } FLM_4x_DICT_TAG_INFO; FLM_4x_DICT_TAG_INFO Flm4xDictTagInfo[] = { {FLM_4x_FIELD_TAG_NAME, FLM_4x_FIELD_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_INDEX_TAG_NAME, FLM_4x_INDEX_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_TYPE_TAG_NAME, FLM_4x_TYPE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_CONTAINER_TAG_NAME, FLM_4x_CONTAINER_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_LANGUAGE_TAG_NAME, FLM_4x_LANGUAGE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_OPTIONAL_TAG_NAME, FLM_4x_OPTIONAL_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_UNIQUE_TAG_NAME, FLM_4x_UNIQUE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_KEY_TAG_NAME, FLM_4x_KEY_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_REFS_TAG_NAME, FLM_4x_REFS_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_AREA_TAG_NAME, FLM_4x_AREA_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_STATE_TAG_NAME, FLM_4x_STATE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_BLOB_TAG_NAME, FLM_4x_BLOB_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_THRESHOLD_TAG_NAME, FLM_4x_THRESHOLD_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_SUFFIX_TAG_NAME, FLM_4x_SUFFIX_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_SUBDIRECTORY_TAG_NAME, FLM_4x_SUBDIRECTORY_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_RESERVED_TAG_NAME, FLM_4x_RESERVED_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_SUBNAME_TAG_NAME, FLM_4x_SUBNAME_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_NAME_TAG_NAME, FLM_4x_NAME_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_BASE_TAG_NAME, FLM_4x_BASE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_CASE_TAG_NAME, FLM_4x_CASE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_COMBINATIONS_TAG_NAME, FLM_4x_COMBINATIONS_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_COUNT_TAG_NAME, FLM_4x_COUNT_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_POSITIONING_TAG_NAME, FLM_4x_POSITIONING_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_PAIRED_TAG_NAME, FLM_4x_PAIRED_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_PARENT_TAG_NAME, FLM_4x_PARENT_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_POST_TAG_NAME, FLM_4x_POST_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_REQUIRED_TAG_NAME, FLM_4x_REQUIRED_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_USE_TAG_NAME, FLM_4x_USE_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_FILTER_TAG_NAME, FLM_4x_FILTER_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_LIMIT_TAG_NAME, FLM_4x_LIMIT_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_DICT_TAG_NAME, FLM_4x_DICT_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_RECINFO_TAG_NAME, FLM_4x_RECINFO_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_DRN_TAG_NAME, FLM_4x_DRN_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_DICT_SEQ_TAG_NAME, FLM_4x_DICT_SEQ_TAG, FLM_4x_TEXT_TYPE}, {FLM_4x_LAST_CONTAINER_INDEXED_TAG_NAME, FLM_4x_LAST_CONTAINER_INDEXED_TAG, FLM_4x_NUMBER_TYPE}, {FLM_4x_LAST_DRN_INDEXED_TAG_NAME, FLM_4x_LAST_DRN_INDEXED_TAG, FLM_4x_NUMBER_TYPE}, {FLM_4x_ONLINE_TRANS_ID_TAG_NAME, FLM_4x_ONLINE_TRANS_ID_TAG, FLM_4x_NUMBER_TYPE}, {NULL, 0} }; /*************************************************************************** Desc: ****************************************************************************/ F_4xReader::F_4xReader() { m_tmpPool.poolInit( 32 * 1024); m_pSuperHdl = NULL; m_pLckFile = NULL; m_uiMaxFileSize = 0; m_pLFileTbl = NULL; m_uiLFileCnt = 0; m_uiFieldTblSize = 0; m_puiFieldTbl = NULL; m_ppBlockTbl = NULL; m_uiBlockTblSize = 0; m_uiDefaultContainer = 0; m_pNameTable = NULL; f_memset( &m_fileHdr, 0, sizeof( F_4x_FILE_HDR)); f_memset( &m_logHdr, 0, sizeof( F_4x_LOG_HDR)); } /*************************************************************************** Desc: ****************************************************************************/ F_4xReader::~F_4xReader() { closeDatabase(); m_tmpPool.poolFree(); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::openDatabase( char * pszPath) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucReadBuf = NULL; FLMBYTE * pucPrefix; FLMBYTE * pucFileHdr; FLMBYTE * pucLogHdr; FLMUINT uiBytesRead; FLMUINT uiTmp; F_FileHdl * pFileHdl = NULL; flmAssert( !m_pSuperHdl); if( (m_pSuperHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = m_pSuperHdl->Setup( NULL, pszPath, NULL))) { goto Exit; } // We must have exclusive access. if( RC_BAD( rc = createLckFile( pszPath))) { goto Exit; } // Read and verify the file and log headers. if( RC_BAD( rc = m_pSuperHdl->GetFileHdl( 0, FALSE, (IF_FileHdl **)&pFileHdl))) { goto Exit; } if( RC_BAD( rc = f_alloc( 2048, &pucReadBuf))) { goto Exit; } // Read the fixed information area if( RC_BAD( rc = pFileHdl->Read( 0, 2048, pucReadBuf, &uiBytesRead))) { goto Exit; } *pucReadBuf = 0xFF; pucPrefix = pucReadBuf; pucFileHdr = &pucReadBuf[ FLM_4x_FLAIM_HEADER_START]; // Make sure we have a valid prefix if( pucPrefix[ 1] != f_toascii('W') || pucPrefix[ 2] != f_toascii('P') || pucPrefix[ 3] != f_toascii('C')) { rc = RC_SET( NE_XFLM_NOT_FLAIM); goto Exit; } // Extract the file header info m_fileHdr.uiBlockSize = (FLMUINT)FB2UW( &pucFileHdr[ FLM_4x_DB_BLOCK_SIZE]); m_fileHdr.uiAppMajorVer = pucPrefix[ 10]; m_fileHdr.uiAppMinorVer = pucPrefix[ 11]; m_fileHdr.uiDefaultLanguage = pucFileHdr[ FLM_4x_DB_DEFAULT_LANGUAGE]; m_fileHdr.uiVersionNum = ((FLMUINT16)(pucFileHdr[ FLM_4x_VER_POS] - ASCII_ZERO) * 100 + (FLMUINT16)(pucFileHdr[ FLM_4x_MINOR_VER_POS] - ASCII_ZERO) * 10 + (FLMUINT16)(pucFileHdr[ FLM_4x_SMINOR_VER_POS] - ASCII_ZERO)); // Is the block size valid? if( m_fileHdr.uiBlockSize != 4096 && m_fileHdr.uiBlockSize != 8192) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Supported version? switch( m_fileHdr.uiVersionNum) { case FLM_VER_4_0: case FLM_VER_4_3: case FLM_VER_4_31: case FLM_VER_4_50: case FLM_VER_4_51: break; default: rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); goto Exit; } // Get other log header elements. m_fileHdr.uiFirstLFHBlkAddr = (FLMUINT)FB2UD( &pucFileHdr[ FLM_4x_DB_1ST_LFH_ADDR]); if( pucFileHdr[ FLM_4x_FLAIM_NAME_POS ] != f_toascii( 'F') || pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 1 ] != f_toascii( 'L') || pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 2 ] != f_toascii( 'A') || pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 3 ] != f_toascii( 'I') || pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 4 ] != f_toascii( 'M')) { rc = RC_SET( NE_XFLM_NOT_FLAIM); goto Exit; } // Set up the uiSigBitsInBlkSize member of the file // header m_fileHdr.uiSigBitsInBlkSize = 0; uiTmp = m_fileHdr.uiBlockSize; while( !(uiTmp & 0x0001)) { m_fileHdr.uiSigBitsInBlkSize++; uiTmp >>= 1; } // Get the log file header information pucLogHdr = &pucReadBuf[ FLM_4x_DB_LOG_HEADER_START]; // Verify the checksums in the log header if( lgHdrCheckSum( pucLogHdr, TRUE) != 0) { rc = RC_SET( NE_XFLM_BLOCK_CRC); goto Exit; } m_logHdr.uiCurrTransID = (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_CURR_TRANS_ID]); m_logHdr.uiLogicalEOF = (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_LOGICAL_EOF]); m_logHdr.uiFirstAvailBlkAddr = (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PF_AVAIL_BLKS]); m_logHdr.uiAvailBlkCount = (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PF_NUM_AVAIL_BLKS]); // Get the maximum file size if( m_fileHdr.uiVersionNum >= FLM_VER_4_3) { m_uiMaxFileSize = (FLMUINT)(FB2UW(&((pucLogHdr)[ FLM_4x_LOG_MAX_FILE_SIZE]))) << 16; } else { m_uiMaxFileSize = 0x7FF00000; } // Make sure that no recovery needs to be done. if( (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_ROLLBACK_EOF]) != m_fileHdr.uiBlockSize || (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PL_FIRST_CP_BLOCK_ADDR])) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Set the block size m_pSuperHdl->ReleaseFile( (FLMUINT)0, TRUE); m_pSuperHdl->SetBlockSize( m_fileHdr.uiBlockSize); // Set up the block table m_uiBlockTblSize = 1024; if( RC_BAD( rc = f_calloc( sizeof( F_Block *) * m_uiBlockTblSize, &m_ppBlockTbl))) { m_uiBlockTblSize = 0; goto Exit; } // Set the default container m_uiDefaultContainer = FLM_4x_DATA_CONTAINER;; // Read the LFile table if( RC_BAD( rc = readLFiles())) { goto Exit; } // Read the dictionary if( RC_BAD( rc = readDictionary())) { goto Exit; } Exit: if( pFileHdl) { m_pSuperHdl->ReleaseFile( (FLMUINT)0, TRUE); } if( pucReadBuf) { f_free( &pucReadBuf); } if( RC_BAD( rc)) { closeDatabase(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ FLMUINT F_4xReader::lgHdrCheckSum( FLMBYTE * pucLogHdr, FLMBOOL bCompare) { FLMUINT uiCnt; FLMUINT uiTempSum; FLMUINT uiCurrCheckSum; FLMUINT uiTempSum2; FLMUINT uiBytesToChecksum; uiBytesToChecksum = (FB2UW( &pucLogHdr[ FLM_4x_LOG_FLAIM_VERSION]) < FLM_VER_4_3) ? 88 : 156; if( (uiCurrCheckSum = (FLMUINT)FB2UW( &pucLogHdr[ FLM_4x_LOG_HDR_CHECKSUM])) == 0xFFFF) { uiCurrCheckSum = 0; } if( bCompare && !uiCurrCheckSum) { return( 0); } for( uiTempSum = 0 - (FLMUINT)FB2UW( &pucLogHdr[ FLM_4x_LOG_HDR_CHECKSUM]), uiCnt = 1 + uiBytesToChecksum / sizeof( FLMUINT16); --uiCnt != 0;) { uiTempSum += (FLMUINT)FB2UW( pucLogHdr); pucLogHdr += sizeof( FLMUINT16); } if( (0 == (uiTempSum2 = (uiTempSum & 0xFFFF))) || (uiTempSum2 == 0xFFFF)) { uiTempSum2 = 1; } return( (FLMUINT)(((bCompare) && (uiTempSum2 == uiCurrCheckSum)) ? (FLMUINT)0 : uiTempSum2) ); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::createLckFile( char * pszFilePath) { RCODE rc = NE_XFLM_OK; char szLockPath[ F_PATH_MAX_SIZE]; char szDbBaseName[ F_FILENAME_SIZE]; char * pszFileExt; F_FileHdl * pLockFileHdl = NULL; // Extract the 8.3 name and put a .lck extension on it to create // the full path for the .lck file. if( RC_BAD( rc = gv_pFileSystem->pathReduce( pszFilePath, szLockPath, szDbBaseName))) { goto Exit; } pszFileExt = &szDbBaseName[ 0]; while( (*pszFileExt) && (*pszFileExt != '.')) { pszFileExt++; } f_strcpy( pszFileExt, ".lck"); if (RC_BAD( rc = gv_pFileSystem->pathAppend( szLockPath, szDbBaseName))) { goto Exit; } if( (pLockFileHdl = f_new F_FileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } #ifndef FLM_UNIX pLockFileHdl->setupFileHdl( 0, TRUE); #else // On Unix, we do not want to delete the file because it // will succeed even if someone else has the file open. pLockFileHdl->setupFileHdl( 0, FALSE); #endif if( RC_BAD( pLockFileHdl->Create( szLockPath, XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) { #ifndef FLM_UNIX if (RC_BAD( gv_pFileSystem->Delete( szLockPath))) { rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); goto Exit; } else if (RC_BAD( pLockFileHdl->Create( szLockPath, XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) { rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); goto Exit; } #else if( RC_BAD( pLockFileHdl->Open( szLockPath, XFLM_IO_RDWR | XFLM_IO_SH_DENYRW))) { rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); goto Exit; } #endif } #ifdef FLM_UNIX if( RC_BAD( pLockFileHdl->Lock())) { rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); goto Exit; } #endif m_pLckFile = pLockFileHdl; pLockFileHdl = NULL; Exit: if (pLockFileHdl) { pLockFileHdl->Close(); pLockFileHdl->Release(); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ void F_4xReader::closeDatabase( void) { FLMUINT uiLoop; m_tmpPool.poolReset( NULL); if( m_pLckFile) { m_pLckFile->Release(); m_pLckFile = NULL; } if( m_pSuperHdl) { m_pSuperHdl->Release(); } if( m_pLFileTbl) { f_free( &m_pLFileTbl); m_pLFileTbl = NULL; } m_uiLFileCnt = 0; if( m_puiFieldTbl) { f_free( &m_puiFieldTbl); m_puiFieldTbl = NULL; } m_uiFieldTblSize = 0; if( m_ppBlockTbl) { for( uiLoop = 0; uiLoop < m_uiBlockTblSize; uiLoop++) { if( m_ppBlockTbl[ uiLoop]) { m_ppBlockTbl[ uiLoop]->Release(); } } f_free( &m_ppBlockTbl); } m_uiBlockTblSize = 0; if( m_pNameTable) { m_pNameTable->Release(); m_pNameTable = NULL; } m_uiMaxFileSize = 0; f_memset( &m_fileHdr, 0, sizeof( F_4x_FILE_HDR)); f_memset( &m_logHdr, 0, sizeof( F_4x_LOG_HDR)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::readLFiles( void) { RCODE rc = NE_XFLM_OK; F_Block * pBlock = NULL; FLMBYTE * pucBlk; FLMUINT uiBlkAddress; FLMUINT uiPos; FLMUINT uiEndPos; FLMUINT uiEstCount; FLMUINT uiLFileCnt; FLMUINT uiLFHCnt; FLMUINT uiBlkSize = m_fileHdr.uiBlockSize; F_4x_LFILE TmpLFile; F_4x_LFILE * pLFile; F_4x_LFILE * pLFiles = NULL; f_memset( &TmpLFile, 0, sizeof( F_4x_LFILE)); for( uiEstCount = 0, uiLFileCnt = 4, uiBlkAddress = m_fileHdr.uiFirstLFHBlkAddr; uiBlkAddress != FLM_4x_BT_END;) { if( RC_BAD( rc = readBlock( uiBlkAddress, &pBlock))) { goto Exit; } pucBlk = pBlock->m_pucBlk; uiPos = FLM_4x_BH_OVHD; if( (uiEndPos = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END])) <= FLM_4x_BH_OVHD) { uiEndPos = FLM_4x_BH_OVHD; uiLFHCnt = 0; } else { if( uiEndPos > uiBlkSize) { uiEndPos = uiBlkSize; } uiLFHCnt = (FLMUINT)((uiEndPos - FLM_4x_BH_OVHD) / FLM_4x_LFH_SIZE); uiEndPos = (FLMUINT)(FLM_4x_BH_OVHD + uiLFHCnt * FLM_4x_LFH_SIZE); } // May allocate too many like the inactive ones but OK for now. // Allocate an additional 2 for the default data and dict containers. if( !uiEstCount) { uiEstCount = uiLFHCnt + uiLFileCnt; if( uiEstCount) { if( RC_BAD( rc = f_calloc( uiEstCount * sizeof( F_4x_LFILE), &pLFiles))) { goto Exit; } } } else if( uiLFHCnt) { uiEstCount += uiLFHCnt; if( RC_BAD(rc = f_recalloc( uiEstCount * sizeof( F_4x_LFILE), &pLFiles))) { goto Exit; } } // Read through all of the logical file definitions in the block for( ; uiPos < uiEndPos; uiPos += FLM_4x_LFH_SIZE) { FLMUINT uiLfNum; // Have to fix up the offsets later when they are read in TmpLFile.uiBlkAddress = uiBlkAddress; TmpLFile.uiOffsetInBlk = uiPos; if( (TmpLFile.uiLfType = (FLMUINT)pucBlk[ uiPos + FLM_4x_LFH_TYPE_OFFSET]) == FLM_4x_LF_INVALID) { TmpLFile.uiLfType = FLM_4x_LF_INVALID; continue; } TmpLFile.uiLfNum = (FLMUINT)FB2UW( &pucBlk[ uiPos + FLM_4x_LFH_LF_NUMBER_OFFSET]); TmpLFile.uiRootBlk = (FLMUINT)FB2UD( &pucBlk[ uiPos + FLM_4x_LFH_ROOT_BLK_OFFSET]); TmpLFile.uiNextDrn = (FLMUINT) FB2UD( &pucBlk[ uiPos + FLM_4x_LFH_NEXT_DRN_OFFSET]); uiLfNum = TmpLFile.uiLfNum; if( uiLfNum == FLM_4x_DATA_CONTAINER) { pLFile = pLFiles + FLM_4x_LFILE_DATA_CONTAINER_OFFSET; } else if( uiLfNum == FLM_4x_DICT_CONTAINER) { pLFile = pLFiles + FLM_4x_LFILE_DICT_CONTAINER_OFFSET; } else if( uiLfNum == FLM_4x_DICT_INDEX) { pLFile = pLFiles + FLM_4x_LFILE_DICT_INDEX_OFFSET; } else if( uiLfNum == FLM_4x_TRACKER_CONTAINER) { pLFile = pLFiles + FLM_4x_LFILE_TRACKER_CONTAINER_OFFSET; } else { pLFile = pLFiles + uiLFileCnt++; } f_memcpy( pLFile, &TmpLFile, sizeof( F_4x_LFILE)); } // Get the next block in the chain uiBlkAddress = (FLMUINT)FB2UD( &pucBlk[ FLM_4x_BH_NEXT_BLK]); } m_pLFileTbl = pLFiles; m_uiLFileCnt = uiLFileCnt; pLFiles = NULL; Exit: if( pBlock) { pBlock->Release(); } if( pLFiles) { f_free( &pLFiles); } return( rc ); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getLFile( FLMUINT uiLFile, F_4x_LFILE ** ppLFile) { RCODE rc = NE_XFLM_OK; F_4x_LFILE * pLFile = NULL; FLMUINT uiLoop; if( uiLFile == FLM_4x_DATA_CONTAINER) { pLFile = &m_pLFileTbl[ FLM_4x_LFILE_DATA_CONTAINER_OFFSET]; } else if( uiLFile == FLM_4x_DICT_CONTAINER) { pLFile = &m_pLFileTbl[ FLM_4x_LFILE_DICT_CONTAINER_OFFSET]; } else if( uiLFile == FLM_4x_TRACKER_CONTAINER) { pLFile = &m_pLFileTbl[ FLM_4x_LFILE_TRACKER_CONTAINER_OFFSET]; } else { for( uiLoop = 0; uiLoop < m_uiLFileCnt; uiLoop++) { if( m_pLFileTbl[ uiLoop].uiLfNum == uiLFile) { pLFile = &m_pLFileTbl[ uiLoop]; break; } } } if( !pLFile) { rc = RC_SET( NE_XFLM_BAD_COLLECTION); goto Exit; } *ppLFile = pLFile; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getNextDrn( FLMUINT uiContainer, FLMUINT * puiDrn) { RCODE rc = NE_XFLM_OK; BTSK stackBuf[ FLM_4x_BH_MAX_LEVELS ]; BTSK * pStack = stackBuf; FLMBYTE * pucElm; FLMBOOL bUsedStack = FALSE; F_4x_LFILE * pLFile; if( RC_BAD( rc = getLFile( uiContainer, &pLFile))) { goto Exit; } bUsedStack = TRUE; initStack( &stackBuf[ 0]); if( RC_BAD( rc = btSearchEnd( pLFile, FLM_4x_DRN_LAST_MARKER, &pStack))) { goto Exit; } if( pLFile->uiRootBlk == FLM_4x_BT_END) { *puiDrn = pLFile->uiNextDrn; } else { if( pLFile->uiLfNum != FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM])) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } pucElm = FLM_4x_CURRENT_ELM( pStack); pucElm += FLM_4x_BBE_GETR_KL( pucElm) + FLM_4x_BBE_KEY; *puiDrn = FB2UD( pucElm); } Exit: if( bUsedStack) { releaseStack( stackBuf); } return( rc); } /*************************************************************************** Desc: Search the right-most end of the b-tree. ****************************************************************************/ RCODE F_4xReader::btSearchEnd( F_4x_LFILE * pLFile, FLMUINT uiDrn, BTSK ** ppStack) { RCODE rc = NE_XFLM_OK; BTSK * pStack = *ppStack; FLMBYTE ucKey[ FLM_4x_DIN_KEY_SIZ]; FLMUINT uiBlkAddr; if( RC_BAD( rc = getRootBlock( pLFile, pStack))) { goto Exit; } longToByte( uiDrn, ucKey); for(;;) { if( pStack->uiLevel) { pStack->uiCurElm = pStack->uiBlkEnd; btPrevElm( pStack, pLFile); } else { if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) { if( RC_BAD( rc = btScan( pStack, ucKey))) { goto Exit; } } else { if( RC_BAD( rc = btScanNonLeafData( pStack, uiDrn))) { goto Exit; } } } if( !pStack->uiLevel) { break; } uiBlkAddr = childBlkAddr( pStack); pStack++; if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) { goto Exit; } } *ppStack = pStack; Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::readBlock( FLMUINT uiBlkAddr, F_Block ** ppBlock) { RCODE rc = NE_XFLM_OK; F_Block * pBlock = NULL; F_Block * pReuseBlock = NULL; F_Block ** ppTblSlot = NULL; if( *ppBlock) { (*ppBlock)->Release(); *ppBlock = NULL; } if( m_ppBlockTbl) { ppTblSlot = getHashBucket( uiBlkAddr); pBlock = *ppTblSlot; if( pBlock) { if( FLM_4x_GET_BH_ADDR( pBlock->m_pucBlk) != uiBlkAddr) { if( pBlock->getRefCount() == 1) { pReuseBlock = *ppTblSlot; } else { (*ppTblSlot)->Release(); } pBlock = NULL; *ppTblSlot = NULL; } else { pBlock->AddRef(); } } } if( !pBlock) { if( pReuseBlock) { pBlock = pReuseBlock; pReuseBlock = NULL; } else { if( (pBlock = f_new F_Block) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } if( RC_BAD( rc = pBlock->allocBlockBuf( m_fileHdr.uiBlockSize))) { goto Exit; } if( RC_BAD( rc = m_pSuperHdl->ReadBlock( uiBlkAddr, m_fileHdr.uiBlockSize, pBlock->m_pucBlk, NULL))) { if( rc == NE_XFLM_IO_END_OF_FILE) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Verify the block checksum if( RC_BAD( rc = blkCheckSum( pBlock->m_pucBlk, uiBlkAddr, m_fileHdr.uiBlockSize))) { goto Exit; } // See if we even got the block we thought we wanted if( FLM_4x_GET_BH_ADDR( pBlock->m_pucBlk) != uiBlkAddr) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } flmAssert( *ppTblSlot == NULL); *ppTblSlot = pBlock; pBlock->AddRef(); } flmAssert( *ppTblSlot == pBlock); *ppBlock = pBlock; pBlock = NULL; Exit: if( pBlock) { pBlock->Release(); } if( pReuseBlock) { pReuseBlock->Release(); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::blkCheckSum( FLMBYTE * pucBlkPtr, FLMUINT uiBlkAddress, FLMUINT uiBlkSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiCnt; FLMUINT uiAdds; FLMUINT uiXORs; FLMUINT uiCurrCheckSum; FLMUINT uiNewCheckSum; FLMUINT uiEncryptSize; FLMBYTE * pucSaveBlkPtr = pucBlkPtr; // Check the block length against the max. block size uiEncryptSize = (FLMUINT)getEncryptSize( pucBlkPtr); if( uiEncryptSize > uiBlkSize || uiEncryptSize < FLM_4x_BH_OVHD) { rc = RC_SET( NE_XFLM_BLOCK_CRC); goto Exit; } uiCurrCheckSum = (FLMUINT)(((FLMUINT)pucBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH] << 8) + (FLMUINT)pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW]); uiAdds = 0 - (pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] + pucBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]); uiXORs = pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] ^ pucBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]; if( uiBlkAddress != FLM_4x_BT_END) { uiAdds += (FLMBYTE)uiBlkAddress; uiXORs ^= (FLMBYTE)uiBlkAddress; } for( uiCnt = uiEncryptSize; uiCnt--;) { uiAdds += *pucBlkPtr; uiXORs ^= *(pucBlkPtr++); } uiNewCheckSum = (((uiAdds << 8) + uiXORs) & 0xFFFF); if( uiBlkAddress == FLM_4x_BT_END ) { FLMBYTE byXor; FLMBYTE byAdd; FLMBYTE byDelta; // If there is a one byte value that will satisfy both // sides of the checksum, the checksum is OK and that value // is the first byte value. byXor = (FLMBYTE) uiNewCheckSum; byAdd = (FLMBYTE) (uiNewCheckSum >> 8); byDelta = byXor ^ pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW]; // This is the big check, if byDelta is also what is // off with the add portion of the checksum, we have // a good value. if( ((FLMBYTE) (byAdd + byDelta)) == pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]) { // Set the low checksum value with the computed value. pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] = byDelta; goto Exit; } } else { // This has the side effect of setting the low block address byte // in the block thus getting rid of the low checksum byte. // // NOTE: We are allowing the case where the calculated checksum is // zero and the stored checksum is one because we used to change // a calculated zero to a one in old databases and store the one. // This is probably a somewhat rare case (1 out of 65536 checksums // will be zero), so forgiving it will be OK most of the time. // So that those don't cause us to report block checksum errors, // we just allow it - checksumming isn't a perfect check anyway. if( uiNewCheckSum == uiCurrCheckSum || ((!uiNewCheckSum) && (uiCurrCheckSum == 1))) { pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] = (FLMBYTE)uiBlkAddress; goto Exit; } } // Otherwise, we have a checksum error. rc = RC_SET( NE_XFLM_BLOCK_CRC); Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::retrieveRec( FLMUINT uiContainer, FLMUINT uiDrn, FLMUINT uiFlags, F_Record ** ppRecord) { RCODE rc = NE_XFLM_OK; BTSK stackBuf[ FLM_4x_BH_MAX_LEVELS]; BTSK * pStack = NULL; F_4x_LFILE * pLFile; initStack( &stackBuf[ 0]); pStack = stackBuf; if( uiDrn >= (FLM_4x_DRN_LAST_MARKER - 1)) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = getLFile( uiContainer, &pLFile))) { goto Exit; } if( uiFlags & XFLM_INCL) { // Search the for the record if( RC_BAD( rc = btSearch( pLFile, uiDrn, &pStack))) { goto Exit; } if( byteToLong( pStack->ucKeyBuf) == FLM_4x_DRN_LAST_MARKER) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } flmAssert( byteToLong( pStack->ucKeyBuf) >= uiDrn); } else if( uiFlags & XFLM_EXCL) { // Search the for the record if( RC_BAD( rc = btSearch( pLFile, uiDrn + 1, &pStack))) { goto Exit; } if( byteToLong( pStack->ucKeyBuf) == FLM_4x_DRN_LAST_MARKER) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } flmAssert( byteToLong( pStack->ucKeyBuf) > uiDrn); } else { // Search the for the record if( RC_BAD( rc = btSearch( pLFile, uiDrn, &pStack))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } goto Exit; } if( !pStack->uiKeyLen || byteToLong( pStack->ucKeyBuf) != uiDrn) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } } // Read the record if( RC_BAD( rc = readRecElements( pStack, pLFile, ppRecord))) { goto Exit; } Exit: releaseStack( stackBuf); return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_4xReader::retrieveNextRec( F_Record ** ppRecord) { RCODE rc = NE_XFLM_OK; FLMUINT uiDrn = 0; FLMUINT uiContainer = m_uiDefaultContainer; if( *ppRecord) { uiDrn = (*ppRecord)->getID(); uiContainer = (*ppRecord)->getContainerID(); } if( RC_BAD( rc = retrieveRec( uiContainer, uiDrn, XFLM_EXCL, ppRecord))) { goto Exit; } Exit: return( rc); } /************************************************************************** Desc: **************************************************************************/ void F_4xReader::releaseStack( BTSK * pStack) { FLMUINT uiNumLevels = FLM_4x_BH_MAX_LEVELS; while( uiNumLevels) { if( pStack->pBlk) { pStack->pBlk->Release(); pStack->pBlk = NULL; } uiNumLevels--; pStack++; } } /************************************************************************** Desc: **************************************************************************/ RCODE F_4xReader::readRecElements( BTSK * pStack, F_4x_LFILE * pLFile, F_Record ** ppRecord) { RCODE rc = NE_XFLM_OK; F_Record * pRecord = NULL; FLMBYTE * pucCurElm; void * pvMark = m_tmpPool.poolMark(); FLMUINT uiElmRecLen; FLMUINT uiFieldLen; FLMUINT uiFieldCount; FLMUINT uiTrueDataSpace; FLMUINT uiFieldPos; TFIELD * pField; FLDGROUP * pFldGroup = NULL; FLDGROUP * pFirstFldGroup = NULL; DATAPIECE * pDataPiece; LOCKED_BLOCK * pLockedBlock = NULL; FSTATE state; // Initialize variables state.uiLevel = 0; uiFieldCount = 0; uiTrueDataSpace = 0; uiFieldPos = NUM_FIELDS_IN_ARRAY; // Check to make sure we are positioned at the first element. pucCurElm = FLM_4x_CURRENT_ELM( pStack); if( !FLM_4x_BBE_IS_FIRST( pucCurElm)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Loop on each element in the record for( ;;) { // Setup all variables to process the current element uiElmRecLen = FLM_4x_BBE_GET_RL( pucCurElm); if( !uiElmRecLen) { rc = RC_SET( NE_XFLM_EOF_HIT); break; } pucCurElm += FLM_4x_BBE_REC_OFS( pucCurElm); state.uiPosInElm = 0; // Loop on each field within this element. while( state.uiPosInElm < uiElmRecLen) { state.pElement = pucCurElm; if( RC_BAD( rc = getFldOverhead( &state))) { goto Exit; } uiFieldLen = state.uiFieldLen; // Old record info data - skip past for now if( !state.uiTagNum) { state.uiPosInElm += uiFieldLen; continue; } if( !pRecord) { // Create a new data record or use the existing data record. if( *ppRecord) { // Reuse the existing F_Record object. pRecord = *ppRecord; *ppRecord = NULL; pRecord->clear(); } else { if( (pRecord = f_new F_Record) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } pRecord->setContainerID( FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM])); pRecord->setID( byteToLong( pStack->ucKeyBuf)); } // Check if out of fields in the tempoary field group if( uiFieldPos >= NUM_FIELDS_IN_ARRAY) { FLDGROUP * pTempFldGroup; uiFieldPos = 0; // Allocate the first field group from the pool. if( RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( FLDGROUP), (void **)&pTempFldGroup))) { goto Exit; } pTempFldGroup->pNext = NULL; if( pFldGroup) { pFldGroup->pNext = pTempFldGroup; } else { pFirstFldGroup = pTempFldGroup; } pFldGroup = pTempFldGroup; } flmAssert( state.uiFieldType != FLM_4x_UNKNOWN_TYPE); uiFieldCount++; pField = &pFldGroup->pFields[ uiFieldPos++]; pField->uiLevel = state.uiLevel; pField->uiFieldID = state.uiTagNum; pField->uiFieldType = state.uiFieldType; pField->uiFieldLen = state.uiFieldLen; pDataPiece = &pField->DataPiece; if( uiFieldLen) { FLMUINT uiDataPos = 0; if( state.uiFieldLen > 4) { // Binary data needs to account for alignment issues. if( state.uiFieldType == FLM_4x_BINARY_TYPE) { if( state.uiFieldLen >= 255) { // Align so that the data is aligned - not the length uiTrueDataSpace += 2; uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); uiTrueDataSpace -= 2; } else { uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); } } uiTrueDataSpace += state.uiFieldLen; // For read only records, greater than 255 bytes are // stored length preceded. if( state.uiFieldLen >= 255) { uiTrueDataSpace += 2; } } // Value may start in the next element. while( uiDataPos < uiFieldLen) { // Need to read next element for the value portion? if( state.uiPosInElm >= uiElmRecLen) { if( FLM_4x_BBE_IS_LAST( FLM_4x_CURRENT_ELM( pStack))) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // If we are going to the next block, lock down this block // beacause data pointers are pointing to it. if( RC_BAD( blkNextElm( pStack))) { LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; if( RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) { goto Exit; } pLockedBlock->pBlock = pStack->pBlk; pLockedBlock->pBlock->AddRef(); pLockedBlock->pNext = pLastLockedBlock; if( RC_BAD( rc = btNextElm( pStack, pLFile))) { goto Exit; } } pucCurElm = FLM_4x_CURRENT_ELM( pStack); uiElmRecLen = FLM_4x_BBE_GET_RL( pucCurElm); pucCurElm += FLM_4x_BBE_REC_OFS( pucCurElm); state.uiPosInElm = 0; } // Compare number of bytes left if value <= # bytes left in element if( (uiFieldLen - uiDataPos) <= (uiElmRecLen - state.uiPosInElm)) { FLMUINT uiDelta = uiFieldLen - uiDataPos; pDataPiece->pData = &pucCurElm[ state.uiPosInElm]; pDataPiece->uiLength = uiDelta; state.uiPosInElm += uiDelta; pDataPiece->pNext = NULL; break; } else { // Take what is there and get next element to grab some more. FLMUINT uiBytesToMove = uiElmRecLen - state.uiPosInElm; DATAPIECE * pNextDataPiece; pDataPiece->pData = &pucCurElm[ state.uiPosInElm]; pDataPiece->uiLength = uiBytesToMove; state.uiPosInElm += uiBytesToMove; uiDataPos += uiBytesToMove; if( RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( DATAPIECE), (void **)&pNextDataPiece))) { goto Exit; } pDataPiece->pNext = pNextDataPiece; pDataPiece = pNextDataPiece; } } } } // Done? if( FLM_4x_BBE_IS_LAST( FLM_4x_CURRENT_ELM( pStack))) { break; } // Position to next element if( RC_BAD( blkNextElm( pStack))) { LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; if( RC_BAD( rc = m_tmpPool.poolAlloc( sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) { goto Exit; } pLockedBlock->pBlock = pStack->pBlk; pLockedBlock->pBlock->AddRef(); pLockedBlock->pNext = pLastLockedBlock; if( RC_BAD( rc = btNextElm( pStack, pLFile))) { goto Exit; } } // Corruption Check. pucCurElm = FLM_4x_CURRENT_ELM( pStack); if( FLM_4x_BBE_IS_FIRST( pucCurElm)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } } if( pRecord) { void * pvField; if( RC_BAD( rc = pRecord->preallocSpace( uiTrueDataSpace))) { goto Exit; } pFldGroup = pFirstFldGroup; for( uiFieldPos = 0; uiFieldCount--; uiFieldPos++) { if( uiFieldPos >= NUM_FIELDS_IN_ARRAY) { uiFieldPos = 0; if( (pFldGroup = pFldGroup->pNext) == NULL) { break; } } pField = &pFldGroup->pFields[ uiFieldPos]; if( RC_BAD( rc = pRecord->insertLast( pField->uiLevel, pField->uiFieldID, pField->uiFieldType, &pvField))) { goto Exit; } if( pField->uiFieldLen) { FLMBYTE * pDataPtr; // Points to where the data will go. pDataPiece = &pField->DataPiece; pDataPtr = pRecord->getImportDataPtr( pvField, pField->uiFieldType, pField->uiFieldLen); if( !pDataPtr) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } do { f_memcpy( pDataPtr, pDataPiece->pData, pDataPiece->uiLength); pDataPtr += pDataPiece->uiLength; pDataPiece = pDataPiece->pNext; } while( pDataPiece); } } } if( *ppRecord) { flmAssert( 0); (*ppRecord)->Release(); } *ppRecord = pRecord; pRecord = NULL; Exit: // Release all locked down blocks except the current block. while( pLockedBlock) { pLockedBlock->pBlock->Release(); pLockedBlock = pLockedBlock->pNext; } m_tmpPool.poolReset( pvMark); if( pRecord) { pRecord->Release(); } return( rc); } /************************************************************************** Desc: **************************************************************************/ RCODE F_4xReader::getFldOverhead( FSTATE * pState) { RCODE rc = NE_XFLM_OK; FLMBYTE * pFieldOvhd = &pState->pElement[ pState->uiPosInElm]; FLMBYTE * pElement = pState->pElement; FLMBOOL bDoesntHaveFieldDef = TRUE; FLMUINT uiFieldLen; FLMUINT uiFieldType = 0; FLMUINT uiTagNum; FLMBYTE ucTemp; if( FLM_4x_FOP_IS_STANDARD( pFieldOvhd)) { if( FLM_4x_FSTA_LEVEL( pFieldOvhd)) { pState->uiLevel++; } uiFieldLen = FLM_4x_FSTA_FLD_LEN( pFieldOvhd); uiTagNum = FLM_4x_FSTA_FLD_NUM( pFieldOvhd); pFieldOvhd += FLM_4x_FSTA_OVHD; } else if( FLM_4x_FOP_IS_OPEN( pFieldOvhd)) { if( FLM_4x_FOPE_LEVEL( pFieldOvhd)) { pState->uiLevel++; } ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd++)); uiTagNum = (FLMUINT)*pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) { uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; } uiFieldLen = (FLMUINT) *pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) { uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; } } else if( FLM_4x_FOP_IS_NO_VALUE( pFieldOvhd)) { if( FLM_4x_FNOV_LEVEL( pFieldOvhd)) { pState->uiLevel++; } ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd++)); uiTagNum = (FLMUINT)*pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) { uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; } uiFieldLen = uiFieldType = 0; } else if( FLM_4x_FOP_IS_SET_LEVEL( pFieldOvhd)) { pState->uiLevel -= FLM_4x_FSLEV_GET( pFieldOvhd++); pState->uiPosInElm = (FLMUINT)( pFieldOvhd - pElement); rc = getFldOverhead( pState); goto Exit; } else if( FLM_4x_FOP_IS_TAGGED( pFieldOvhd)) { bDoesntHaveFieldDef = FALSE; if( FLM_4x_FTAG_LEVEL( pFieldOvhd)) { pState->uiLevel++; } ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd)); uiFieldType = (FLMUINT)(FLM_4x_FTAG_FLD_TYPE( pFieldOvhd)); pFieldOvhd += FLM_4x_FTAG_OVHD; uiTagNum = (FLMUINT) *pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) { uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; } uiTagNum ^= 0x8000; uiFieldLen = (FLMUINT)*pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) { uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; } } else if( FLM_4x_FOP_IS_RECORD_INFO( pFieldOvhd)) { bDoesntHaveFieldDef = FALSE; ucTemp = *pFieldOvhd++; uiFieldLen = *pFieldOvhd++; if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) { uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; } uiTagNum = 0; } else { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( bDoesntHaveFieldDef) { if( RC_BAD( rc = getFieldType( uiTagNum, &uiFieldType))) { goto Exit; } } pState->uiFieldType = uiFieldType; pState->uiFieldLen = uiFieldLen; pState->uiPosInElm = (FLMUINT)(pFieldOvhd - pElement); pState->uiTagNum = uiTagNum; Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ FLMUINT F_4xReader::childBlkAddr( BTSK * pStack) { FLMBYTE * pucChildBlkPtr; FLMUINT uiElmOvhd = pStack->uiElmOvhd; if( uiElmOvhd == FLM_4x_BNE_DATA_OVHD) { pucChildBlkPtr = FLM_4x_BLK_ELM_ADDR( pStack, pStack->uiCurElm + FLM_4x_BNE_DATA_CHILD_BLOCK); return( FB2UD( pucChildBlkPtr)); } else { // Corruption flmAssert( 0); return( 0); } } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::btSearch( F_4x_LFILE * pLFile, FLMUINT uiDrn, BTSK ** ppStack) { RCODE rc = NE_XFLM_OK; BTSK * pStack = *ppStack; FLMBYTE ucKey[ FLM_4x_DIN_KEY_SIZ]; FLMUINT uiBlkAddr; // Get the root block if( RC_BAD( rc = getRootBlock( pLFile, pStack))) { goto Exit; } longToByte( uiDrn, ucKey); // Read each block going down the b-tree. // Save state information in the stack. for(;;) { if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) { if( RC_BAD( rc = btScan( pStack, ucKey))) { goto Exit; } } else { if( RC_BAD( rc = btScanNonLeafData( pStack, uiDrn))) { goto Exit; } } if( !pStack->uiLevel) { break; } uiBlkAddr = childBlkAddr( pStack); pStack++; if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) { goto Exit; } } *ppStack = pStack; Exit: return( rc); } /*************************************************************************** Desc: Scan a b-tree block for a matching key at any b-tree block level. ****************************************************************************/ RCODE F_4xReader::btScan( BTSK * pStack, FLMBYTE * pucSearchKey) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucCurElm; FLMBYTE * pBlk; FLMBYTE * pucKeyBuf; FLMBYTE * pElmKey; FLMUINT uiRecLen = 0; FLMUINT uiPrevKeyCnt; FLMUINT uiElmKeyLen; FLMUINT uiBlkType; FLMUINT uiElmOvhd; FLMUINT uiBytesMatched; uiBlkType = pStack->uiBlkType; flmAssert( uiBlkType != FLM_4x_BHT_NON_LEAF_DATA); pucKeyBuf = pStack->ucKeyBuf; pBlk = pStack->pBlk->m_pucBlk; uiElmOvhd = pStack->uiElmOvhd; pStack->uiCurElm = FLM_4x_BH_OVHD; pStack->uiKeyLen = 0; pStack->uiPKC = 0; pStack->uiPrevElmPKC = 0; uiBytesMatched = 0; for( ;;) { pucCurElm = &pBlk[ pStack->uiCurElm]; uiElmKeyLen = FLM_4x_BBE_GETR_KL( pucCurElm); // Read in RAW mode - doesn't do all bit checking if( (uiPrevKeyCnt = (FLM_4x_BBE_GETR_PKC( pucCurElm))) > FLM_4x_BBE_PKC_MAX) { uiElmKeyLen += (uiPrevKeyCnt & FLM_4x_BBE_KL_HBITS) << FLM_4x_BBE_KL_SHIFT_BITS; uiPrevKeyCnt &= FLM_4x_BBE_PKC_MAX; } // Should not have a non-zero PKC if we are on the first element // of a block if( uiPrevKeyCnt && pStack->uiCurElm == FLM_4x_BH_OVHD) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Get the record portion length when on the leaf blocks. if( uiBlkType == FLM_4x_BHT_LEAF) { uiRecLen = FLM_4x_BBE_GET_RL( pucCurElm); } pStack->uiPrevElmPKC = pStack->uiPKC; // The zero length key is the terminating // element in a right-most block. if( (pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen) == 0) { pStack->uiPrevElmPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); pStack->uiPKC = 0; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // Handle special case of left-end compression maxing out. if( uiPrevKeyCnt == FLM_4x_BBE_PKC_MAX && FLM_4x_BBE_PKC_MAX < uiBytesMatched) { uiBytesMatched = FLM_4x_BBE_PKC_MAX; } // Check out this element to see if the key matches. if( uiPrevKeyCnt == uiBytesMatched) { pElmKey = &pucCurElm[ uiElmOvhd]; for(;;) { // All bytes of the search key are matched? if( uiBytesMatched == FLM_4x_DIN_KEY_SIZ) { pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); if( pStack->uiKeyLen != FLM_4x_DIN_KEY_SIZ) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Current key is equal to the search key. f_memcpy( pucKeyBuf, pucSearchKey, FLM_4x_DIN_KEY_SIZ); goto Exit; } if( uiBytesMatched == pStack->uiKeyLen) { pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); goto Next_Element; } // Compare the next byte in the search key and element if( pucSearchKey[ uiBytesMatched] != *pElmKey) { break; } uiBytesMatched++; pElmKey++; } pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); // Check if we are done comparing if( pucSearchKey[ uiBytesMatched] < *pElmKey) { if( uiBytesMatched) { flmAssert( uiBytesMatched <= FLM_4x_DIN_KEY_SIZ); f_memcpy( pucKeyBuf, pucSearchKey, uiBytesMatched); } flmAssert( pStack->uiKeyLen <= FLM_4x_DIN_KEY_SIZ); f_memcpy( &pucKeyBuf[ uiBytesMatched], pElmKey, pStack->uiKeyLen - uiBytesMatched); goto Exit; } } else if( uiPrevKeyCnt < uiBytesMatched) { // Current key > search key. Set pucKeyBuf and break out. pStack->uiPKC = uiPrevKeyCnt; if( uiPrevKeyCnt) { flmAssert( uiPrevKeyCnt <= FLM_4x_DIN_KEY_SIZ); f_memcpy( pucKeyBuf, pucSearchKey, uiPrevKeyCnt); } flmAssert( uiPrevKeyCnt + uiElmKeyLen <= FLM_4x_DIN_KEY_SIZ); f_memcpy( &pucKeyBuf[ uiPrevKeyCnt], &pucCurElm[ uiElmOvhd], uiElmKeyLen); if( byteToLong( pucKeyBuf) == FLM_4x_BT_END) { rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } Next_Element: // Position to the next element pStack->uiCurElm += uiElmKeyLen + ((uiBlkType == FLM_4x_BHT_LEAF ) ? (FLM_4x_BBE_KEY + uiRecLen) : (FLM_4x_BNE_IS_DOMAIN( pucCurElm) ? (FLM_4x_BNE_DOMAIN_LEN + uiElmOvhd) : uiElmOvhd)); // Most common check first. if( pStack->uiCurElm < pStack->uiBlkEnd) { continue; } if( pStack->uiCurElm == pStack->uiBlkEnd) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // Marched off the end of the block rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Binary search into a non-leaf data record block. ****************************************************************************/ RCODE F_4xReader::btScanNonLeafData( BTSK * pStack, FLMUINT uiDrn) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBlk = pStack->pBlk->m_pucBlk; FLMUINT uiLow = 0; FLMUINT uiMid; FLMUINT uiHigh = ((pStack->uiBlkEnd - FLM_4x_BH_OVHD) >> 3) - 1; FLMUINT uiTblSize = uiHigh; FLMUINT uiCurDrn; for(;;) { uiMid = (uiLow + uiHigh) >> 1; uiCurDrn = byteToLong( &pucBlk[ FLM_4x_BH_OVHD + (uiMid << 3)]); if( !uiCurDrn) { // Special case - at the end of a rightmost block. break; } if( uiDrn == uiCurDrn) { // Remember a data record can span multiple blocks (same DRN). while( uiMid) { uiCurDrn = byteToLong( &pucBlk[ FLM_4x_BH_OVHD + ((uiMid - 1) << 3)]); if( uiDrn != uiCurDrn) { break; } uiMid--; } break; } // Down to one item if too high then position to next item. if( uiLow >= uiHigh) { if( (uiDrn > uiCurDrn) && uiMid < uiTblSize) { uiMid++; } break; } // If too high then try lower section if( uiDrn < uiCurDrn) { // First item too high? if( !uiMid) { break; } uiHigh = uiMid - 1; } else { if( uiMid == uiTblSize) { uiMid++; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } uiLow = uiMid + 1; } } // Set curElm and the key buffer. pStack->uiCurElm = FLM_4x_BH_OVHD + (uiMid << 3); longToByte( uiCurDrn, pStack->ucKeyBuf); Exit: return( rc); } /**************************************************************************** Desc: Goto the next element within the block ****************************************************************************/ RCODE F_4xReader::blkNextElm( BTSK * pStack) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucElm; FLMUINT uiElmSize; pucElm = &pStack->pBlk->m_pucBlk[ pStack->uiCurElm]; if( pStack->uiBlkType == FLM_4x_BHT_LEAF) { uiElmSize = FLM_4x_BBE_LEN( pucElm); if( pStack->uiCurElm + FLM_4x_BBE_LEM_LEN < pStack->uiBlkEnd) { if( ((pStack->uiCurElm += uiElmSize) + FLM_4x_BBE_LEM_LEN < pStack->uiBlkEnd) == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } uiElmSize = FLM_4x_BNE_DATA_OVHD; if( pStack->uiCurElm < pStack->uiBlkEnd) { // Check if this is not the last element within the block if( (pStack->uiCurElm += uiElmSize) >= pStack->uiBlkEnd) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Go to the next element in the logical b-tree ****************************************************************************/ RCODE F_4xReader::btNextElm( BTSK * pStack, F_4x_LFILE * pLFile) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucCurElm; FLMUINT uiLFile; uiLFile = FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM]); if( pStack->uiCurElm < FLM_4x_BH_OVHD) { pStack->uiCurElm = FLM_4x_BH_OVHD; } else { if( RC_BAD( rc = blkNextElm( pStack))) { if( rc == NE_XFLM_EOF_HIT) { FLMBYTE * pucBlk = FLM_4x_BLK_ELM_ADDR( pStack, FLM_4x_BH_NEXT_BLK); FLMUINT uiBlkNum = FB2UD( pucBlk); if( uiBlkNum != FLM_4x_BT_END) { // Current element was last element in the block - goto next block */ if( RC_BAD( rc = getBlock( pLFile, uiBlkNum, pStack))) { goto Exit; } pucBlk = pStack->pBlk->m_pucBlk; pStack->uiBlkEnd = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END ]); pStack->uiCurElm = FLM_4x_BH_OVHD; btAdjustStack( pStack, pLFile, TRUE); } } } } pucCurElm = FLM_4x_CURRENT_ELM( pStack); // Copy the key f_memcpy( pStack->ucKeyBuf, pucCurElm, FLM_4x_DIN_KEY_SIZ); Exit: return(rc ); } /*************************************************************************** Desc: Go to the previous element in the logical b-tree ****************************************************************************/ RCODE F_4xReader::btPrevElm( BTSK * pStack, F_4x_LFILE * pLFile) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkAddr; FLMUINT uiTargetElm; FLMUINT uiPrevElm = 0; FLMUINT uiPrevKeyCnt = 0; FLMUINT uiElmKeyLen = 0; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMBYTE * pucCurElm; FLMBYTE * pucBlk; // Check if we are at or before the first element in the block if( pStack->uiCurElm <= FLM_4x_BH_OVHD) { pucBlk = pStack->pBlk->m_pucBlk; // We're at or before the first element, so read in the previous // block and go to the last element if( (uiBlkAddr = (FLMUINT)FB2UD( &pucBlk[ FLM_4x_BH_PREV_BLK])) == FLM_4x_BT_END) { // We are at the end rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } else { if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) { // Set uiBlkEnd and uiCurElm. // Adjust the parent block to the previous element pucBlk = pStack->pBlk->m_pucBlk; pStack->uiCurElm = pStack->uiBlkEnd; btAdjustStack( pStack, pLFile, FALSE); goto Exit; } } } // Move down 1 before the current element if( pStack->uiBlkType == FLM_4x_BHT_NON_LEAF_DATA) { pStack->uiCurElm -= FLM_4x_BNE_DATA_OVHD; pucBlk = pStack->pBlk->m_pucBlk; pucCurElm = &pucBlk[ pStack->uiCurElm]; f_memcpy( pStack->ucKeyBuf, pucCurElm, FLM_4x_DIN_KEY_SIZ); goto Exit; } // Set up to point to first element in the block uiTargetElm = pStack->uiCurElm; pStack->uiCurElm = FLM_4x_BH_OVHD; pucBlk = pStack->pBlk->m_pucBlk; while( pStack->uiCurElm < uiTargetElm) { pucCurElm = &pucBlk[ pStack->uiCurElm]; uiPrevKeyCnt = (FLMUINT)(FLM_4x_BBE_GET_PKC( pucCurElm)); uiElmKeyLen = (FLMUINT)(FLM_4x_BBE_GET_KL( pucCurElm)); if( uiElmKeyLen + uiPrevKeyCnt > FLM_4x_DIN_KEY_SIZ) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( uiElmKeyLen) { flmAssert( uiPrevKeyCnt + uiElmKeyLen <= FLM_4x_DIN_KEY_SIZ); f_memcpy( &pStack->ucKeyBuf[ uiPrevKeyCnt], &pucCurElm[ uiElmOvhd], uiElmKeyLen); } uiPrevElm = pStack->uiCurElm; if( RC_BAD( rc = blkNextElm( pStack))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } } pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen; pStack->uiCurElm = uiPrevElm; flmAssert( pStack->uiKeyLen == FLM_4x_DIN_KEY_SIZ); Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::btAdjustStack( BTSK * pStack, F_4x_LFILE * pLFile, FLMBOOL bMovedNext) { RCODE rc = NE_XFLM_OK; pStack--; if( RC_BAD( rc = getBlock( pLFile, pStack->uiBlkAddr, pStack))) { goto Exit; } if( bMovedNext) { if( RC_BAD( rc = btNextElm( pStack, pLFile))) { goto Exit; } } else { if( RC_BAD( rc = btPrevElm( pStack, pLFile))) { goto Exit; } } Exit: pStack++; return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getNameTable( F_4xNameTable ** ppNameTable) { RCODE rc = NE_XFLM_OK; FLMBOOL bAllocated = FALSE; if( !m_pNameTable) { if( (m_pNameTable = f_new F_4xNameTable) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } bAllocated = TRUE; if( RC_BAD( rc = m_pNameTable->setupNameTable( this))) { goto Exit; } } flmAssert( *ppNameTable == NULL); m_pNameTable->AddRef(); *ppNameTable = m_pNameTable; Exit: if( RC_BAD( rc) && bAllocated) { m_pNameTable->Release(); m_pNameTable = NULL; } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ F_Record::F_Record() { m_uiContainerID = 0; m_uiRecordID = 0; m_pool.poolInit( sizeof( m_fieldList)); m_pFirstFld = m_pLastFld = NULL; resetFieldList(); m_pDataBuf = NULL; m_uiDataBufOffset = 0; m_uiDataBufLength = 0; } /*************************************************************************** Desc: ****************************************************************************/ F_Record::~F_Record() { m_pool.poolFree(); if( m_pDataBuf) { f_free( &m_pDataBuf); } } /*************************************************************************** Desc: ****************************************************************************/ void F_Record::resetFieldList( void) { FLMUINT uiLoop; FIELD * pCurField; FIELD * pPrevField = NULL; for( uiLoop = 0; uiLoop < FLM_4x_FIELD_LIST_SIZE; uiLoop++) { pCurField = &m_fieldList[ uiLoop]; pCurField->ui16FieldID = 0xFFFF; pCurField->pPrev = pPrevField; pCurField->pNext = &m_fieldList[ uiLoop + 1]; pPrevField = pCurField; } pCurField->pNext = NULL; m_pAvailFld = &m_fieldList[ 0]; } /*************************************************************************** Desc: ****************************************************************************/ void F_Record::clear() { m_uiDataBufOffset = 0; resetFieldList(); m_pool.poolReset( NULL); m_pFirstFld = NULL; m_pLastFld = NULL; m_uiContainerID = 0; m_uiRecordID = 0; } /*************************************************************************** Desc: ****************************************************************************/ FIELD * F_Record::lastSubTreeField( FIELD * pField) { FIELD * pTempField = (FIELD *)lastChild( pField); FIELD * pLastChild = NULL; FLMUINT uiStartLevel = pField->ui8Level; // Step down through the tree for( ; pTempField && pTempField->ui8Level > uiStartLevel; pTempField = nextField( pTempField)) { pLastChild = pTempField; } return( pLastChild); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::insertLast( FLMUINT uiLevel, FLMUINT uiFieldID, FLMUINT uiDataType, void ** ppvField) { RCODE rc = NE_XFLM_OK; FIELD * pField = NULL; // Insert new field following current last field if( RC_BAD( rc = createField( m_pLastFld, &pField))) { goto Exit; } // Set up the new field and set as the current field pField->ui16FieldID = (FLMUINT16) uiFieldID; pField->ui8Level = (FLMUINT8) uiLevel; pField->ui8Type = (FLMUINT8) uiDataType; if( ppvField) { *ppvField = pField; } Exit: #ifdef FLM_DEBUG if ( pField) { flmAssert( pField->pNext != pField); flmAssert( pField->pPrev != pField); flmAssert( pField->ui16FieldID != 0xFFFF); } #endif return( rc); } /*************************************************************************** Desc: ****************************************************************************/ FIELD * F_Record::nextSiblingField( FIELD * pField) { FLMUINT8 ui8Level = pField->ui8Level; while( (pField = nextField( pField)) != NULL && pField->ui8Level > ui8Level) { ; } return( (pField && pField->ui8Level == ui8Level) ? pField : NULL); } /*************************************************************************** Desc: ****************************************************************************/ void * F_Record::prevSibling( void * pvField) { if( !pvField) { return( NULL); } FIELD * pField = (FIELD *)pvField; FLMUINT8 ui8Level = pField->ui8Level; while( (pField = prevField( pField)) != NULL && pField->ui8Level > ui8Level) { ; } return( (pField && pField->ui8Level == ui8Level) ? pField : NULL); } /*************************************************************************** Desc: ****************************************************************************/ void * F_Record::lastChild( void * pvField) { FIELD * pField = (FIELD *)pvField; FIELD * pLastField = NULL; if( !pField) { return( NULL); } for( pField = firstChildField( pField); pField; pField = nextSiblingField( pField)) { pLastField = pField; } return( pLastField); } /*************************************************************************** Desc: ****************************************************************************/ void * F_Record::parent( void * pvField) { FIELD * pField = (FIELD *) pvField; FLMUINT8 ui8Level; if( !pField) { return( NULL); } ui8Level = pField->ui8Level; while( (pField = prevField( pField)) != NULL && pField->ui8Level >= ui8Level) { ; } return( pField); } /*************************************************************************** Desc: ****************************************************************************/ FLMBYTE * F_Record::getImportDataPtr( void * pvField, FLMUINT uiDataType, FLMUINT uiLength) { FLMBYTE * pucData = NULL; getNewDataPtr( (FIELD *)pvField, uiDataType, uiLength, &pucData); ((FIELD *)pvField)->ui8Type = (FLMUINT8) uiDataType; return( pucData); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::preallocSpace( FLMUINT uiDataSize) { RCODE rc = NE_XFLM_OK; m_uiDataBufOffset = 0; if( m_uiDataBufLength >= uiDataSize) { goto Exit; } if( !m_pDataBuf) { m_uiDataBufLength = 0; if( RC_BAD( rc = f_alloc( uiDataSize, &m_pDataBuf))) { goto Exit; } m_uiDataBufLength = uiDataSize; } else { if( RC_BAD( rc = f_realloc( uiDataSize, &m_pDataBuf))) { goto Exit; } m_uiDataBufLength = uiDataSize; } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ void * F_Record::find( void * pvField, FLMUINT uiFieldID, FLMUINT uiOccur, FLMBOOL bSearchForest) { if( uiOccur == 0) { uiOccur = 1; } if( pvField) { FLMUINT uiStartLevel = ((FIELD *)pvField)->ui8Level; do { if( (uiFieldID == ((FIELD *)pvField)->ui16FieldID) && (--uiOccur < 1)) { return( pvField); } } while( (pvField = (FIELD *)(pvField ? ((FIELD *)pvField)->pNext : NULL)) != NULL && ((((FIELD *)pvField)->ui8Level > uiStartLevel) || (bSearchForest && ((FIELD *)pvField)->ui8Level == uiStartLevel))); } return( NULL); } /*************************************************************************** Desc: ****************************************************************************/ void * F_Record::find( void * pvField, FLMUINT * puiPathArray, FLMUINT uiOccur, FLMBOOL bSearchForest) { void * pvSaveField; FLMUINT * puiPath; FLMUINT uiLevel; // Handle empty record if( !pvField) { return( NULL); } if( !uiOccur) { uiOccur = 1; } uiLevel = ((FIELD *)pvField)->ui8Level; for(;;) { puiPath = puiPathArray + ( ((FIELD *)pvField)->ui8Level - uiLevel); pvSaveField = pvField; if( *puiPath == ((FIELD *)pvField)->ui16FieldID) { if( *(puiPath + 1) == 0 && (--uiOccur < 1)) { return( pvField); } // Go down level for rest of path if( ( pvField = firstChild( pvField)) != NULL) { continue; } pvField = pvSaveField; } // Find next sibling/uncle/end do { pvField = (FIELD *)(pvField ? ((FIELD *)pvField)->pNext : NULL); } while( pvField != NULL && ((FIELD *)pvField)->ui8Level > ((FIELD *)pvSaveField)->ui8Level); // Are we at the end? if( !pvField || ((FIELD *)pvField)->ui8Level < uiLevel || (bSearchForest && ((FIELD *)pvField)->ui8Level == uiLevel)) { break; } } return( NULL); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::createField( FIELD * pCurField, FIELD ** ppNewField) { RCODE rc = NE_XFLM_OK; FIELD * pNewField; if( m_pAvailFld) { pNewField = m_pAvailFld; m_pAvailFld = m_pAvailFld->pNext; if( m_pAvailFld) m_pAvailFld->pPrev = NULL; pNewField->pPrev = NULL; pNewField->pNext = NULL; } else { if( RC_BAD( rc = m_pool.poolAlloc( sizeof( FIELD), (void **)&pNewField))) { goto Exit; } } if( !pCurField && m_pLastFld) { pCurField = m_pLastFld; } if( pCurField) { pNewField->pNext = pCurField->pNext; pNewField->pPrev = pCurField; if( pCurField->pNext) { pCurField->pNext->pPrev = pNewField; } pCurField->pNext = pNewField; } if( !m_pFirstFld) { m_pFirstFld = pNewField; } if( !m_pLastFld || pCurField == m_pLastFld) { m_pLastFld = pNewField; } pNewField->uiDataLength = 0; pNewField->uiDataOffset = 0; *ppNewField = pNewField; Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getNewDataPtr( FIELD * pField, FLMUINT uiDataType, FLMUINT uiNewLength, FLMBYTE ** ppDataPtr) { RCODE rc = NE_XFLM_OK; FLMBYTE * pDataPtr; FLMUINT uiTemp; if( pField->uiDataLength) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } if( uiNewLength <= sizeof( FLMUINT)) { pDataPtr = (FLMBYTE *) &(pField->uiDataOffset); } else { // If this is a binary field it must start on an aligned byte. if( uiDataType == FLM_4x_BINARY_TYPE && (m_uiDataBufOffset & FLM_ALLOC_ALIGN) != 0) { uiTemp = (FLM_ALLOC_ALIGN + 1) - (m_uiDataBufOffset & FLM_ALLOC_ALIGN); m_uiDataBufOffset += uiTemp; } if( uiNewLength + m_uiDataBufOffset > m_uiDataBufLength) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } pDataPtr = m_pDataBuf + m_uiDataBufOffset; pField->uiDataOffset = m_uiDataBufOffset; m_uiDataBufOffset += uiNewLength; } pField->uiDataLength = uiNewLength; *ppDataPtr = pDataPtr; Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getINT( void * pvField, FLMINT * piNumber) { return( pvField ? storage2INT( getDataType( pvField), getDataLength( pvField), getDataPtr( (FIELD *)pvField), piNumber) : RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getUINT( void * pvField, FLMUINT * puiNumber) { return( pvField ? storage2UINT( getDataType( pvField), getDataLength( pvField), getDataPtr( (FIELD *)pvField), puiNumber) : RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getUINT32( void * pvField, FLMUINT32 * pui32Number) { return( pvField ? storage2UINT32( getDataType( pvField), getDataLength( pvField), getDataPtr( (FIELD *)pvField), pui32Number) : RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getUnicode( void * pvField, FLMUNICODE * pUnicode, FLMUINT * puiBufLen) { return( pvField ? getUnicode( getDataType( pvField), getDataLength( pvField), getDataPtr( (FIELD *) pvField), puiBufLen, pUnicode) : RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getNative( void * pvField, char * pszString, FLMUINT * puiBufLen) { return( pvField ? storage2Native( getDataType( pvField), getDataLength( pvField), getDataPtr( (FIELD *) pvField), puiBufLen, pszString) : RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getBinary( void * pvField, void * pvBuf, FLMUINT * puiBufLen) { if( pvField) { *puiBufLen = f_min( *puiBufLen, getDataLength( pvField)); f_memcpy( pvBuf, getDataPtr( (FIELD *)pvField), *puiBufLen); return( NE_XFLM_OK); } return( RC_SET( NE_XFLM_NOT_FOUND)); } /*************************************************************************** Desc: ****************************************************************************/ FLMBYTE * F_Record::getDataPtr( FIELD * pField) { if( !pField->uiDataLength) { return( NULL); } else if( pField->uiDataLength <= sizeof( FLMUINT)) { return (FLMBYTE *) &(pField->uiDataOffset); } return( m_pDataBuf + pField->uiDataOffset); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::storage2INT( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuf, FLMINT * piNum) { RCODE rc = NE_XFLM_OK; BCD_TYPE bcd = {0}; if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) { goto Exit; } if( bcd.bNegFlag) { *piNum = -((FLMINT)bcd.uiNum); if( !((bcd.uiNibCnt < 11) || (bcd.uiNibCnt == 11 && (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, gv_ucMinBcdINT32, 6) <= 0))))) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } } else { *piNum = (FLMINT)bcd.uiNum; if( !((bcd.uiNibCnt < 10) || (bcd.uiNibCnt == 10 && (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, gv_ucMaxBcdINT32, 5) <= 0))))) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::storage2UINT( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuf, FLMUINT * puiNum) { RCODE rc = NE_XFLM_OK; BCD_TYPE bcd = {0}; if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) { goto Exit; } *puiNum = bcd.uiNum; if( bcd.bNegFlag) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } else if( bcd.uiNibCnt == 10) { if( !(!bcd.pucPtr || (f_memcmp( bcd.pucPtr, gv_ucMaxBcdUINT32, 5) <= 0))) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } } else if( bcd.uiNibCnt > 10) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::storage2UINT32( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuf, FLMUINT32 * pui32Num) { RCODE rc = NE_XFLM_OK; BCD_TYPE bcd = {0}; if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) { goto Exit; } *pui32Num = (FLMUINT32)bcd.uiNum; if( bcd.bNegFlag) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } else if( bcd.uiNibCnt == 10) { if( !(!bcd.pucPtr || (f_memcmp( bcd.pucPtr, gv_ucMaxBcdUINT32, 5) <= 0))) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } } else if( bcd.uiNibCnt > 10) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::bcd2Num( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuf, BCD_TYPE * bcd) { RCODE rc = NE_XFLM_OK; if( !pBuf) { rc = RC_SET( NE_XFLM_CONV_NULL_SRC); goto Exit; } switch( uiType) { case FLM_4x_NUMBER_TYPE: { FLMUINT uiTotalNum = 0; FLMUINT uiByte; FLMUINT uiNibCnt; bcd->pucPtr = pBuf; // Get each nibble and use to create the number #define FLM_MAX_NIB_CNT 11 for( bcd->bNegFlag = (FLMBOOL)(uiNibCnt = ((*pBuf & 0xF0) == 0xB0) ? 1 : 0); uiNibCnt <= FLM_MAX_NIB_CNT; uiNibCnt++ ) { uiByte = (uiNibCnt & 0x01) ? (FLMUINT)(0x0F & *pBuf++) : (FLMUINT)(*pBuf >> 4); if( uiByte == 0x0F) { break; } // Multiply by 10 and add n // NOTE: 10y = 8y + 2y = (y << 3) + (y << 1) // faster than using the long multiply (10 * y) uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1) + uiByte; } bcd->uiNibCnt = uiNibCnt; bcd->uiNum = uiTotalNum; break; } case FLM_4x_TEXT_TYPE: { FLMUINT uiNumber = 0; // If it is a TEXT Value, convert to a numeric value // WARNING: The text is not null terminated while( uiBufLength--) { if( *pBuf < ASCII_ZERO || *pBuf > ASCII_NINE) { break; } uiNumber = (uiNumber * 10) + (*pBuf - ASCII_ZERO); pBuf++; } bcd->uiNum = uiNumber; bcd->uiNibCnt = 0; bcd->bNegFlag = FALSE; break; } case FLM_4x_CONTEXT_TYPE : { if( uiBufLength == sizeof( FLMUINT32)) { bcd->uiNum = (FLMUINT)( FB2UD( pBuf)); bcd->bNegFlag = 0; // Now set the uiNibCnt, the uiNibCnt will not be totally // accurate, but it's close enough to get the value out... 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 : { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Record::getUnicode( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuffer, FLMUINT * puiStrBufLen, FLMUNICODE * puzStrBuf) { RCODE rc = NE_XFLM_OK; FLMBYTE ucChar; FLMUINT uiBytesProcessed = 0; FLMUINT uiBytesOutput = 0; FLMBOOL bOutputData = FALSE; FLMUINT uiMaxOutLen; FLMBYTE ucObjType; FLMUINT uiObjLength = 0; FLMBYTE tempBuf[ 80]; FLMBYTE chrSet, chrVal; FLMUNICODE newChrVal; // If the value is a number, convert to text first if( uiType != FLM_4x_TEXT_TYPE) { if( !pBuffer) { uiBufLength = 0; } else { if( uiType == FLM_4x_NUMBER_TYPE) { uiBufLength = sizeof( tempBuf); if( RC_BAD( rc = numToText( pBuffer, tempBuf, &uiBufLength))) { goto Exit; } } else if( uiType == FLM_4x_TEXT_TYPE) { uiBufLength = sizeof( tempBuf); if( RC_BAD( rc = contextToText( pBuffer, tempBuf, &uiBufLength))) { goto Exit; } } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } pBuffer = &tempBuf[ 0]; } } uiMaxOutLen = *puiStrBufLen; if( puzStrBuf != NULL && uiMaxOutLen > 1) { bOutputData = TRUE; uiMaxOutLen -= 2; } // Parse through the string outputting data to the buffer as we go while( uiBytesProcessed < uiBufLength) { // Determine what we are pointing at ucChar = *pBuffer; ucObjType = (FLMBYTE)textObjType( ucChar); switch( ucObjType) { case FLM_4x_ASCII_CHAR_CODE: { uiObjLength = 1; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } *puzStrBuf++ = ucChar; } uiBytesOutput += 2; break; } case FLM_4x_CHAR_SET_CODE: { uiObjLength = 2; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } // Convert WP to UNICODE chrSet = ucChar & 0x3F; chrVal = *(pBuffer + 1); if( RC_BAD( rc = flmWPToUnicode( (((FLMUINT16)chrSet) << 8) | chrVal, &newChrVal))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } *puzStrBuf++ = newChrVal; } } uiBytesOutput += 2; break; } case FLM_4x_WHITE_SPACE_CODE: { uiObjLength = 1; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x0C)) { *puzStrBuf = 9; } else if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x0D)) { *puzStrBuf = 10; } else if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x07)) { *puzStrBuf = 13; } else { *puzStrBuf = 0x20; } puzStrBuf++; } uiBytesOutput += 2; break; } case FLM_4x_EXT_CHAR_CODE: { uiObjLength = 3; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } // Convert back from WP to UNICODE chrSet = *(pBuffer + 1); chrVal = *(pBuffer + 2); if( RC_BAD( rc = flmWPToUnicode( (((FLMUINT16)chrSet) << 8) | chrVal, &newChrVal))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } *puzStrBuf++ = newChrVal; } } uiBytesOutput += 2; break; } case FLM_4x_OEM_CODE: { uiObjLength = 2; break; } case FLM_4x_UNICODE_CODE: { uiObjLength = 3; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } *puzStrBuf++ = (*(pBuffer + 1) << 8) + *(pBuffer + 2); } uiBytesOutput += 2; break; } case FLM_4x_UNK_EQ_1_CODE: { uiObjLength = 2; if( bOutputData) { if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto GetUNICODE_Output; } *puzStrBuf++ = *(pBuffer+1); } uiBytesOutput += 2; break; } default: { flmAssert( 0); uiBytesProcessed = uiBufLength; break; } } pBuffer += uiObjLength; uiBytesProcessed += uiObjLength; } GetUNICODE_Output: if( bOutputData) { *puzStrBuf = 0; } *puiStrBufLen = uiBytesOutput; Exit: return( rc); } /*************************************************************************** Desc: Convert a storage text string into a native string ***************************************************************************/ RCODE F_Record::storage2Native( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuffer, FLMUINT * puiOutBufLenRV, char * pOutBuffer) { RCODE rc = NE_XFLM_OK; FLMBYTE * ptr = pBuffer; FLMBYTE * pucOut; FLMBYTE ucChar; FLMUINT uiBytesProcessed; FLMUINT uiBytesOutput; FLMUINT uiValLength = uiBufLength; FLMBOOL bOutputData = FALSE; FLMUINT uiMaxOutLen = 0; FLMBYTE ucObjType; FLMUINT uiObjLength = 0; FLMBYTE TempBuf[ 80]; // If needed, try and convert the data to text if( uiType != FLM_4x_TEXT_TYPE) { if( !ptr) { uiValLength = 0; } else if( uiType == FLM_4x_NUMBER_TYPE) { uiValLength = sizeof( TempBuf); if( RC_BAD( rc = numToText( ptr, TempBuf, &uiValLength))) { goto Exit; } ptr = &TempBuf[ 0]; } else if( uiType == FLM_4x_CONTEXT_TYPE) { uiValLength = sizeof( TempBuf); if( RC_BAD( rc = contextToText( ptr, TempBuf, &uiValLength))) { goto Exit; } ptr = &TempBuf[ 0]; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } if( pOutBuffer != NULL && *puiOutBufLenRV) { bOutputData = TRUE; uiMaxOutLen = *puiOutBufLenRV - 1; } uiBytesProcessed = 0; uiBytesOutput = 0; pucOut = (FLMBYTE *)pOutBuffer; // Parse through the string outputting data to the buffer // as we go while( uiBytesProcessed < uiValLength) { // Determine what we are pointing at ucChar = *ptr; ucObjType = (FLMBYTE)textObjType( ucChar); switch( ucObjType) { case FLM_4x_ASCII_CHAR_CODE: { uiObjLength = 1; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) *pucOut++ = f_tonative( ucChar); else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } case FLM_4x_CHAR_SET_CODE: { uiObjLength = 2; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { if( (ucChar & (~ucObjType)) == 0) { *pucOut++ = f_tonative( *(ptr + 1)); } else { *pucOut++ = 0xFF; } } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } case FLM_4x_WHITE_SPACE_CODE: { uiObjLength = 1; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { ucChar &= (~FLM_4x_WHITE_SPACE_MASK); if( (ucChar == 0x03) || (ucChar == 0x04) || (ucChar == 0x05)) { ucChar = ASCII_DASH; } else if( ucChar == 0x0C) { ucChar = ASCII_TAB; } else if( ucChar == 0x0D) { ucChar = 0x0A; } else if( ucChar == 0x07) { ucChar = 0x0D; } else { ucChar = 0x20; } *pucOut++ = f_tonative( ucChar); } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } case FLM_4x_UNK_EQ_1_CODE: { uiObjLength = 2; // Skip it if it is not a NATIVE code if( (ucChar & (~ucObjType)) == 0x02) { if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { *pucOut++ = f_tonative( *(ptr + 1)); } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; } break; } case FLM_4x_EXT_CHAR_CODE: { uiObjLength = 3; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { *pucOut += 0xFF; } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } case FLM_4x_OEM_CODE: { uiObjLength = 2; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { *pucOut++ = f_tonative( *(ptr + 1)); } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } case FLM_4x_UNICODE_CODE: { uiObjLength = 3; if( bOutputData) { if( uiBytesOutput < uiMaxOutLen ) { *pucOut++ = 0xFF; } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Native_Output; } } uiBytesOutput++; break; } default: { flmAssert( 0); break; } } ptr += uiObjLength; uiBytesProcessed += uiObjLength; } // Add a terminating NULL character, but DO NOT increment the // uiBytesOutput counter Native_Output: if( bOutputData) { *pucOut = 0; } *puiOutBufLenRV = uiBytesOutput; Exit: return( rc); } /*************************************************************************** Desc: Convert a storage number into a storage text string ***************************************************************************/ RCODE F_Record::numToText( FLMBYTE * pucNum, FLMBYTE * pucOutBuffer, FLMUINT * puiBufLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucOutput; FLMBYTE ucChar; FLMBYTE ucOutChar; FLMUINT uiBytesOutput; FLMBOOL bOutputData; FLMUINT uiMaxOutLen; FLMBOOL bFirstNibble; uiMaxOutLen = *puiBufLen; bOutputData = ((pucOutBuffer != NULL) && uiMaxOutLen) ? TRUE : FALSE; uiBytesOutput = 0; pucOutput = pucOutBuffer; // Parse through the string outputting data to the buffer // as we go if( !pucNum) { goto Exit; } bFirstNibble = TRUE; for( ;;) { if( bFirstNibble) { ucChar = (FLMBYTE)(*pucNum >> 4); } else { ucChar = (FLMBYTE)(*pucNum++ & 0x0F); } bFirstNibble = !bFirstNibble; if( ucChar <= 9) { ucOutChar = (FLMBYTE)( ASCII_ZERO + ucChar); } else if( ucChar == 0x0F) { break; } else { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( bOutputData) { if( uiBytesOutput < uiMaxOutLen) { *pucOutput++ = ucOutChar; } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } } uiBytesOutput++; } Exit: *puiBufLen = uiBytesOutput; return( rc); } /*************************************************************************** Desc: Convert a context value into a storage text string ***************************************************************************/ RCODE F_Record::contextToText( FLMBYTE * pucValue, FLMBYTE * pucOutBuffer, FLMUINT * puiBufLen) { RCODE rc = NE_XFLM_OK; FLMUINT uiMaxOutLen; FLMUINT uiStrLen; FLMUINT uiBytesOutput = 0; FLMBOOL bOutputData; FLMBYTE ucTmpBuf[ 32]; uiMaxOutLen = *puiBufLen; bOutputData = ((pucOutBuffer != NULL) && uiMaxOutLen) ? TRUE : FALSE; if( !pucValue) { goto Exit; } f_sprintf( (char *)ucTmpBuf, "%u", FB2UD( pucValue)); uiStrLen = f_strlen( ucTmpBuf) + 1; uiBytesOutput = f_min( uiStrLen, uiMaxOutLen); if( bOutputData) { f_memcpy( pucOutBuffer, ucTmpBuf, uiBytesOutput); pucOutBuffer[ uiBytesOutput - 1] = 0; } if( uiMaxOutLen < uiBytesOutput) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } Exit: *puiBufLen = uiBytesOutput; return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getRootBlock( F_4x_LFILE * pLFile, BTSK * pStack) { RCODE rc = NE_XFLM_OK; F_Block * pBlock = NULL; if( RC_BAD( rc = readBlock( pLFile->uiRootBlk, &pBlock))) { goto Exit; } if( !(FLM_4x_BH_IS_ROOT_BLK( pBlock->m_pucBlk)) || (pLFile->uiLfNum != FB2UW( &pBlock->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM]))) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Set up the stack blkToStack( &pBlock, pStack); Exit: if( pBlock) { pBlock->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getBlock( F_4x_LFILE * pLFile, FLMUINT uiBlkAddr, BTSK * pStack) { RCODE rc = NE_XFLM_OK; F_Block * pBlock = NULL; if( RC_BAD( rc = readBlock( uiBlkAddr, &pBlock))) { goto Exit; } if( pLFile->uiLfNum != FB2UW( &pBlock->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM])) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Set up the stack blkToStack( &pBlock, pStack); Exit: if( pBlock) { pBlock->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void F_4xReader::blkToStack( F_Block ** ppBlock, BTSK * pStack) { FLMBYTE * pucBlk = (*ppBlock)->m_pucBlk; FLMUINT uiBlkType; uiBlkType = (FLMUINT)(FLM_4x_BH_GET_TYPE( pucBlk)); pStack->uiBlkType = uiBlkType; if( uiBlkType == FLM_4x_BHT_LEAF) { pStack->uiElmOvhd = FLM_4x_BBE_KEY; } else if( uiBlkType == FLM_4x_BHT_NON_LEAF_DATA) { pStack->uiElmOvhd = FLM_4x_BNE_DATA_OVHD; } else if( uiBlkType == FLM_4x_BHT_NON_LEAF) { pStack->uiElmOvhd = FLM_4x_BNE_KEY_START; } else if( uiBlkType == FLM_4x_BHT_NON_LEAF_COUNTS) { pStack->uiElmOvhd = FLM_4x_BNE_KEY_COUNTS_START; } else { flmAssert( 0); pStack->uiElmOvhd = FLM_4x_BNE_KEY_START; } pStack->uiKeyLen = 0; pStack->uiPKC = 0; pStack->uiPrevElmPKC = 0; pStack->uiCurElm = FLM_4x_BH_OVHD; pStack->uiBlkEnd = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END]); pStack->uiLevel = (FLMUINT)pucBlk[ FLM_4x_BH_LEVEL ]; if( pStack->pBlk) { pStack->pBlk->Release(); } pStack->pBlk = *ppBlock; *ppBlock = NULL; } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getFieldType( FLMUINT uiFieldNum, FLMUINT * puiType) { RCODE rc = NE_XFLM_OK; if( uiFieldNum < m_uiFieldTblSize) { if( (*puiType = m_puiFieldTbl[ uiFieldNum - 1]) == FLM_4x_UNKNOWN_TYPE) { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } } else { // Check if the field is a FLAIM dictionary field. // Most of these fields are TEXT fields. if( (uiFieldNum >= FLM_4x_DICT_FIELD_NUMS) && (uiFieldNum <= FLM_4x_LAST_DICT_FIELD_NUM)) { *puiType = FLM_4x_TEXT_TYPE; } else if( uiFieldNum >= FLM_4x_UNREGISTERED_TAGS) { *puiType = FLM_4x_TEXT_TYPE; } else { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::readDictionary( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiType; FLMUINT uiFieldNum; F_Record * pRec = NULL; void * pvField; if( m_puiFieldTbl) { f_free( &m_puiFieldTbl); } m_uiFieldTblSize = 0; if( RC_BAD( rc = f_alloc( FLM_4x_RESERVED_TAG_NUMS, &m_puiFieldTbl))) { goto Exit; } m_uiFieldTblSize = FLM_4x_RESERVED_TAG_NUMS; f_memset( m_puiFieldTbl, FLM_4x_UNKNOWN_TYPE, m_uiFieldTblSize); setDefaultContainer( FLM_4x_DICT_CONTAINER); for( ;;) { if( RC_BAD( rc = retrieveNextRec( &pRec))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } if( pRec->getFieldID( pRec->root()) == FLM_4x_FIELD_TAG) { pvField = pRec->firstChild( pRec->root()); if( RC_BAD( rc = getTypeTag( pRec, pvField, &uiType))) { goto Exit; } uiFieldNum = pRec->getID(); flmAssert( uiFieldNum < FLM_4x_RESERVED_TAG_NUMS); m_puiFieldTbl[ uiFieldNum - 1] = uiType; } } Exit: if( pRec) { pRec->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_4xReader::getTypeTag( F_Record * pRec, void * pvField, FLMUINT * puiType) { RCODE rc = NE_XFLM_OK; FLMUINT uiType; FLMUINT uiBufLen; char szTmpBuf[ 64]; uiBufLen = sizeof( szTmpBuf); if( RC_BAD( rc = pRec->getNative( pvField, szTmpBuf, &uiBufLen))) { goto Exit; } if( f_strnicmp( szTmpBuf, "text", 4) == 0) { uiType = FLM_4x_TEXT_TYPE; } else if( f_strnicmp( szTmpBuf, "numb", 4) == 0) { uiType = FLM_4x_NUMBER_TYPE; } else if( f_strnicmp( szTmpBuf, "bina", 4) == 0) { uiType = FLM_4x_BINARY_TYPE; } else if( f_strnicmp( szTmpBuf, "cont", 4) == 0) { uiType = FLM_4x_CONTEXT_TYPE; } else if( f_strnicmp( szTmpBuf, "blob", 4) == 0) { uiType = FLM_4x_BLOB_TYPE; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } *puiType = uiType; Exit: return( rc); } /**************************************************************************** Desc: Constructor ****************************************************************************/ F_4xNameTable::F_4xNameTable() { m_pool.poolInit( 1024); m_ppSortedByTagName = NULL; m_ppSortedByTagNum = NULL; m_ppSortedByTagTypeAndName = NULL; m_uiTblSize = 0; m_uiNumTags = 0; m_bTablesSorted = FALSE; } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_4xNameTable::~F_4xNameTable() { clearTable(); m_pool.poolFree(); } /**************************************************************************** Desc: Free everything in the table ****************************************************************************/ void F_4xNameTable::clearTable( void) { m_pool.poolFree(); m_pool.poolInit( 1024); // NOTE: Only one allocation is used for m_ppSortedByTagName, // m_ppSortedByTagNum, and m_ppSortedByTagTypeAndName - there is no // need to free m_ppSortedByTagNum and m_ppSortedByTagTypeAndName. if (m_ppSortedByTagName) { f_free( &m_ppSortedByTagName); m_ppSortedByTagNum = NULL; m_ppSortedByTagTypeAndName = NULL; m_uiTblSize = 0; m_uiNumTags = 0; } } /**************************************************************************** Desc: Compare two tag names. Name1 can be NATIVE or UNICODE. If a non-NULL UNICODE string is passed, it will be used. Otherwise, the NATIVE string will be used. Note: Comparison is case insensitive for the ASCII characters A-Z. ****************************************************************************/ FLMINT F_4xNameTable::tagNameCompare( const FLMUNICODE * puzName1, // If NULL, use pszName1 for comparison const char * pszName1, const FLMUNICODE * puzName2) { FLMUNICODE uzChar1; FLMUNICODE uzChar2; if (puzName1) { for (;;) { uzChar1 = *puzName1; uzChar2 = *puzName2; // Convert to lower case for comparison. if (uzChar1 >= 'A' && uzChar1 <= 'Z') { uzChar1 = uzChar1 - 'A' + 'a'; } if (uzChar2 >= 'A' && uzChar2 <= 'Z') { uzChar2 = uzChar2 - 'A' + 'a'; } if (!uzChar1 || !uzChar2 || uzChar1 != uzChar2) { break; } puzName1++; puzName2++; } } else { for (;;) { uzChar1 = (FLMUNICODE)*pszName1; uzChar2 = *puzName2; // Convert to lower case for comparison. if (uzChar1 >= 'A' && uzChar1 <= 'Z') { uzChar1 = uzChar1 - 'A' + 'a'; } if (uzChar2 >= 'A' && uzChar2 <= 'Z') { uzChar2 = uzChar2 - 'A' + 'a'; } if (!uzChar1 || !uzChar2 || uzChar1 != uzChar2) { break; } pszName1++; puzName2++; } } if (uzChar1) { return( (FLMINT)((uzChar2 && uzChar1 < uzChar2) ? (FLMINT)-1 : (FLMINT)1)); } else if (uzChar2) { return( -1); } else { return( 0); } } /**************************************************************************** Desc: Lookup a tag by tag name. Tag name is passed in as a UNICODE string or a NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLM_4x_TAG_INFO * F_4xNameTable::findTagByName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT * puiInsertPos) { FLM_4x_TAG_INFO * pTagInfo = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMINT iCmp; // Do binary search in the table if ((uiTblSize = m_uiNumTags) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; iCmp = tagNameCompare( puzTagName, pszTagName, m_ppSortedByTagName [uiMid]->puzTagName); if (iCmp == 0) { // Found Match pTagInfo = m_ppSortedByTagName [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (iCmp < 0) ? uiMid : uiMid + 1; } goto Exit; } if (iCmp < 0) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pTagInfo); } /**************************************************************************** Desc: Lookup a tag by tag number. ****************************************************************************/ FLM_4x_TAG_INFO * F_4xNameTable::findTagByNum( FLMUINT uiTagNum, FLMUINT * puiInsertPos) { FLM_4x_TAG_INFO * pTagInfo = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblTagNum; // Do binary search in the table if ((uiTblSize = m_uiNumTags) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblTagNum = m_ppSortedByTagNum [uiMid]->uiTagNum; if (uiTagNum == uiTblTagNum) { // Found Match pTagInfo = m_ppSortedByTagNum [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (uiTagNum < uiTblTagNum) ? uiMid : uiMid + 1; } goto Exit; } if (uiTagNum < uiTblTagNum) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pTagInfo); } /**************************************************************************** Desc: Lookup a tag by tag type and tag name. Tag name is passed in as a UNICODE string or a NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLM_4x_TAG_INFO * F_4xNameTable::findTagByTypeAndName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiType, FLMUINT * puiInsertPos) { FLM_4x_TAG_INFO * pTagInfo = NULL; FLMUINT uiTblType; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMINT iCmp; // Do binary search in the table if ((uiTblSize = m_uiNumTags) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblType = m_ppSortedByTagTypeAndName [uiMid]->uiType; if (uiType < uiTblType) { iCmp = -1; } else if (uiType > uiTblType) { iCmp = 1; } else if ((iCmp = tagNameCompare( puzTagName, pszTagName, m_ppSortedByTagTypeAndName [uiMid]->puzTagName)) == 0) { // Found Match pTagInfo = m_ppSortedByTagTypeAndName [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (iCmp < 0) ? uiMid : uiMid + 1; } goto Exit; } if (iCmp < 0) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pTagInfo); } /**************************************************************************** Desc: Copy a tag name to a UNICODE or NATIVE buffer. Truncate if necessary. If a non-NULL UNICODE string is passed in, it will be populated. Otherwise, the NATIVE string will be populated. ****************************************************************************/ void F_4xNameTable::copyTagName( FLMUNICODE * puzDestTagName, char * pszDestTagName, FLMUINT uiDestBufSize, // Bytes, must be enough for null terminator FLMUNICODE * puzSrcTagName) { if (puzDestTagName) { // Decrement name buffer size by sizeof( FLMUNICODE) to allow for a // terminating NULL character. uiDestBufSize better be at list big // enough for a null terminating character. flmAssert( uiDestBufSize >= sizeof( FLMUNICODE)); uiDestBufSize -= sizeof( FLMUNICODE); // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters // will be returned as question marks (?). while (uiDestBufSize >= sizeof( FLMUNICODE) && *puzSrcTagName) { *puzDestTagName++ = *puzSrcTagName; uiDestBufSize -= sizeof( FLMUNICODE); puzSrcTagName++; } *puzDestTagName = 0; } else { // Decrement name buffer size by one to allow for a terminating // NULL character. uiDestBufSize better be at list big // enough for a null terminating character. flmAssert( uiDestBufSize); uiDestBufSize--; // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters // will be returned as question marks (?). while (uiDestBufSize && *puzSrcTagName) { if (*puzSrcTagName <= 127) { *pszDestTagName++ = (char)*puzSrcTagName; } else { *pszDestTagName++ = '?'; } uiDestBufSize--; puzSrcTagName++; } *pszDestTagName = 0; } } /*************************************************************************** Desc: Sort an array of SCACHE pointers by their block address. ****************************************************************************/ void F_4xNameTable::sortTagTbl( FLM_4x_TAG_INFO ** ppTagInfoTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, FLM_4x_TAG_COMPARE_FUNC fnTagCompare) { FLMUINT uiLBPos; FLMUINT uiUBPos; FLMUINT uiMIDPos; FLMUINT uiLeftItems; FLMUINT uiRightItems; FLM_4x_TAG_INFO * pCurTagInfo; FLMINT iCompare; Iterate_Larger_Half: uiUBPos = uiUpperBounds; uiLBPos = uiLowerBounds; uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; pCurTagInfo = ppTagInfoTbl [uiMIDPos ]; for (;;) { while (uiLBPos == uiMIDPos || // Don't compare with target ((iCompare = fnTagCompare( ppTagInfoTbl [uiLBPos], pCurTagInfo)) < 0)) { if (uiLBPos >= uiUpperBounds) { break; } uiLBPos++; } while (uiUBPos == uiMIDPos || // Don't compare with target (((iCompare = fnTagCompare( pCurTagInfo, ppTagInfoTbl [uiUBPos])) < 0))) { if (!uiUBPos) { break; } uiUBPos--; } if (uiLBPos < uiUBPos ) // Interchange and continue loop. { // Exchange [uiLBPos] with [uiUBPos]. tagInfoSwap( ppTagInfoTbl, uiLBPos, uiUBPos); uiLBPos++; // Scan from left to right. uiUBPos--; // Scan from right to left. } else // Past each other - done { break; } } // Check for swap( LB, MID ) - cases 3 and 4 if( uiLBPos < uiMIDPos ) { // Exchange [uiLBPos] with [uiMIDPos] tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiLBPos); uiMIDPos = uiLBPos; } else if( uiMIDPos < uiUBPos ) { // Exchange [uUBPos] with [uiMIDPos] tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiUBPos); uiMIDPos = uiUBPos; } // Check the left piece. uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) ? uiMIDPos - uiLowerBounds // 2 or more : 0; uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) ? uiUpperBounds - uiMIDPos // 2 or more : 0; if( uiLeftItems < uiRightItems ) { // Recurse on the LEFT side and goto the top on the RIGHT side. if (uiLeftItems ) { sortTagTbl( ppTagInfoTbl, uiLowerBounds, uiMIDPos - 1, fnTagCompare); } uiLowerBounds = uiMIDPos + 1; goto Iterate_Larger_Half; } else if (uiLeftItems ) // Compute a truth table to figure out this check. { // Recurse on the RIGHT side and goto the top for the LEFT side. if (uiRightItems ) { sortTagTbl( ppTagInfoTbl, uiMIDPos + 1, uiUpperBounds, fnTagCompare); } uiUpperBounds = uiMIDPos - 1; goto Iterate_Larger_Half; } } /**************************************************************************** Desc: Allocate a new tag info structure and set it up. ****************************************************************************/ RCODE F_4xNameTable::allocTag( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLM_4x_TAG_INFO ** ppTagInfo) { RCODE rc = NE_XFLM_OK; void * pvMark; FLM_4x_TAG_INFO * pTagInfo; FLMUINT uiNameSize; FLMUNICODE * puzTmp; // Create a new tag info structure. pvMark = m_pool.poolMark(); if( RC_BAD( rc = m_pool.poolAlloc( sizeof( FLM_4x_TAG_INFO), (void **)&pTagInfo))) { goto Exit; } // Allocate the space for the tag name. if (puzTagName) { uiNameSize = (f_unilen( puzTagName) + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = m_pool.poolAlloc( uiNameSize, (void **)&pTagInfo->puzTagName))) { goto Exit; } f_memcpy( pTagInfo->puzTagName, puzTagName, uiNameSize); } else { uiNameSize = (f_strlen( pszTagName) + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = m_pool.poolAlloc( uiNameSize, (void **)&pTagInfo->puzTagName))) { goto Exit; } puzTmp = pTagInfo->puzTagName; while (*pszTagName) { *puzTmp++ = (FLMUNICODE)*pszTagName; pszTagName++; } *puzTmp = 0; } pTagInfo->uiTagNum = uiTagNum; pTagInfo->uiType = uiType; pTagInfo->uiSubType = uiSubType; Exit: if (RC_BAD( rc)) { m_pool.poolReset( pvMark); pTagInfo = NULL; } *ppTagInfo = pTagInfo; return( rc); } /**************************************************************************** Desc: Allocate the sort tables. ****************************************************************************/ RCODE F_4xNameTable::reallocSortTables( FLMUINT uiNewTblSize) { RCODE rc = NE_XFLM_OK; FLM_4x_TAG_INFO ** ppNewTbl; if( RC_BAD( rc = f_alloc( sizeof( FLM_4x_TAG_INFO *) * uiNewTblSize * 3, &ppNewTbl))) { goto Exit; } // Copy the old tables into the new. if (m_uiNumTags) { f_memcpy( ppNewTbl, m_ppSortedByTagName, sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); f_memcpy( &ppNewTbl [uiNewTblSize], m_ppSortedByTagNum, sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); f_memcpy( &ppNewTbl [uiNewTblSize + uiNewTblSize], m_ppSortedByTagTypeAndName, sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); f_free( &m_ppSortedByTagName); } m_ppSortedByTagName = ppNewTbl; m_ppSortedByTagNum = &ppNewTbl [uiNewTblSize]; m_ppSortedByTagTypeAndName = &ppNewTbl [uiNewTblSize + uiNewTblSize]; m_uiTblSize = uiNewTblSize; Exit: return( rc); } /**************************************************************************** Desc: Get a tag name, number, etc. using tag number ordering. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getNextTagNumOrder( FLMUINT * puiNextPos, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiTagNum, // May be NULL FLMUINT * puiType, // May be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo = NULL; if (!m_bTablesSorted) { sortTags(); } if (*puiNextPos < m_uiNumTags) { pTagInfo = m_ppSortedByTagNum [*puiNextPos]; if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if (puiType) { *puiType = pTagInfo->uiType; } if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } if( puzTagName || pszTagName) { copyTagName( puzTagName, pszTagName, uiNameBufSize, pTagInfo->puzTagName); } // Returned *puiNextPos should be the next one to retrieve. (*puiNextPos)++; } else { // Nothing more in list, but initialize return variables anyway. if (puzTagName) { *puzTagName = 0; } if (pszTagName) { *pszTagName = 0; } if (puiTagNum) { *puiTagNum = 0; } if (puiType) { *puiType = 0; } if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Get a tag name, number, etc. using tag name ordering. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getNextTagNameOrder( FLMUINT * puiNextPos, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiTagNum, // May be NULL FLMUINT * puiType, // May be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo = NULL; if (!m_bTablesSorted) { sortTags(); } if (*puiNextPos < m_uiNumTags) { pTagInfo = m_ppSortedByTagName [*puiNextPos]; if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if (puiType) { *puiType = pTagInfo->uiType; } if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } if( puzTagName || pszTagName) { copyTagName( puzTagName, pszTagName, uiNameBufSize, pTagInfo->puzTagName); } // Returned *puiNextPos should be the next one to retrieve. (*puiNextPos)++; } else { // Nothing more in list, but initialize return variables anyway. if (puzTagName) { *puzTagName = 0; } if (pszTagName) { *pszTagName = 0; } if (puiTagNum) { *puiTagNum = 0; } if (puiType) { *puiType = 0; } if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Get a tag name and number from type. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getFromTagType( FLMUINT uiType, FLMUINT * puiNextPos, // To get first, initialize to zero. FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, // In bytes - must be at least sizeof( FLMUNICODE) FLMUINT * puiTagNum, // May be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo = NULL; if (!m_bTablesSorted) { sortTags(); } if (*puiNextPos == 0) { // A value of zero indicates we should try to find the first // one. (void)findTagByTypeAndName( NULL, "", uiType, puiNextPos); if (*puiNextPos < m_uiNumTags && m_ppSortedByTagTypeAndName [*puiNextPos]->uiType != uiType) { (*puiNextPos)++; } } if (*puiNextPos < m_uiNumTags && m_ppSortedByTagTypeAndName [*puiNextPos]->uiType == uiType) { pTagInfo = m_ppSortedByTagTypeAndName [*puiNextPos]; if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } if( puzTagName || pszTagName) { copyTagName( puzTagName, pszTagName, uiNameBufSize, pTagInfo->puzTagName); } // Returned *puiNextPos should be the next one to retrieve, so that // it is not zero. (*puiNextPos)++; } else { // Type was not found, but initialize return variables anyway. if (puzTagName) { *puzTagName = 0; } if (pszTagName) { *pszTagName = 0; } if (puiTagNum) { *puiTagNum = 0; } if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Get a tag name from its tag number. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getFromTagNum( FLMUINT uiTagNum, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, // In bytes, at least enough for null char. FLMUINT * puiType, // May be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo; if (!m_bTablesSorted) { sortTags(); } if ((pTagInfo = findTagByNum( uiTagNum)) != NULL) { if (puiType) { *puiType = pTagInfo->uiType; } if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } if( puzTagName || pszTagName) { copyTagName( puzTagName, pszTagName, uiNameBufSize, pTagInfo->puzTagName); } } else { // Tag number was not found, but initialize return variables anyway. if (puzTagName) { *puzTagName = 0; } if (pszTagName) { *pszTagName = 0; } if (puiType) { *puiType = 0; } if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Get a tag number and type from its tag name. Tag name is passed in as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getFromTagName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT * puiTagNum, // Cannot be NULL FLMUINT * puiType, // May be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo; if (!m_bTablesSorted) { sortTags(); } if ((pTagInfo = findTagByName( puzTagName, pszTagName)) != NULL) { flmAssert( puiTagNum); *puiTagNum = pTagInfo->uiTagNum; if (puiType) { *puiType = pTagInfo->uiType; } if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } } else { // Tag name was not found, but initialize return variables anyway. *puiTagNum = 0; if (puiType) { *puiType = 0; } if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Get a tag number from its tag name and type. Tag name is passed in as a UNICODE or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLMBOOL F_4xNameTable::getFromTagTypeAndName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiType, FLMUINT * puiTagNum, // Cannot be NULL FLMUINT * puiSubType) // May be NULL { FLM_4x_TAG_INFO * pTagInfo; if (!m_bTablesSorted) { sortTags(); } if ((pTagInfo = findTagByTypeAndName( puzTagName, pszTagName, uiType)) != NULL) { flmAssert( puiTagNum); *puiTagNum = pTagInfo->uiTagNum; if (puiSubType) { *puiSubType = pTagInfo->uiSubType; } } else { // Tag name was not found, but initialize return variables anyway. *puiTagNum = 0; if (puiSubType) { *puiSubType = 0; } } return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); } /**************************************************************************** Desc: Insert a tag info structure into the sorted tables at the specified positions. ****************************************************************************/ RCODE F_4xNameTable::insertTagInTables( FLM_4x_TAG_INFO * pTagInfo, FLMUINT uiTagNameTblInsertPos, FLMUINT uiTagTypeAndNameTblInsertPos, FLMUINT uiTagNumTblInsertPos) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; // See if we need to resize the tables. Start at 256. Double each // time up to 2048. Then just add 2048 at a time. if (m_uiNumTags == m_uiTblSize) { FLMUINT uiNewSize; if (!m_uiTblSize) { uiNewSize = 256; } else if (m_uiTblSize < 2048) { uiNewSize = m_uiTblSize * 2; } else { uiNewSize = m_uiTblSize + 2048; } if (RC_BAD( rc = reallocSortTables( uiNewSize))) { goto Exit; } } // Insert into the sorted-by-name table uiLoop = m_uiNumTags; while (uiLoop > uiTagNameTblInsertPos) { m_ppSortedByTagName [uiLoop] = m_ppSortedByTagName [uiLoop - 1]; uiLoop--; } m_ppSortedByTagName [uiTagNameTblInsertPos] = pTagInfo; // Insert into the sorted-by-number table uiLoop = m_uiNumTags; while (uiLoop > uiTagNumTblInsertPos) { m_ppSortedByTagNum [uiLoop] = m_ppSortedByTagNum [uiLoop - 1]; uiLoop--; } m_ppSortedByTagNum [uiTagNumTblInsertPos] = pTagInfo; // Insert into the sorted-by-tag-name-and-type table uiLoop = m_uiNumTags; while (uiLoop > uiTagTypeAndNameTblInsertPos) { m_ppSortedByTagTypeAndName [uiLoop] = m_ppSortedByTagTypeAndName [uiLoop - 1]; uiLoop--; } m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblInsertPos] = pTagInfo; // Increment the total number of tags m_uiNumTags++; Exit: return( rc); } /**************************************************************************** Desc: Add a tag to the table. Tag name is passed in as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_4xNameTable::addTag( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLMBOOL bCheckDuplicates) { RCODE rc = NE_XFLM_OK; FLMUINT uiTagNameTblInsertPos; FLMUINT uiTagTypeAndNameTblInsertPos; FLMUINT uiTagNumTblInsertPos; FLM_4x_TAG_INFO * pTagInfo; // Must have a non-NULL tag name. Use UNICODE string if it is // non-NULL. Otherwise, use NATIVE string. if (puzTagName && *puzTagName) { pszTagName = NULL; } else if (pszTagName && *pszTagName) { puzTagName = NULL; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Tag number of zero not allowed. if (!uiTagNum) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Tables must be sorted in order for this to work if (bCheckDuplicates) { if (!m_bTablesSorted) { sortTags(); } // Make sure that the tag name is not already used. if (findTagByName( puzTagName, pszTagName, &uiTagNameTblInsertPos)) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } // Make sure that the tag name + type is not already used. if (findTagByTypeAndName( puzTagName, pszTagName, uiType, &uiTagTypeAndNameTblInsertPos)) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } // Make sure that the tag number is not already used. if (findTagByNum( uiTagNum, &uiTagNumTblInsertPos)) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } } else { uiTagNameTblInsertPos = uiTagTypeAndNameTblInsertPos = uiTagNumTblInsertPos = m_uiNumTags; m_bTablesSorted = FALSE; } // Create a new tag info structure. if (RC_BAD( rc = allocTag( puzTagName, pszTagName, uiTagNum, uiType, uiSubType, &pTagInfo))) { goto Exit; } // Insert the tag structure into the appropriate places in the // sorted tables. if (RC_BAD( rc = insertTagInTables( pTagInfo, uiTagNameTblInsertPos, uiTagTypeAndNameTblInsertPos, uiTagNumTblInsertPos))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Sort the tag tables according to their respective criteria. ****************************************************************************/ void F_4xNameTable::sortTags( void) { if (!m_bTablesSorted && m_uiNumTags > 1) { sortTagTbl( m_ppSortedByTagName, 0, m_uiNumTags - 1, compareTagNameOnly); sortTagTbl( m_ppSortedByTagNum, 0, m_uiNumTags - 1, compareTagNumOnly); sortTagTbl( m_ppSortedByTagTypeAndName, 0, m_uiNumTags - 1, compareTagTypeAndName); } m_bTablesSorted = TRUE; } /**************************************************************************** Desc: Initialize a name table from a database. ****************************************************************************/ RCODE F_4xNameTable::setupNameTable( F_4xReader * pDb) { RCODE rc = NE_XFLM_OK; F_Record * pRec = NULL; FLMUINT uiDrn; FLMUINT uiLoop; void * pvField; FLMUNICODE uzName[ 60]; FLMUNICODE * puzName = &uzName[ 0]; FLMUINT uiNameLen = sizeof( uzName); FLMUINT uiLen; FLMUINT uiSubType; // Clean out all existing tags, if any. clearTable(); // Find the next DRN in the dictionary container if( RC_BAD( rc = pDb->getNextDrn( FLM_4x_DICT_CONTAINER, &uiDrn))) { goto Exit; } // Count the reserved tags for (uiLoop = 0; Flm4xDictTagInfo[ uiLoop].pszTagName; uiLoop++) { ; } // Preallocate space so we don't have to do it over and over. if( RC_BAD( rc = reallocSortTables( uiLoop + uiDrn))) { goto Exit; } // Add in all of the reserved dictionary tags. for( uiLoop = 0; Flm4xDictTagInfo[ uiLoop].pszTagName; uiLoop++) { if( RC_BAD( rc = addTag( NULL, Flm4xDictTagInfo[ uiLoop].pszTagName, Flm4xDictTagInfo[ uiLoop].uiTagNum, FLM_4x_FIELD_TAG, Flm4xDictTagInfo[ uiLoop].uiFieldType, FALSE))) { goto Exit; } } // Read through all of the dictionary records pDb->setDefaultContainer( FLM_4x_DICT_CONTAINER); for( ;;) { if( RC_BAD( rc = pDb->retrieveNextRec( &pRec))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } pvField = pRec->root(); uiDrn = pRec->getID(); // Get the unicode name length (does not include NULL terminator) if( RC_BAD( rc = pRec->getUnicode( pvField, NULL, &uiLen))) { goto Exit; } // Account for NULL character. uiLen += sizeof( FLMUNICODE); // See if we need a larger buffer to get the name. if (uiLen > uiNameLen) { FLMUNICODE * puzTmp; // Add enough for 60 more unicode characters. uiLen += (60 * sizeof( FLMUNICODE)); if( RC_BAD( rc = f_alloc( uiLen, &puzTmp))) { goto Exit; } if (puzName != &uzName [0]) { f_free( &puzName); } puzName = puzTmp; uiNameLen = uiLen; } // Get the tag name. uiLen = uiNameLen; if (RC_BAD( rc = pRec->getUnicode( pvField, puzName, &uiLen))) { goto Exit; } // Get the sub-type. if (pRec->getFieldID( pvField) == FLM_4x_FIELD_TAG) { void * pvFld = pRec->find( pvField, FLM_4x_TYPE_TAG, 1, FALSE); if (!pvFld || RC_BAD( pDb->getTypeTag( pRec, pvFld, &uiSubType))) { uiSubType = FLM_4x_TEXT_TYPE; } } else { uiSubType = 0; } // Add tag to table, without sorting yet. if (RC_BAD( rc = addTag( puzName, NULL, uiDrn, pRec->getFieldID( pvField), uiSubType, FALSE))) { goto Exit; } } sortTags(); Exit: if( RC_BAD( rc)) { clearTable(); } if( pRec) { pRec->Release(); } if( puzName != &uzName [0]) { f_free( &puzName); } return( rc); }