//------------------------------------------------------------------------- // Desc: Name table class // Tabs: 3 // // Copyright (c) 1992,1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fntable.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" typedef FLMINT (* TAG_COMPARE_FUNC)( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC FLMINT tagNameCompare( const FLMUNICODE * puzName1, const char * pszName1, const FLMUNICODE * puzName2); FSTATIC FLMINT compareTagNumOnly( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC FLMINT compareTagNameOnly( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC FLMINT compareTagTypeAndName( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); /* ** WARNING: ANY CHANGES MADE TO THE FlmDictTags TABLE MUST BE REFLECTED ** IN THE TAG DEFINES FOUND IN FLAIM.H */ typedef struct FlmDictTag { const char * pszTagName; FLMUINT uiTagNum; FLMUINT uiFieldType; } DICT_TAG_NAME; DICT_TAG_NAME FlmDictTags[] = { {FLM_FIELD_TAG_NAME, FLM_FIELD_TAG, FLM_TEXT_TYPE}, {FLM_INDEX_TAG_NAME, FLM_INDEX_TAG, FLM_TEXT_TYPE}, {FLM_TYPE_TAG_NAME, FLM_TYPE_TAG, FLM_TEXT_TYPE}, {FLM_COMMENT_TAG_NAME, FLM_COMMENT_TAG, FLM_TEXT_TYPE}, {FLM_CONTAINER_TAG_NAME, FLM_CONTAINER_TAG, FLM_TEXT_TYPE}, {FLM_LANGUAGE_TAG_NAME, FLM_LANGUAGE_TAG, FLM_TEXT_TYPE}, {FLM_OPTIONAL_TAG_NAME, FLM_OPTIONAL_TAG, FLM_TEXT_TYPE}, {FLM_UNIQUE_TAG_NAME, FLM_UNIQUE_TAG, FLM_TEXT_TYPE}, {FLM_KEY_TAG_NAME, FLM_KEY_TAG, FLM_TEXT_TYPE}, {FLM_REFS_TAG_NAME, FLM_REFS_TAG, FLM_TEXT_TYPE}, {FLM_ENCDEF_TAG_NAME, FLM_ENCDEF_TAG, FLM_TEXT_TYPE}, {FLM_DELETE_TAG_NAME, FLM_DELETE_TAG, FLM_TEXT_TYPE}, {FLM_BLOCK_CHAIN_TAG_NAME, FLM_BLOCK_CHAIN_TAG, FLM_NUMBER_TYPE}, {FLM_AREA_TAG_NAME, FLM_AREA_TAG, FLM_TEXT_TYPE}, {FLM_STATE_TAG_NAME, FLM_STATE_TAG, FLM_TEXT_TYPE}, {FLM_BLOB_TAG_NAME, FLM_BLOB_TAG, FLM_TEXT_TYPE}, {FLM_THRESHOLD_TAG_NAME, FLM_THRESHOLD_TAG, FLM_TEXT_TYPE}, {FLM_SUFFIX_TAG_NAME, FLM_SUFFIX_TAG, FLM_TEXT_TYPE}, {FLM_SUBDIRECTORY_TAG_NAME, FLM_SUBDIRECTORY_TAG, FLM_TEXT_TYPE}, {FLM_RESERVED_TAG_NAME, FLM_RESERVED_TAG, FLM_TEXT_TYPE}, {FLM_SUBNAME_TAG_NAME, FLM_SUBNAME_TAG, FLM_TEXT_TYPE}, {FLM_NAME_TAG_NAME, FLM_NAME_TAG, FLM_TEXT_TYPE}, {FLM_BASE_TAG_NAME, FLM_BASE_TAG, FLM_TEXT_TYPE}, {FLM_CASE_TAG_NAME, FLM_CASE_TAG, FLM_TEXT_TYPE}, {FLM_COMBINATIONS_TAG_NAME, FLM_COMBINATIONS_TAG, FLM_TEXT_TYPE}, {FLM_COUNT_TAG_NAME, FLM_COUNT_TAG, FLM_TEXT_TYPE}, {FLM_POSITIONING_TAG_NAME, FLM_POSITIONING_TAG, FLM_TEXT_TYPE}, {FLM_PAIRED_TAG_NAME, FLM_PAIRED_TAG, FLM_TEXT_TYPE}, {FLM_PARENT_TAG_NAME, FLM_PARENT_TAG, FLM_TEXT_TYPE}, {FLM_POST_TAG_NAME, FLM_POST_TAG, FLM_TEXT_TYPE}, {FLM_REQUIRED_TAG_NAME, FLM_REQUIRED_TAG, FLM_TEXT_TYPE}, {FLM_USE_TAG_NAME, FLM_USE_TAG, FLM_TEXT_TYPE}, {FLM_FILTER_TAG_NAME, FLM_FILTER_TAG, FLM_TEXT_TYPE}, {FLM_LIMIT_TAG_NAME, FLM_LIMIT_TAG, FLM_TEXT_TYPE}, {FLM_DICT_TAG_NAME, FLM_DICT_TAG, FLM_TEXT_TYPE}, {FLM_RECINFO_TAG_NAME, FLM_RECINFO_TAG, FLM_TEXT_TYPE}, {FLM_DRN_TAG_NAME, FLM_DRN_TAG, FLM_TEXT_TYPE}, {FLM_DICT_SEQ_TAG_NAME, FLM_DICT_SEQ_TAG, FLM_TEXT_TYPE}, {FLM_LAST_CONTAINER_INDEXED_TAG_NAME, FLM_LAST_CONTAINER_INDEXED_TAG, FLM_NUMBER_TYPE}, {FLM_LAST_DRN_INDEXED_TAG_NAME, FLM_LAST_DRN_INDEXED_TAG, FLM_NUMBER_TYPE}, {FLM_ONLINE_TRANS_ID_TAG_NAME, FLM_ONLINE_TRANS_ID_TAG, FLM_NUMBER_TYPE}, {NULL, 0} }; /* ** WARNING: ANY CHANGES MADE TO THE FlmDictTags TABLE MUST BE REFLECTED ** IN THE TAG DEFINES FOUND IN FLAIM.H */ FSTATIC void sortTagTbl( FLM_TAG_INFO ** ppTagInfoTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, TAG_COMPARE_FUNC fnTagCompare); /**************************************************************************** Desc: Constructor ****************************************************************************/ F_NameTable::F_NameTable() { 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_NameTable::~F_NameTable() { clearTable(); m_pool.poolFree(); } /**************************************************************************** Desc: Free everything in the table ****************************************************************************/ void F_NameTable::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. ****************************************************************************/ FSTATIC FLMINT 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_TAG_INFO * F_NameTable::findTagByName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT * puiInsertPos) { FLM_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_TAG_INFO * F_NameTable::findTagByNum( FLMUINT uiTagNum, FLMUINT * puiInsertPos ) { FLM_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_TAG_INFO * F_NameTable::findTagByTypeAndName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiType, FLMUINT * puiInsertPos) { FLM_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_NameTable::copyTagName( FLMUNICODE * puzDestTagName, char * pszDestTagName, FLMUINT uiDestBufSize, const 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++ = (FLMBYTE)*puzSrcTagName; } else { *pszDestTagName++ = '?'; } uiDestBufSize--; puzSrcTagName++; } *pszDestTagName = 0; } } /*************************************************************************** Desc: Swap two entries in tag info table during sort. *****************************************************************************/ FINLINE void tagInfoSwap( FLM_TAG_INFO ** ppTagInfoTbl, FLMUINT uiPos1, FLMUINT uiPos2) { FLM_TAG_INFO * pTmpTagInfo = ppTagInfoTbl [uiPos1]; ppTagInfoTbl [uiPos1] = ppTagInfoTbl [uiPos2]; ppTagInfoTbl [uiPos2] = pTmpTagInfo; } /*************************************************************************** Desc: Comparison function for sorting by tag number. ****************************************************************************/ FSTATIC FLMINT compareTagNumOnly( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2 ) { if (pTagInfo1->uiTagNum < pTagInfo2->uiTagNum) { return( -1); } else if (pTagInfo1->uiTagNum > pTagInfo2->uiTagNum) { return( 1); } else { return( 0); } } /*************************************************************************** Desc: Comparison function for sorting by tag name. ****************************************************************************/ FSTATIC FLMINT compareTagNameOnly( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2 ) { return (tagNameCompare( pTagInfo1->puzTagName, NULL, pTagInfo2->puzTagName)); } /*************************************************************************** Desc: Comparison function for sorting by tag type and name. ****************************************************************************/ FSTATIC FLMINT compareTagTypeAndName( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2 ) { if (pTagInfo1->uiType < pTagInfo2->uiType) { return( -1); } else if (pTagInfo1->uiType > pTagInfo2->uiType) { return( 1); } else { return (tagNameCompare( pTagInfo1->puzTagName, NULL, pTagInfo2->puzTagName)); } } /*************************************************************************** Desc: Sort an array of SCACHE pointers by their block address. ****************************************************************************/ FSTATIC void sortTagTbl( FLM_TAG_INFO ** ppTagInfoTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, TAG_COMPARE_FUNC fnTagCompare ) { FLMUINT uiLBPos; FLMUINT uiUBPos; FLMUINT uiMIDPos; FLMUINT uiLeftItems; FLMUINT uiRightItems; FLM_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_NameTable::allocTag( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLM_TAG_INFO ** ppTagInfo) { RCODE rc = FERR_OK; void * pvMark; FLM_TAG_INFO * pTagInfo; FLMUINT uiNameSize; FLMUNICODE * puzTmp; // Create a new tag info structure. pvMark = m_pool.poolMark(); if( RC_BAD( rc = m_pool.poolCalloc( sizeof( FLM_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( (void *)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 = (FLMUNICODE *)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_NameTable::reallocSortTables( FLMUINT uiNewTblSize) { RCODE rc = FERR_OK; FLM_TAG_INFO ** ppNewTbl; if( RC_BAD( rc = f_alloc( sizeof( FLM_TAG_INFO *) * (uiNewTblSize * 3), &ppNewTbl))) { goto Exit; } // Copy the old tables into the new. if (m_uiNumTags) { f_memcpy( ppNewTbl, m_ppSortedByTagName, sizeof( FLM_TAG_INFO *) * m_uiNumTags); f_memcpy( &ppNewTbl [uiNewTblSize], m_ppSortedByTagNum, sizeof( FLM_TAG_INFO *) * m_uiNumTags); f_memcpy( &ppNewTbl [uiNewTblSize + uiNewTblSize], m_ppSortedByTagTypeAndName, sizeof( FLM_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: Initialize a name table from a database. ****************************************************************************/ RCODE F_NameTable::setupFromDb( HFDB hDb // May be HFDB_NULL - means only get reserved tags. ) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; LFILE * pLFile; FLMBOOL bStartedTrans = FALSE; FlmRecord * pRec = NULL; FLMUINT uiDrn; FLMUINT uiLoop; BTSK StackBuf [BH_MAX_LEVELS]; FLMBOOL bStackInitialized = FALSE; BTSK * pStack; FLMBYTE ucKeyBuf [8]; FLMBYTE ucDrnBuf [8]; void * pvField; FLMUNICODE uzName [60]; FLMUNICODE * puzName = &uzName [0]; FLMUINT uiNameLen = sizeof( uzName); FLMUINT uiLen; FLMUINT uiSubType; // Is this a client/server handle if( hDb != HFDB_NULL && IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); // Send a request to get the name table. if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_GET_NAME_TABLE))) { goto ExitCS; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Set the name table Wire.setNameTable( this); // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (RC_BAD( rc = Wire.getRCode())) { goto ExitCS; } goto ExitCS; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto ExitCS; ExitCS: fdbExit( pDb); Wire.setNameTable( NULL); goto Exit; } // Clean out all existing tags, if any. clearTable(); if (hDb != HFDB_NULL) { // Start a read transaction, if one not going. if (pDb->uiTransType == FLM_NO_TRANS) { if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0))) { goto Exit; } bStartedTrans = TRUE; } // Get the LFILE for the dictionary if (RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, &pLFile))) { goto Exit; } // See what the next DRN in the dictionary container will be uiDrn = 0; if (RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiDrn))) { goto Exit; } } else { uiDrn = 0; } // Count the number of reserved tags. for (uiLoop = 0; FlmDictTags [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; FlmDictTags [uiLoop].pszTagName; uiLoop++) { if( RC_BAD( rc = addTag( NULL, FlmDictTags [uiLoop].pszTagName, FlmDictTags [uiLoop].uiTagNum, FLM_FIELD_TAG, FlmDictTags [uiLoop].uiFieldType, FALSE))) { goto Exit; } } if( hDb != HFDB_NULL) { // Set up to read through all of the records. */ FSInitStackCache( &StackBuf [0], BH_MAX_LEVELS); bStackInitialized = TRUE; pStack = StackBuf; pStack->pKeyBuf = ucKeyBuf; // Position to the first record in the B-Tree. f_UINT32ToBigEndian( 0, ucDrnBuf); if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, ucDrnBuf, 4, ZERO_DOMAIN))) { goto Exit; } // Read through all of the records in the B-Tree. while (pStack->uiCmpStatus != BT_END_OF_DATA) { if ((uiDrn = f_bigEndianToUINT32( ucKeyBuf)) == DRN_LAST_MARKER) { break; } if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_DICT_CONTAINER, uiDrn, FALSE, NULL, NULL, &pRec))) { if (rc != FERR_NOT_FOUND) { goto Exit; } // NOTE: Deliberately not bringing in to cache if not // found there. if (RC_BAD( rc = FSReadElement( pDb, &pDb->TempPool, pLFile, uiDrn, pStack, TRUE, &pRec, NULL, NULL))) { goto Exit; } } pvField = pRec->root(); // Get the unicode name length (does not include NULL terminator) if (RC_BAD( rc = pRec->getUnicodeLength( pvField, &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_FIELD_TAG) { void * pvFld = pRec->find( pvField, FLM_TYPE_TAG, 1, SEARCH_TREE); if (!pvFld || RC_BAD( DDGetFieldType( pRec, pvFld, &uiSubType))) { uiSubType = FLM_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; } // Position to the next record. if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack))) { if (rc == FERR_BT_END_OF_DATA) { rc = FERR_OK; break; } goto Exit; } } } sortTags(); Exit: if (RC_BAD( rc)) { clearTable(); } if (bStartedTrans) { (void)FlmDbTransAbort( hDb); } if (pRec) { pRec->Release(); } if (puzName != &uzName [0]) { f_free( &puzName); } if (bStackInitialized) { FSReleaseStackCache( StackBuf, BH_MAX_LEVELS, FALSE); } 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_NameTable::getNextTagNumOrder( FLMUINT * puiNextPos, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiTagNum, FLMUINT * puiType, FLMUINT * puiSubType) { FLM_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_NameTable::getNextTagNameOrder( FLMUINT * puiNextPos, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiTagNum, FLMUINT * puiType, FLMUINT * puiSubType) { FLM_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_NameTable::getFromTagType( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiTagNum, FLMUINT * puiSubType) { FLM_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_NameTable::getFromTagNum( FLMUINT uiTagNum, FLMUNICODE * puzTagName, char * pszTagName, FLMUINT uiNameBufSize, FLMUINT * puiType, FLMUINT * puiSubType) { FLM_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_NameTable::getFromTagName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT * puiTagNum, FLMUINT * puiType, FLMUINT * puiSubType) { FLM_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_NameTable::getFromTagTypeAndName( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiType, FLMUINT * puiTagNum, FLMUINT * puiSubType) { FLM_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_NameTable::insertTagInTables( FLM_TAG_INFO * pTagInfo, FLMUINT uiTagNameTblInsertPos, FLMUINT uiTagTypeAndNameTblInsertPos, FLMUINT uiTagNumTblInsertPos ) { RCODE rc = FERR_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_NameTable::addTag( const FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLMBOOL bCheckDuplicates) { RCODE rc = FERR_OK; FLMUINT uiTagNameTblInsertPos; FLMUINT uiTagTypeAndNameTblInsertPos; FLMUINT uiTagNumTblInsertPos; FLM_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 { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } // Tag number of zero not allowed. if (!uiTagNum) { flmAssert( 0); rc = RC_SET( FERR_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( FERR_EXISTS); goto Exit; } // Make sure that the tag name + type is not already used. if (findTagByTypeAndName( puzTagName, pszTagName, uiType, &uiTagTypeAndNameTblInsertPos)) { rc = RC_SET( FERR_EXISTS); goto Exit; } // Make sure that the tag number is not already used. if (findTagByNum( uiTagNum, &uiTagNumTblInsertPos)) { rc = RC_SET( FERR_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_NameTable::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; }