Files
mars-flaim/flaim/src/fdict.cpp
2026-06-05 21:48:59 +02:00

4333 lines
94 KiB
C++

//-------------------------------------------------------------------------
// Desc: Dictionary access routiones.
// Tabs: 3
//
// Copyright (c) 1995-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library 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
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com.
//
// $Id$
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE fdictAddDictIndex(
TDICT * pTDict);
FSTATIC RCODE DDFieldParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord,
FLMUINT uiDictRecNum);
FSTATIC RCODE DDGetReference(
FlmRecord * pRecord,
void * pvField,
const char * pszBuffer,
FLMUINT * puiIdRef);
FSTATIC RCODE DDAllocEntry(
TDICT * pTDict,
FlmRecord * pRecord,
FLMUINT uiDictRecNum,
DDENTRY ** ppDDEntryRV);
FSTATIC RCODE DDIxParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord,
void * pvField);
FSTATIC RCODE DDBuildFldPath(
TDICT * pTDict,
TIFD ** ppTIfd,
FlmRecord * pRecord,
void * pvField,
FLMUINT uiBaseNum);
FSTATIC FLMBOOL DDMoveWord(
char * pucDest,
char * pucSrc,
FLMUINT uiMaxDestLen,
FLMUINT * puiPos);
FSTATIC RCODE DDContainerParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord);
FSTATIC void DDTextToNative(
FlmRecord * pRecord,
void * pvField,
char * pszBuffer,
FLMUINT uiBufLen,
FLMUINT * puiBufLen);
FSTATIC RCODE DDParseStateOptions(
FlmRecord * pRecord,
void * pvField,
FLMUINT * puiFldInfo);
FSTATIC RCODE DDEncDefParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord);
FSTATIC RCODE DDGetEncKey(
TDICT * pTDict,
FlmRecord * pRecord,
void * pvField,
TENCDEF * pTEncDef);
FSTATIC RCODE DDMakeDictIxKey(
FDB * pDb,
FlmRecord * pRecord,
FLMBYTE * pKeyBuf,
FLMUINT * puiKeyLenRV);
FSTATIC RCODE DDCheckNameConflict(
FDB * pDb,
LFILE * pDictIxLFile,
FlmRecord * pNewRec,
FLMUINT uiDrn,
FlmRecord * pOldRec);
FSTATIC RCODE DDCheckIDConflict(
FDB * pDb,
LFILE * pDictContLFile,
FLMUINT uiDrn);
FSTATIC RCODE DDIxDictRecord(
FDB * pDb,
LFILE * pDictIxLFile,
FLMUINT uiDrn,
FlmRecord * pRecord,
FLMUINT uiFlags);
FSTATIC void fdictFixupPointers(
FDICT * pNewDict,
FDICT * pOldDict);
FSTATIC RCODE fdictReallocAllTables(
TDICT * pTDict);
FSTATIC RCODE fdictReallocTbl(
FLMUINT uiElementSize,
FLMUINT uiTblSize,
FLMUINT uiAddElements,
void ** ppvTblRV);
FSTATIC void fdictAddItem(
TDICT * pTDict,
FLMUINT uiFieldNum,
FLMUINT uiFieldType);
FSTATIC RCODE fdictAddIndex(
TDICT * pTDict,
DDENTRY * pEntry);
FSTATIC RCODE fdictFixupIfdPointers(
FDICT * pDict,
FLMUINT uiIfdStartOffset);
FSTATIC RCODE fdictAddNewCCS(
TDICT * pTDict,
TENCDEF * pTEncDef,
FLMUINT uiRecNum);
#define FDD_MAX_VALUE_SIZE 64
#define MAX_ENC_TYPES 3
#define START_DD_INDEX_OPTS 0
#define DD_IX_FIELD_OPT 0
#define DD_IX_COMPOUND_OPT 1
#define DD_IX_UPPER_OPT 2
#define DD_IX_EACHWORD_OPT 3
#define DD_IX_MIXED_OPT 4
#define DD_IX_CONTEXT_OPT 5
#define DD_IX_POST_OPT 6
#define MAX_DD_INDEX_OPTS 7
// NOTE: If you change the arrangement of the values in this array, make sure
// you search the entire codebase for references to DDEncOpts and DDGetEncType
// and verify that the changes won't cause problems. This is particularly
// important because these values DO NOT match up exactly with the values in
// the SMEncryptionScheme enum that's used at the SMI level.
const char * DDEncOpts[ MAX_ENC_TYPES] =
{
"aes",
"des3",
"des"
};
FSTATIC POOL_STATS g_TDictPoolStats = {0,0};
/*******************************************************************************
Desc: Retrieves a dictionary item name.
Notes: Given an item ID, this routine will search a specified shared or
local dictionary for the item. If it is found, the name
of the item will be returned. This routine supports version 2.0 and
higher databases only.
*******************************************************************************/
FLMEXP RCODE FLMAPI FlmGetItemName(
HFDB hDb,
FLMUINT uiItemId,
FLMUINT uiNameBufSize,
char * pszNameBuf)
{
RCODE rc = FERR_OK;
FlmRecord * pRecord = NULL;
*pszNameBuf = 0;
if( RC_BAD( rc = FlmRecordRetrieve( hDb,
FLM_DICT_CONTAINER, uiItemId, FO_EXACT, &pRecord, NULL)))
{
goto Exit;
}
if( RC_BAD( rc = pRecord->getNative( pRecord->root(),
pszNameBuf, &uiNameBufSize)))
{
goto Exit;
}
Exit:
if( pRecord)
{
pRecord->Release();
}
return (rc == FERR_EOF_HIT) ? RC_SET( FERR_NOT_FOUND) : rc;
}
/****************************************************************************
Desc: Read all data dictionary records parsing and sending to process.
All temporary structures are off of pTDict. pTDict must be setup.
****************************************************************************/
RCODE fdictProcessAllDictRecs(
FDB * pDb,
TDICT * pTDict)
{
RCODE rc;
LFILE * pLFile = pTDict->pLFile;
BTSK stackBuf[ BH_MAX_LEVELS ]; // Stack to hold b-tree variables
BTSK * stack = stackBuf; // Points to proper stack frame
FLMBYTE btKeyBuf[ DRN_KEY_SIZ +8]; // Key buffer pointed to by stack
FLMBYTE key[4]; // Used for dummy empty key
FLMUINT uiDrn;
FlmRecord * pRecord = NULL;
// Add the dictionary index to the front of TDICT.
if( RC_BAD( rc = fdictAddDictIndex( pTDict)))
{
goto Exit;
}
// Position to the first of the data dictionary data records & read.
FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS);
stack->pKeyBuf = btKeyBuf;
f_UINT32ToBigEndian( 0, key);
if( RC_BAD(rc = FSBtSearch( pDb, pLFile, &stack, key, DRN_KEY_SIZ, 0 )))
{
goto Exit;
}
// Special case of no records.
if( stack->uiCmpStatus == BT_END_OF_DATA)
goto Exit;
stack->uiFlags = NO_STACK; // Fake out the stack for speed.
do
{
uiDrn = f_bigEndianToUINT32( btKeyBuf);
if( uiDrn == DRN_LAST_MARKER)
{
break;
}
// VERY IMPORTANT NOTE:
// DO NOT READ FROM CACHE - THE RECORD MAY
// NOT HAVE BEEN PUT INTO RECORD CACHE YET, AND WE NEED TO HAVE
// THE CORRECT VERSION OF THE RECORD.
if( RC_BAD( rc = FSReadElement( pDb, &pDb->TempPool, pLFile,
uiDrn, stack, TRUE, &pRecord, NULL, NULL)))
{
break;
}
if( RC_BAD(rc = fdictProcessRec( pTDict, pRecord, uiDrn)))
{
pDb->Diag.uiDrn = uiDrn;
pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN;
if( pTDict->uiBadField != 0)
{
pDb->Diag.uiFieldNum = pTDict->uiBadField;
pDb->Diag.uiInfoFlags |= FLM_DIAG_FIELD_NUM;
}
break;
}
// Position to the next record - SUCCESS or FERR_BT_END_OF_DATA
rc = FSNextRecord( pDb, pLFile, stack);
} while( RC_OK(rc));
rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
Exit:
if( pRecord)
{
pRecord->Release();
}
FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE);
return( rc );
}
/****************************************************************************
Desc: Add the dictionary index to pTDict.
****************************************************************************/
RCODE fdictAddDictIndex(
TDICT * pTDict)
{
RCODE rc;
DDENTRY * pDDEntry;
TIXD * pTIxd;
TIFD * pTIfd;
TIFP * pTIfp;
if( RC_BAD( rc = DDAllocEntry( pTDict, NULL, FLM_DICT_INDEX, &pDDEntry)))
{
goto Exit;
}
pDDEntry->uiType = ITT_INDEX_TYPE;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIXD), (void **)&pTIxd)))
{
goto Exit;
}
pTDict->uiNewIxds++;
pDDEntry->vpDef = (void *) pTIxd;
pTIxd->uiFlags = IXD_UNIQUE;
pTIxd->uiContainerNum = FLM_DICT_CONTAINER;
pTIxd->uiNumFlds = 1;
pTIxd->uiLanguage = pTDict->uiDefaultLanguage;
pTIxd->uiEncId = 0;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFD), (void **)&pTIfd)))
{
goto Exit;
}
pTIxd->pNextTIfd = pTIfd;
pTDict->uiNewIfds++;
pTIfd->pTIfp = NULL;
pTIfd->pNextTIfd = NULL;
pTIfd->uiFlags = (FLMUINT)(IFD_FIELD | FLM_TEXT_TYPE);
pTIfd->uiNextFixupPos = 0;
pTIfd->uiLimit = IFD_DEFAULT_LIMIT;
pTIfd->uiCompoundPos = 0;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp)))
{
goto Exit;
}
pTDict->uiNewFldPaths += 2;
pTIfd->pTIfp = pTIfp;
pTIfp->pNextTIfp = NULL;
pTIfp->bFieldInThisDict = FALSE;
pTIfp->uiFldNum = FLM_NAME_TAG;
Exit:
return( rc);
}
/****************************************************************************
Desc: Process a single data dictionary record. Parse the record for syntax
errors depending on flag value. Only supports adding new stuff
to pTDict.
****************************************************************************/
RCODE fdictProcessRec(
TDICT * pTDict,
FlmRecord * pRecord,
FLMUINT uiDictRecNum)
{
RCODE rc = FERR_OK;
DDENTRY * pDDEntry;
void * pvField = pRecord->root();
// Ignore items with root nodes that are in the unregistered range.
if( pRecord->getFieldID( pvField) >= FLM_UNREGISTERED_TAGS)
{
goto Exit;
}
// Parse only on modify or add.
switch( pRecord->getFieldID( pvField))
{
case FLM_FIELD_TAG:
{
if( RC_BAD( rc = DDAllocEntry(
pTDict, pRecord, uiDictRecNum, &pDDEntry)))
{
goto Exit;
}
pDDEntry->uiType = 0; // Type of zero means field.
if( RC_BAD( rc = DDFieldParse( pTDict, pDDEntry,
pRecord, uiDictRecNum)))
{
goto Exit;
}
break;
}
case FLM_INDEX_TAG:
{
if( RC_BAD( rc = DDAllocEntry(
pTDict, pRecord, uiDictRecNum, &pDDEntry)))
{
goto Exit;
}
pDDEntry->uiType = ITT_INDEX_TYPE;
if( RC_BAD( rc = DDIxParse( pTDict, pDDEntry, pRecord, pvField)))
{
goto Exit;
}
pTDict->uiNewIxds++;
break;
}
case FLM_CONTAINER_TAG:
{
if( RC_BAD( rc = DDAllocEntry(
pTDict, pRecord, uiDictRecNum, &pDDEntry)))
{
goto Exit;
}
pDDEntry->uiType = ITT_CONTAINER_TYPE;
if( RC_BAD( rc = DDContainerParse( pTDict, pDDEntry, pRecord)))
{
goto Exit;
}
pTDict->uiTotalLFiles++;
break;
}
case FLM_ENCDEF_TAG:
{
if( RC_BAD( rc = DDAllocEntry(
pTDict, pRecord, uiDictRecNum, &pDDEntry)))
{
goto Exit;
}
pDDEntry->uiType = ITT_ENCDEF_TYPE;
if (RC_BAD( rc = DDEncDefParse( pTDict, pDDEntry, pRecord)))
{
goto Exit;
}
break;
}
case FLM_AREA_TAG:
case FLM_RESERVED_TAG:
{
break;
}
default:
{
// Cannot allow anything else to pass through the dictionary.
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Allocate, check and add a name to the DDEntry structure.
****************************************************************************/
FSTATIC RCODE DDAllocEntry(
TDICT * pTDict,
FlmRecord * pRecord,
FLMUINT uiDictRecNum,
DDENTRY ** ppDDEntryRV)
{
RCODE rc = FERR_OK;
DDENTRY * pNewEntry;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( DDENTRY),
(void **)&pNewEntry)))
{
goto Exit;
}
pNewEntry->pNextEntry = NULL;
pNewEntry->vpDef = NULL;
pNewEntry->uiEntryNum = uiDictRecNum;
pNewEntry->uiType = 0;
// Zero length name NOT allowed for dictionary items.
if( pRecord)
{
if( pRecord->getDataLength( pRecord->root()) == 0)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
if( pTDict->pLastEntry)
{
pTDict->pLastEntry->pNextEntry = pNewEntry;
}
else
{
pTDict->pFirstEntry = pNewEntry;
}
pTDict->pLastEntry = pNewEntry;
*ppDDEntryRV = pNewEntry;
Exit:
return( rc );
}
/****************************************************************************
Desc: Parse field definition
****************************************************************************/
FSTATIC RCODE DDFieldParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord,
FLMUINT uiDictRecNum)
{
RCODE rc = FERR_OK;
TFIELD * pTField;
void * pvField = NULL;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TFIELD),
(void **)&pTField)))
{
goto Exit;
}
pTField->uiFldNum = uiDictRecNum;
pTField->uiFldInfo = FLM_CONTEXT_TYPE;
pDDEntry->vpDef = (void *) pTField;
if( (pvField = pRecord->firstChild( pRecord->root())) == NULL)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
for( ; pvField; pvField = pRecord->nextSibling( pvField))
{
switch( pRecord->getFieldID( pvField))
{
case FLM_TYPE_TAG:
{
rc = DDGetFieldType( pRecord, pvField, &pTField->uiFldInfo);
break;
}
case FLM_STATE_TAG:
{
rc = DDParseStateOptions( pRecord, pvField, &pTField->uiFldInfo);
break;
}
default:
{
if( pRecord->getFieldID( pvField) < FLM_UNREGISTERED_TAGS &&
pRecord->getFieldID( pvField) != FLM_COMMENT_TAG)
{
rc = RC_SET( FERR_SYNTAX);
}
break;
}
}
}
Exit:
if( RC_BAD(rc) && pvField)
{
pTDict->uiBadField = pRecord->getFieldID( pvField);
}
return( rc );
}
/****************************************************************************
Desc: Returns the fields data type. May be called outside of DDPREP.C
****************************************************************************/
RCODE DDGetFieldType(
FlmRecord * pRecord,
void * pvField,
FLMUINT * puiFldInfo)
{
RCODE rc = FERR_OK;
char szNativeBuf[ FDD_MAX_VALUE_SIZE];
DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL );
// Parse the type keyword - only one type allowed.
if (f_strnicmp( szNativeBuf, "text", 4) == 0)
{
*puiFldInfo = FLM_TEXT_TYPE;
}
else if (f_strnicmp( szNativeBuf, "numb", 4) == 0)
{
*puiFldInfo = FLM_NUMBER_TYPE;
}
else if (f_strnicmp( szNativeBuf, "bina", 4) == 0)
{
*puiFldInfo = FLM_BINARY_TYPE;
}
else if (f_strnicmp( szNativeBuf, "cont", 4) == 0)
{
*puiFldInfo = FLM_CONTEXT_TYPE;
}
else if (f_strnicmp( szNativeBuf, "blob", 4) == 0)
{
*puiFldInfo = FLM_BLOB_TYPE;
}
else
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Parses the 'state' option that is found within the 'field'
dictionary definition.
Format: state [checking | unused | purge | active]
****************************************************************************/
FSTATIC RCODE DDParseStateOptions(
FlmRecord * pRecord,
void * pvField,
FLMUINT * puiFldInfo)
{
RCODE rc = FERR_OK;
char szNativeBuf[ FDD_MAX_VALUE_SIZE];
DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL);
// Parse the 'state' keyword - only one type allowed
if( f_strnicmp( szNativeBuf, "chec", 4) == 0)
{
// 0xFFCF is used to clear out any existing field 'state' value
*puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_CHECKING);
}
else if( f_strnicmp( szNativeBuf, "unus", 4) == 0)
{
*puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_UNUSED);
}
else if( f_strnicmp( szNativeBuf, "purg", 4) == 0)
{
*puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_PURGE);
}
else if( f_strnicmp( szNativeBuf, "acti", 4) == 0)
{
*puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_ACTIVE);
}
else
{
rc = RC_SET( FERR_SYNTAX);
}
return( rc);
}
/****************************************************************************
Desc: Get a number reference and set in the (OUT) parameter.
****************************************************************************/
FSTATIC RCODE DDGetReference(
FlmRecord * pRecord,
void * pvField,
const char * pszBuffer,
FLMUINT * puiIdRef)
{
RCODE rc = FERR_OK;
*puiIdRef = 0;
if( pszBuffer)
{
if( !(*pszBuffer))
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
*puiIdRef = f_atoud( pszBuffer);
}
else
{
if( RC_BAD( rc = pRecord->getUINT( pvField, puiIdRef)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Returns the encryption type. May be called outside of DDPREP.C
****************************************************************************/
RCODE DDGetEncType(
FlmRecord * pRecord,
void * pvField,
FLMUINT * puiFldInfo)
{
RCODE rc = FERR_OK;
FLMUINT uiType;
char szNativeBuf[ FDD_MAX_VALUE_SIZE];
DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL );
// Parse the type keyword - only one type allowed.
for( uiType = 0;
uiType < MAX_ENC_TYPES ;
uiType++)
{
if( f_strnicmp( szNativeBuf, DDEncOpts[ uiType],
f_strlen(DDEncOpts[ uiType])) == 0)
{
*puiFldInfo = uiType;
goto Exit;
}
}
rc = RC_SET( FERR_SYNTAX);
Exit:
return( rc);
}
/****************************************************************************
Desc: Returns the binary key info. May be called outside of DDPREP.C
****************************************************************************/
FSTATIC RCODE DDGetEncKey(
TDICT * pTDict,
FlmRecord * pRecord,
void * pvField,
TENCDEF * pTEncDef)
{
RCODE rc = FERR_OK;
char * pucBuffer = NULL;
FLMUINT uiLength;
pTEncDef->uiLength = 0;
if (RC_BAD( rc = pRecord->getNativeLength( pvField, &uiLength)))
{
goto Exit;
}
uiLength++;
if( RC_BAD( rc = pTDict->pool.poolAlloc( uiLength,
(void **)&pucBuffer)))
{
goto Exit;
}
if (RC_BAD( rc = pRecord->getNative( pvField, pucBuffer, &uiLength)))
{
goto Exit;
}
pTEncDef->uiLength = uiLength;
pTEncDef->pucKeyInfo = (FLMBYTE *)pucBuffer;
Exit:
return( rc);
}
/****************************************************************************
Desc: Parse an data dictionary index definition for correct syntax &
assign the correct attributes. Build the pcode buffer for the index.
Return: RCODE - SUCCESS or FERR_SYNTAX
Format:
0 index <psName> # FLM_INDEX_TAG
[ 1 area [ 0 | <ID>]] # FLM_AREA_TAG - QF files area, 0 = "same as DB"
[ 1 container {DEFAULT | <ID>}] # FLM_CONTAINER_TAG - indexes span only one container
[ 1 count [ KEYS &| REFS]] # FLM_COUNT_TAG - key count of keys and/or refs
[ 1 language {US | <language>}] # FLM_LANGUAGE_TAG - for full-text parsing and/or sorting
[ 1 positioning] # FLM_POSITIONING_TAG - full reference counts at all b-tree elements
[ 1 encdef <EncryptionDefId>] # FLM_ENCDEF_TAG - identify the encryption definition to use
1 key [EACHWORD] # FLM_KEY_TAG - 'use' defaults based on type
[ 2 base <ID>] # FLM_BASE_TAG - base rec/field for fields below
[ 2 combinations <below> # FLM_COMBINATIONS_TAG - how to handle repeating fields
{ ALL | NORMALIZED}]
[ 2 post] # FLM_POST_TAG - case-flags post-pended to key
[ 2 required*] # FLM_REQUIRED_TAG - key value is required
[ 2 unique] # FLM_UNIQUE_TAG - key has only 1 reference
{ 2 <field> }... # FLM_FIELD_TAG - compound key if 2 or more
[ 3 case mixed | upper] # FLM_CASE_TAG - text-only, define chars case
[ 3 <field>]... # FLM_FIELD_TAG - alternate field(s)
[ 3 paired] # FLM_PAIRED_TAG - add field ID to key
[ 3 optional* # FLM_OPTIONAL_TAG - component's value is optional
| 3 required] # FLM_REQUIRED_TAG - component's value is required
[ 3 use eachword|value|field|minspaces|nounderscore|nospace|nodash] # FLM_USE_TAG
<field> ==
n field <field path> # path identifies field -- maybe "based"
[ m type <data type>] # FLM_TYPE_TAG - only for ixing unregistered fields
Please Note: This code only supports the minimal old 11 index format
needed for skads databases.
****************************************************************************/
FSTATIC RCODE DDIxParse(
TDICT * pTDict,
DDENTRY * pDDEntry, // Points to defined entry.
FlmRecord * pRecord, // Index definition record.
void * pvField)
{
RCODE rc = FERR_OK;
FLMUINT uiIfdFlags;
FLMUINT uiTempIfdFlags;
FLMUINT uiBaseNum;
FLMUINT uiNLen;
TIXD * pTIxd;
TIFD * pLastTIfd;
TIFD * pTIfd;
void * pvTempField = NULL;
void * pvIfdField = NULL;
char szNativeBuf[ 64];
FLMUINT uiCompoundPos;
FLMUINT uiTemp;
FLMBOOL bHasRequiredTag = TRUE;
FLMBOOL bOld11Mode = FALSE;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIXD),
(void **)&pTIxd)))
{
goto Exit;
}
pTIxd->pNextTIfd = NULL;
pTIxd->uiFlags = 0;
pTIxd->uiContainerNum = FLM_DATA_CONTAINER;
pTIxd->uiNumFlds = 0;
pTIxd->uiLanguage = pTDict->uiDefaultLanguage;
pTIxd->uiEncId = 0;
if( (pvField = pRecord->firstChild( pRecord->root())) == NULL)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
pLastTIfd = NULL;
for( ; pvField; pvField = pRecord->nextSibling( pvField))
{
switch( pRecord->getFieldID( pvField))
{
case FLM_CONTAINER_TAG:
{
char szTmpBuf [50];
FLMUINT uiLen = sizeof( szTmpBuf);
// See if a special keyword is used - ALL or *
if ((pRecord->getDataType( pvField) == FLM_TEXT_TYPE) &&
(RC_OK( pRecord->getNative( pvField, szTmpBuf, &uiLen))) &&
(f_stricmp( "ALL", szTmpBuf) == 0 ||
f_stricmp( "*", szTmpBuf) == 0))
{
if (pTDict->pDb->pFile->FileHdr.uiVersionNum <
FLM_FILE_FORMAT_VER_4_50)
{
rc = RC_SET( FERR_UNSUPPORTED_FEATURE);
goto Exit;
}
// Zero will mean all containers
pTIxd->uiContainerNum = 0;
}
else
{
if( RC_BAD( rc = DDGetReference( pRecord, pvField, NULL,
&pTIxd->uiContainerNum)))
{
goto Exit;
}
if( pTIxd->uiContainerNum == 0)
{
pTIxd->uiContainerNum = FLM_DATA_CONTAINER;
}
}
break;
}
case FLM_COUNT_TAG:
{
pTIxd->uiFlags |= IXD_COUNT;
break;
}
case FLM_LANGUAGE_TAG:
{
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvField, szNativeBuf, &uiNLen);
pTIxd->uiLanguage = f_languageToNum( szNativeBuf);
break;
}
case FLM_ENCDEF_TAG:
{
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvField, szNativeBuf, &uiNLen);
pTIxd->uiEncId = f_atoud( szNativeBuf);
flmAssert( pTIxd->uiEncId);
break;
}
case FLM_TYPE_TAG:
{
bOld11Mode = TRUE;
break;
}
case FLM_POSITIONING_TAG:
{
if (pTDict->pDb->pFile->FileHdr.uiVersionNum >=
FLM_FILE_FORMAT_VER_4_3)
{
pTIxd->uiFlags |= IXD_POSITIONING;
}
else
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
break;
}
case FLM_FIELD_TAG:
{
uiCompoundPos = 0;
uiBaseNum = 0;
uiIfdFlags = IFD_FIELD;
bHasRequiredTag = TRUE;
pvTempField = pvField;
bOld11Mode = TRUE;
goto Parse_Fields;
}
case FLM_KEY_TAG:
{
uiCompoundPos = 0;
uiBaseNum = 0;
uiIfdFlags = IFD_FIELD | IFD_OPTIONAL;
bHasRequiredTag = FALSE;
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvField, szNativeBuf, &uiNLen);
if( f_strnicmp( szNativeBuf, "EACH", 4) == 0)
{
pTIxd->uiFlags |= IXD_EACHWORD;
uiIfdFlags = IFD_EACHWORD | IFD_OPTIONAL;
}
if( (pvTempField = pRecord->firstChild( pvField)) == NULL)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
Parse_Fields:
for( ; pvTempField; pvTempField = pRecord->nextSibling( pvTempField))
{
switch( pRecord->getFieldID( pvTempField))
{
case FLM_BASE_TAG:
{
if( RC_BAD( rc = DDGetReference( pRecord,
pvTempField, NULL, &uiBaseNum)))
{
goto Exit;
}
break;
}
case FLM_COMBINATIONS_TAG:
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
case FLM_POST_TAG:
{
pTIxd->uiFlags |= IXD_HAS_POST;
uiIfdFlags |= IFD_POST;
break;
}
case FLM_REQUIRED_TAG:
{
break;
}
case FLM_OPTIONAL_TAG:
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
case FLM_UNIQUE_TAG:
{
pTIxd->uiFlags |= IXD_UNIQUE;
uiIfdFlags |= IFD_UNIQUE_PIECE;
break;
}
case FLM_FIELD_TAG:
{
pTIxd->uiNumFlds++;
if( bOld11Mode)
{
pvField = pvTempField;
}
// Need to set IFD_COMPOUND if there is more than one field.
if( pTIxd->uiNumFlds == 1 &&
(pRecord->find( pvTempField, FLM_FIELD_TAG, 2) != NULL))
{
uiIfdFlags |= IFD_COMPOUND;
}
pTIfd = pLastTIfd;
if( RC_BAD(rc = DDBuildFldPath( pTDict, &pLastTIfd,
pRecord, pvTempField, uiBaseNum)))
{
goto Exit;
}
pLastTIfd->uiCompoundPos = uiCompoundPos++;
if( !pTIfd)
{
pTIxd->pNextTIfd = pLastTIfd;
}
else
{
pTIfd->pNextTIfd = pLastTIfd;
}
uiTempIfdFlags = uiIfdFlags;
if( bOld11Mode)
{
uiTempIfdFlags &= ~IFD_OPTIONAL;
uiTempIfdFlags |= (IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET);
}
for( pvIfdField = pRecord->firstChild( pvTempField);
pvIfdField; pvIfdField = pRecord->nextSibling( pvIfdField))
{
switch ( pRecord->getFieldID( pvIfdField))
{
case FLM_CASE_TAG:
{
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen);
if( f_strnicmp( szNativeBuf, "UPPE", 4) == 0)
{
uiTempIfdFlags |= IFD_UPPER;
}
break;
}
case FLM_FIELD_TAG:
{
break;
}
case FLM_OPTIONAL_TAG:
{
if( bOld11Mode)
{
// Old 11 format - default for each field is required.
uiTempIfdFlags |= IFD_OPTIONAL;
uiTempIfdFlags &= ~(IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET);
}
// New format default is optional
break;
}
case FLM_PAIRED_TAG:
{
uiTempIfdFlags |= IFD_FIELDID_PAIR;
break;
}
case FLM_POST_TAG:
{
uiTempIfdFlags |= IFD_POST;
break;
}
case FLM_REQUIRED_TAG:
{
bHasRequiredTag = TRUE;
uiTempIfdFlags &= ~IFD_OPTIONAL;
uiTempIfdFlags |= (IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET);
break;
}
case FLM_LIMIT_TAG:
{
if( RC_BAD( pRecord->getUINT( pvIfdField, &uiTemp)) ||
uiTemp > IFD_DEFAULT_LIMIT)
{
pLastTIfd->uiLimit = IFD_DEFAULT_LIMIT;
}
else
{
pLastTIfd->uiLimit = uiTemp;
}
break;
}
case FLM_UNIQUE_TAG:
{
uiTempIfdFlags |= IFD_UNIQUE_PIECE;
pTIxd->uiFlags |= IXD_UNIQUE;
break;
}
case FLM_USE_TAG:
{
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen);
if( f_strnicmp( szNativeBuf, "EACH", 4) == 0)
{
uiTempIfdFlags |= IFD_EACHWORD;
uiTempIfdFlags &= ~(IFD_VALUE|IFD_SUBSTRING);
}
else if( f_strnicmp( szNativeBuf, "SUBS", 4) == 0)
{
pTIxd->uiFlags |= IXD_HAS_SUBSTRING;
uiTempIfdFlags |= IFD_SUBSTRING;
uiTempIfdFlags &= ~(IFD_VALUE|IFD_EACHWORD);
if( pLastTIfd->uiLimit == IFD_DEFAULT_LIMIT)
{
pLastTIfd->uiLimit = IFD_DEFAULT_SUBSTRING_LIMIT;
}
}
else if( f_strnicmp( szNativeBuf, "VALU", 4) == 0)
{
uiTempIfdFlags |= IFD_VALUE;
uiTempIfdFlags &= ~(IFD_EACHWORD|IFD_SUBSTRING);
}
else if( f_strnicmp( szNativeBuf, "FIEL", 4) == 0)
{
uiTempIfdFlags |= IFD_CONTEXT;
uiTempIfdFlags &= ~(IFD_VALUE|IFD_EACHWORD|IFD_SUBSTRING);
}
break;
}
case FLM_FILTER_TAG:
{
uiNLen = sizeof( szNativeBuf);
(void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen);
if( f_strnicmp( szNativeBuf, "MINS", 4) == 0)
{
uiTempIfdFlags |= IFD_MIN_SPACES;
}
else if( f_strnicmp( szNativeBuf, "NOUN", 4) == 0)
{
uiTempIfdFlags |= IFD_NO_UNDERSCORE;
}
else if( f_strnicmp( szNativeBuf, "NOSP", 4) == 0)
{
uiTempIfdFlags |= IFD_NO_SPACE;
}
else if( f_strnicmp( szNativeBuf, "NODA", 4) == 0)
{
uiTempIfdFlags |= IFD_NO_DASH;
}
else
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
break;
}
default:
{
if( pRecord->getFieldID( pvIfdField) < FLM_UNREGISTERED_TAGS &&
pRecord->getFieldID( pvIfdField) != FLM_COMMENT_TAG)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
break;
}
}
}
// Parse again the level 3 field definitions. Now we
// have the IFD uiFlags value to assign each piece that
// will have the same compound position.
pLastTIfd->uiFlags |= uiTempIfdFlags;
for( pvIfdField = pRecord->firstChild( pvTempField);
pvIfdField; pvIfdField = pRecord->nextSibling( pvIfdField))
{
if( pRecord->getFieldID( pvIfdField) == FLM_FIELD_TAG )
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
break;
}
default:
{
if( bOld11Mode)
{
break;
}
if( pRecord->getFieldID( pvTempField) < FLM_UNREGISTERED_TAGS &&
pRecord->getFieldID( pvTempField) != FLM_COMMENT_TAG)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
break;
}
}
}
// Special case for optional
if( !bHasRequiredTag)
{
// Set all of the IFD flags to IFD_REQUIRED_IN_SET
for( pTIfd = pTIxd->pNextTIfd; pTIfd; pTIfd = pTIfd->pNextTIfd)
{
pTIfd->uiFlags |= IFD_REQUIRED_IN_SET;
}
}
break;
}
default:
{
if( pRecord->getFieldID( pvField) < FLM_UNREGISTERED_TAGS &&
pRecord->getFieldID( pvField) != FLM_COMMENT_TAG)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
break;
}
}
}
pDDEntry->vpDef = (void *) pTIxd;
Exit:
if( RC_BAD(rc))
{
if( pvIfdField)
{
pTDict->uiBadField = pRecord->getFieldID( pvIfdField);
}
else if( pvTempField)
{
pTDict->uiBadField = pRecord->getFieldID( pvTempField);
}
else if( pvField)
{
pTDict->uiBadField = pRecord->getFieldID( pvField);
}
}
else
{
pTDict->uiNewIxds++;
pTDict->uiNewIfds += pTIxd->uiNumFlds;
pTDict->uiNewLFiles++;
}
return( rc);
}
/****************************************************************************
Desc: Build field path for each index field. This function will also
check for the existence of the 'batch' option for QF indexes.
****************************************************************************/
FSTATIC RCODE DDBuildFldPath(
TDICT * pTDict,
TIFD ** ppTIfd,
FlmRecord * pRecord,
void * pvField,
FLMUINT uiBaseNum)
{
RCODE rc = FERR_OK;
TIFD * pTIfd;
TIFP * pLastFldPath;
TIFP * pTIfp;
FLMUINT uiNumInFldPath;
char szNameBuf[ 32];
char * pszCurrent;
char szNativeBuf[ FDD_MAX_VALUE_SIZE];
FLMUINT uiBufLen;
FLMUINT uiPos;
pTDict->uiTotalIfds++;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFD), (void **)&pTIfd)))
{
goto Exit;
}
pTIfd->pTIfp = NULL;
pTIfd->pNextTIfd = NULL;
pTIfd->uiFlags = 0;
pTIfd->uiNextFixupPos = 0;
pTIfd->uiLimit = IFD_DEFAULT_LIMIT;
pTIfd->uiCompoundPos = 0;
pLastFldPath = NULL;
*ppTIfd = pTIfd;
// Build the field paths
DDTextToNative( pRecord, pvField, szNativeBuf,
FDD_MAX_VALUE_SIZE, &uiBufLen);
pszCurrent = szNativeBuf;
uiNumInFldPath = uiPos = 0;
if( uiBaseNum)
{
uiNumInFldPath++;
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp)))
{
goto Exit;
}
pTIfp->pNextTIfp = NULL;
pTIfp->bFieldInThisDict = FALSE;
pTIfp->uiFldNum = uiBaseNum;
pTIfd->pTIfp = pTIfp;
pLastFldPath = pTIfp;
}
while( uiPos < uiBufLen)
{
uiNumInFldPath++;
if( DDMoveWord( szNameBuf, pszCurrent,
sizeof( szNameBuf ), &uiPos ) == FALSE )
{
break;
}
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp)))
{
goto Exit;
}
pTIfp->pNextTIfp = NULL;
pTIfp->bFieldInThisDict = FALSE;
if( pTIfd->pTIfp == NULL)
{
pTIfd->pTIfp = pTIfp;
}
else
{
pLastFldPath->pNextTIfp = pTIfp;
}
pLastFldPath = pTIfp;
// See if there is a wildcard in the path.
if (f_stricmp( szNameBuf, "*") == 0)
{
if (pTDict->pDb->pFile->FileHdr.uiVersionNum <
FLM_FILE_FORMAT_VER_4_50)
{
rc = RC_SET( FERR_UNSUPPORTED_FEATURE);
goto Exit;
}
else
{
pTIfp->uiFldNum = FLM_ANY_FIELD;
}
}
else
{
if( RC_BAD( rc = DDGetReference( NULL, NULL, szNameBuf,
&pTIfp->uiFldNum)))
{
goto Exit;
}
}
}
if( uiNumInFldPath == 0)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
// Cannot have wildcard in last field of field path.
if (pLastFldPath->uiFldNum == FLM_ANY_FIELD)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
// Single field has the field NULL terminated
if( uiNumInFldPath == 1 )
{
pTDict->uiNewFldPaths += 2;
}
else
{
// The field paths are stored child to parent and parent to child
// each are zero terminated.
pTDict->uiNewFldPaths += 2 * (uiNumInFldPath + 1);
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Parse a data dictionary domain definition for correct syntax &
assign the correct attributes.
****************************************************************************/
FSTATIC RCODE DDContainerParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord)
{
RCODE rc = FERR_OK;
void * pvField = NULL;
if( pDDEntry)
{
if( (pvField = pRecord->firstChild( pRecord->root())) != NULL)
{
for( ; pvField; pvField = pRecord->nextSibling( pvField))
{
// Only option is unregistered fields
if( pRecord->getFieldID( pvField) < FLM_FREE_TAG_NUMS)
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
}
}
Exit:
if( RC_BAD(rc) && pvField)
{
pTDict->uiBadField = pRecord->getFieldID( pvField);
}
return( rc );
}
/****************************************************************************
Desc: Parse a data dictionary domain definition for correct syntax &
assign the correct attributes.
****************************************************************************/
FSTATIC RCODE DDEncDefParse(
TDICT * pTDict,
DDENTRY * pDDEntry,
FlmRecord * pRecord)
{
RCODE rc = FERR_OK;
void * pvField = NULL;
TENCDEF * pTEncDef;
// Make sure the version of the database is correct for encryption.
if (pTDict->pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60)
{
rc = RC_SET( FERR_UNSUPPORTED_FEATURE);
goto Exit;
}
if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TENCDEF),
(void **)&pTEncDef)))
{
goto Exit;
}
pTEncDef->uiAlgType = 0;
pTEncDef->uiState = 0;
pTEncDef->pucKeyInfo = NULL;
pTEncDef->uiLength = 0;
if( pDDEntry)
{
if( (pvField = pRecord->firstChild( pRecord->root())) != NULL)
{
for( ; pvField; pvField = pRecord->nextSibling( pvField))
{
switch ( pRecord->getFieldID( pvField) )
{
case FLM_TYPE_TAG:
{
// Get the encryption type.
if (RC_BAD( rc = DDGetEncType( pRecord,
pvField,
&pTEncDef->uiAlgType)))
{
goto Exit;
}
break;
}
case FLM_KEY_TAG:
{
// Get the key information.
if (RC_BAD( rc = DDGetEncKey( pTDict,
pRecord,
pvField,
pTEncDef)))
{
goto Exit;
}
break;
}
case FLM_STATE_TAG:
{
// Get the status information.
if (RC_BAD( rc = DDParseStateOptions( pRecord,
pvField,
&pTEncDef->uiState)))
{
goto Exit;
}
break;
}
default:
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
}
pDDEntry->vpDef = (void *)pTEncDef;
}
else
{
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
}
Exit:
if (RC_BAD( rc) && pvField)
{
pTDict->uiBadField = pRecord->getFieldID( pvField);
}
return( rc );
}
/****************************************************************************
Desc: Move word delimited by spaces from src to dest. Used to move a
word at a time for field path lists.
Notes: Isolated so changes can be made to delemeting NAMES.
Visit: Still bugs when name > buffer size - won't happen because only #'s
****************************************************************************/
FSTATIC FLMBOOL DDMoveWord(
char * pucDest,
char * pucSrc,
FLMUINT uiMaxDestLen,
FLMUINT * puiPos)
{
FLMBOOL bFoundWord = TRUE;
FLMUINT uiPos = *puiPos;
char * pMatch;
FLMUINT uiBytesToCopy;
pucSrc += uiPos;
while( *pucSrc == NATIVE_SPACE)
{
pucSrc++;
}
pMatch = pucSrc;
while( *pMatch > NATIVE_SPACE)
{
pMatch++;
}
if( !*pMatch)
{
if( *pucSrc == '\0')
{
bFoundWord = FALSE;
goto Exit;
}
uiBytesToCopy = f_strlen( pucSrc);
if( uiBytesToCopy + 1 > uiMaxDestLen)
{
uiBytesToCopy = uiMaxDestLen - 1;
}
f_memcpy( pucDest, pucSrc, uiBytesToCopy + 1);
*puiPos = uiPos + uiBytesToCopy + 1;
}
else
{
// Copy the bytes between pucSrc and pMatch minus one
uiBytesToCopy = (FLMUINT) (pMatch - pucSrc);
if( uiBytesToCopy + 1 > uiMaxDestLen)
{
uiBytesToCopy = uiMaxDestLen - 1;
}
f_memcpy( pucDest, pucSrc, uiBytesToCopy );
pucDest[ uiBytesToCopy ] = '\0';
// Go past consuctive spaces
while( pucSrc[ ++uiBytesToCopy ] == NATIVE_SPACE)
{
uiBytesToCopy++;
}
*puiPos = uiPos + uiBytesToCopy;
}
Exit:
return( bFoundWord);
}
/****************************************************************************
Desc: Normalizes an internal string with possible formatting codes into
a NATIVE string. Drops all formatting codes and extended chars.
****************************************************************************/
FSTATIC void DDTextToNative(
FlmRecord * pRecord,
void * pvField,
char * pszBuffer,
FLMUINT uiBufLen,
FLMUINT * puiBufLen)
{
RCODE rc = FERR_OK;
pszBuffer[ 0] = 0;
if( pRecord->getDataLength( pvField))
{
if( RC_BAD( rc = pRecord->getNative( pvField, pszBuffer, &uiBufLen)))
{
if( rc != FERR_CONV_DEST_OVERFLOW)
{
pszBuffer[0] = 0;
uiBufLen = 0;
}
}
}
else
{
uiBufLen = 0;
}
if( puiBufLen)
{
// Length needs to include the null byte
*puiBufLen = uiBufLen + 1;
}
return;
}
/****************************************************************************
Desc: Allocate the LFILE and read in the LFile entries. The default
data container and the dictionary container will be at hard coded
slots at the first of the table. The LFiles do not need to be in
any numeric order.
****************************************************************************/
RCODE fdictReadLFiles(
FDB * pDb,
FDICT * pDict)
{
RCODE rc = FERR_OK;
LFILE * pLFiles = NULL;
LFILE * pLFile;
SCACHE * pSCache = NULL;
FLMBOOL bReleaseCache = FALSE;
FLMBYTE * pucBlk;
FLMUINT uiBlkAddress;
FLMUINT uiPos;
FLMUINT uiEndPos;
FLMUINT uiEstCount;
FLMUINT uiLFileCnt;
FLMUINT uiLFHCnt;
FFILE * pFile = pDb->pFile;
FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize;
LFILE TmpLFile;
f_memset( &TmpLFile, 0, sizeof( LFILE));
for( uiEstCount = 0, uiLFileCnt = 4,
uiBlkAddress = pDb->pFile->FileHdr.uiFirstLFHBlkAddr;
uiBlkAddress != BT_END; )
{
if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LFH_BLK,
uiBlkAddress, NULL, &pSCache)))
{
goto Exit;
}
bReleaseCache = TRUE;
pucBlk = pSCache->pucBlk;
uiPos = BH_OVHD;
if( (uiEndPos = (FLMUINT)FB2UW( &pucBlk[ BH_ELM_END])) <= BH_OVHD)
{
uiEndPos = BH_OVHD;
uiLFHCnt = 0;
}
else
{
if( uiEndPos > uiBlkSize)
{
uiEndPos = uiBlkSize;
}
uiLFHCnt = (FLMUINT)((uiEndPos - BH_OVHD) / LFH_SIZE);
uiEndPos = (FLMUINT)(BH_OVHD + uiLFHCnt * 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( LFILE), &pLFiles)))
{
goto Exit;
}
}
}
else if( uiLFHCnt)
{
uiEstCount += uiLFHCnt;
if( RC_BAD(rc = f_recalloc( uiEstCount * sizeof(LFILE), &pLFiles)))
{
goto Exit;
}
}
// Read through all of the logical file definitions in the block
for( ; uiPos < uiEndPos; uiPos += LFH_SIZE)
{
FLMUINT uiLfNum;
// Have to fix up the offsets later when they are read in
if( RC_BAD( rc = flmBufferToLFile( &pucBlk[ uiPos], &TmpLFile,
uiBlkAddress, uiPos)))
{
goto Exit;
}
if( TmpLFile.uiLfType == LF_INVALID)
{
continue;
}
uiLfNum = TmpLFile.uiLfNum;
if( uiLfNum == FLM_DATA_CONTAINER)
{
pLFile = pLFiles + LFILE_DATA_CONTAINER_OFFSET;
}
else if( uiLfNum == FLM_DICT_CONTAINER)
{
pLFile = pLFiles + LFILE_DICT_CONTAINER_OFFSET;
}
else if( uiLfNum == FLM_DICT_INDEX)
{
pLFile = pLFiles + LFILE_DICT_INDEX_OFFSET;
}
else if( uiLfNum == FLM_TRACKER_CONTAINER)
{
pLFile = pLFiles + LFILE_TRACKER_CONTAINER_OFFSET;
}
else
{
pLFile = pLFiles + uiLFileCnt++;
}
f_memcpy( pLFile, &TmpLFile, sizeof(LFILE));
}
// Get the next block in the chain
uiBlkAddress = (FLMUINT)FB2UD( &pucBlk[ BH_NEXT_BLK]);
ScaReleaseCache( pSCache, FALSE);
bReleaseCache = FALSE;
}
// This routine could be called to re-read in the dictionary.
if( pDict->pLFileTbl)
{
f_free( &pDict->pLFileTbl);
}
pDict->pLFileTbl = pLFiles;
pDict->uiLFileCnt = uiLFileCnt;
Exit:
if( bReleaseCache)
{
ScaReleaseCache( pSCache, FALSE);
}
if( RC_BAD(rc) && pLFiles)
{
f_free( &pLFiles);
}
return( rc);
}
/****************************************************************************
Desc: Add data dictionary records to the data dictionary.
****************************************************************************/
RCODE fdictCreate(
FDB * pDb,
const char * pszDictPath,
const char * pDictBuf)
{
RCODE rc = FERR_OK;
IF_FileHdl * pDictFileHdl = NULL;
FlmRecord * pDictRec = NULL;
void * pvField;
const char * pucGedBuf;
LFILE * pDictContLFile;
LFILE * pDictIxLFile;
FLMUINT uiDrn = 0;
FLMUINT uiCurrDictNum;
FLMUINT uiLFileCount;
LFILE DictContLFile;
LFILE DictIxLFile;
LFILE TempLFile;
char ucTempBuf[ 256];
FLMUINT uiBufLen = sizeof( ucTempBuf);
F_NameTable nameTable;
// Initialize the name table
if( RC_BAD( rc = nameTable.setupFromDb( HFDB_NULL)))
{
goto Exit;
}
// Create Dictionary and Default Data containers
if( RC_BAD(rc = flmLFileCreate( pDb, &DictContLFile, FLM_DICT_CONTAINER,
LF_CONTAINER)))
{
goto Exit;
}
uiCurrDictNum = FLM_DICT_CONTAINER;
if( RC_BAD(rc = flmLFileCreate( pDb, &TempLFile, FLM_DATA_CONTAINER,
LF_CONTAINER)))
{
goto Exit;
}
if( RC_BAD( rc = flmLFileCreate( pDb, &DictIxLFile, FLM_DICT_INDEX,
LF_INDEX)))
{
goto Exit;
}
if( RC_BAD( rc = flmLFileCreate( pDb, &TempLFile, FLM_TRACKER_CONTAINER,
LF_CONTAINER)))
{
goto Exit;
}
uiLFileCount = 4;
// If we have a GEDCOM buffer, there is no need to open the file
if( pDictBuf)
{
pucGedBuf = pDictBuf;
uiBufLen = f_strlen( pDictBuf) + 1;
}
else if( pszDictPath)
{
pucGedBuf = ucTempBuf;
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
pszDictPath, FLM_IO_RDONLY, &pDictFileHdl)))
{
goto Exit;
}
}
else
{
// Neither a dictionary buffer or file were specified. Create will
// be done with an empty dictionary.
goto Done_Getting_Dict;
}
// Create a new FDICT so we can write the dictionary records.
// This FDICT is temporary and will be allocated again.
if( RC_BAD( rc = fdictCreateNewDict( pDb)))
{
goto Exit;
}
if( (pDictRec = f_new FlmRecord) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = fdictGetContainer( pDb->pDict,
FLM_DICT_CONTAINER, &pDictContLFile)))
{
goto Exit;
}
if( RC_BAD( rc = fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode,
FLM_DICT_INDEX, &pDictIxLFile, NULL)))
{
goto Exit;
}
// Read through the dictionary records, adding them or creating dictionaries
// as we go.
for( ;;)
{
// Get records from buffer or file
rc = ( pDictFileHdl)
? pDictRec->importRecord( pDictFileHdl, &nameTable)
: pDictRec->importRecord( &pucGedBuf, uiBufLen, &nameTable);
if( RC_BAD( rc))
{
if( rc == FERR_END || rc == FERR_EOF_HIT)
{
rc = FERR_OK;
break;
}
else if( uiDrn)
{
// If an error occur then at least set the DRN of the
// previous record in the diagnostic information.
pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN;
pDb->Diag.uiDrn = uiDrn;
}
goto Exit;
}
// See if we are switching dictionaries.
pvField = pDictRec->root();
if( pDictRec->getFieldID( pvField) == FLM_DICT_TAG)
{
rc = RC_SET( FERR_INVALID_TAG);
goto Exit;
}
// Assign all fields a DRN value - parse for completeness.
// If there is no DRN in the record (zero), one will be assigned
// by FDDDictRecUpdate.
uiDrn = pDictRec->getID();
// Add the data dictionary record. This also checks to see
// if the record is already defined.
if( RC_BAD( rc = fdictRecUpdate( pDb, pDictContLFile,
pDictIxLFile, &uiDrn, pDictRec, NULL)))
{
goto Exit;
}
// Don't need to do the processing below if it is not a record
// being put into the dictionary.
if( uiCurrDictNum != FLM_DICT_CONTAINER)
{
continue;
}
// Create an LFILE for each index and container.
if( pDictRec->getFieldID( pvField) == FLM_INDEX_TAG ||
pDictRec->getFieldID( pvField) == FLM_CONTAINER_TAG)
{
pvField = pDictRec->root();
if( RC_BAD( rc = flmLFileCreate( pDb, &TempLFile, uiDrn,
((pDictRec->getFieldID( pvField) == FLM_INDEX_TAG)
? (FLMUINT)LF_INDEX
: (FLMUINT)LF_CONTAINER))))
{
goto Exit;
}
uiLFileCount++;
}
}
Done_Getting_Dict:
// Create the FDICT again, this time with the dictionary pcode.
if( RC_BAD( rc = fdictCreateNewDict( pDb)))
{
goto Exit;
}
Exit:
if( pDictFileHdl)
{
pDictFileHdl->Release();
}
if( pDictRec)
{
pDictRec->Release();
}
return( rc);
}
/****************************************************************************
Desc: Creates a new dictionary for a database.
This occurs when on database create and on a dictionary change.
****************************************************************************/
RCODE fdictCreateNewDict(
FDB * pDb)
{
RCODE rc = FERR_OK;
// Unlink the DB from the current FDICT, if any.
if( pDb->pDict)
{
f_mutexLock( gv_FlmSysData.hShareMutex);
flmUnlinkFdbFromDict( pDb);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
// Allocate a new FDICT structure for the new dictionary we
// are going to create.
if( RC_BAD( rc = fdictRebuild( pDb)))
{
goto Exit;
}
// Update the FDB structure to indicate that the dictionary
// was updated.
pDb->uiFlags |= FDB_UPDATED_DICTIONARY;
Exit:
// If we allocated an FDICT and there was an error, free the FDICT.
if( (RC_BAD( rc)) && (pDb->pDict))
{
flmFreeDict( pDb->pDict);
pDb->pDict = NULL;
}
return( rc);
}
/****************************************************************************
Desc: Add a new field, container or index definition to the dictionary.
****************************************************************************/
RCODE flmAddRecordToDict(
FDB * pDb,
FlmRecord * pRecord,
FLMUINT uiDictId,
FLMBOOL bRereadLFiles)
{
RCODE rc = FERR_OK;
TDICT tDict;
FLMBOOL bTDictInitialized = FALSE;
if( RC_BAD( rc = fdictCopySkeletonDict( pDb)))
{
goto Exit;
}
bTDictInitialized = TRUE;
if( RC_BAD( rc = fdictInitTDict( pDb, &tDict)))
{
goto Exit;
}
if( RC_BAD( rc = fdictProcessRec( &tDict, pRecord, uiDictId)))
{
goto Exit;
}
if( RC_BAD( rc = fdictBuildTables( &tDict, bRereadLFiles, TRUE)))
{
goto Exit;
}
pDb->uiFlags |= FDB_UPDATED_DICTIONARY;
Exit:
if( bTDictInitialized)
{
tDict.pool.poolFree();
}
// If we allocated an FDICT and there was an error, free the FDICT.
if( (RC_BAD( rc)) && (pDb->pDict))
{
flmFreeDict( pDb->pDict);
pDb->pDict = NULL;
}
return( rc);
}
/****************************************************************************
Desc: Add an index a dictionary record to the container LFILE and the
index LFILE.
****************************************************************************/
RCODE fdictRecUpdate(
FDB * pDb,
LFILE * pDictContLFile,
LFILE * pDictIxLFile,
FLMUINT * puiDrnRV,
FlmRecord * pNewRec,
FlmRecord * pOldRec,
FLMBOOL bRebuildOp)
{
RCODE rc = FERR_OK;
FLMUINT uiDrn = *puiDrnRV;
FLMBOOL bAllocatedID;
void * pvField;
FLMBYTE * pucKeyField = NULL;
FLMUINT32 ui32BufLen;
FLMUINT uiEncType;
bAllocatedID = FALSE;
// Make sure we are using a valid DRN
if( (uiDrn >= FLM_RESERVED_TAG_NUMS) &&
(uiDrn != 0xFFFFFFFF))
{
pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN;
pDb->Diag.uiDrn = uiDrn;
rc = RC_SET( FERR_BAD_DICT_DRN);
goto Exit;
}
// Allocate an unused DRN, if one has not been allocated.
if( (pNewRec) && ((!uiDrn) || (uiDrn == 0xFFFFFFFF)))
{
FLMBOOL bAllocAtEnd = (!uiDrn) ? TRUE : FALSE;
bAllocatedID = TRUE;
if( bAllocAtEnd)
{
if( RC_BAD( rc = FSGetNextDrn( pDb, pDictContLFile, FALSE, &uiDrn)))
{
goto Exit;
}
}
else
{
// Scott 12/99: This must not be called any more.
// The code merged ITT values into the table.
flmAssert(0);
}
// Verify that we are not at our highest possible dictionary DRN.
if( uiDrn >= FLM_RESERVED_TAG_NUMS)
{
rc = RC_SET( FERR_NO_MORE_DRNS);
goto Exit;
}
}
// The following code makes sure that the DRN and name have not already been
// used, if adding. It also makes sure that there is no conflict in
// the type/name index. It checks the entire shared dictionary
// hierarchy if necessary - child and parent - to ensure no
// conflicts.
if( pNewRec)
{
// Check for ID conflicts in the dictionary being added to
if( (!pOldRec) && (!bAllocatedID))
{
if( RC_BAD( rc = DDCheckIDConflict( pDb, pDictContLFile, uiDrn)))
{
if( (rc == FERR_ID_RESERVED) || (rc == FERR_DUPLICATE_DICT_REC))
{
pvField = pNewRec->root();
if( (rc == FERR_DUPLICATE_DICT_REC) &&
(pNewRec->getFieldID( pvField) == FLM_RESERVED_TAG))
{
rc = RC_SET( FERR_CANNOT_RESERVE_ID);
}
pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN;
pDb->Diag.uiDrn = uiDrn;
}
goto Exit;
}
}
// Check for name conflicts in the dictionary being added to
if (pNewRec)
{
if (RC_BAD( rc = DDCheckNameConflict( pDb, pDictIxLFile, pNewRec,
uiDrn, pOldRec)))
goto Exit;
}
}
if (!pOldRec && pNewRec)
{
// If this is an encryption definition record, we need to generate
// a new key.
if (pNewRec->getFieldID( pNewRec->root()) == FLM_ENCDEF_TAG &&
!bRebuildOp && !(pDb->uiFlags & FDB_REPLAYING_RFL))
{
F_CCS Ccs;
// If we are running in limited mode, we will not be able to complete
// this operation.
if( pDb->pFile->bInLimitedMode)
{
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
// Should not have a key yet.
if( pNewRec->find(pNewRec->root(),
FLM_KEY_TAG) != NULL)
{
rc = RC_SET( FERR_CANNOT_SET_KEY);
goto Exit;
}
if( (pvField = pNewRec->find( pNewRec->root(),
FLM_TYPE_TAG)) == NULL)
{
rc = RC_SET( FERR_MISSING_ENC_TYPE);
goto Exit;
}
if( RC_BAD( rc = DDGetEncType( pNewRec, pvField, &uiEncType)))
{
goto Exit;
}
if( RC_BAD( rc = Ccs.init( FALSE, uiEncType)))
{
goto Exit;
}
if( RC_BAD( rc = Ccs.generateEncryptionKey()))
{
goto Exit;
}
if( RC_BAD( rc = Ccs.getKeyToStore( &pucKeyField, &ui32BufLen,
NULL, pDb->pFile->pDbWrappingKey)))
{
goto Exit;
}
// Create the key field
if( RC_BAD( rc = pNewRec->insert( pNewRec->root(), INSERT_LAST_CHILD,
FLM_KEY_TAG, FLM_TEXT_TYPE, &pvField)))
{
goto Exit;
}
// Set the value of the new field
if( RC_BAD( rc = pNewRec->setNative( pvField,
(const char *)pucKeyField)))
{
goto Exit;
}
}
}
// Delete the old record and its index entries, if any
if( pOldRec)
{
// Delete the old record's index entries
if( RC_BAD( rc = DDIxDictRecord( pDb, pDictIxLFile, uiDrn,
pOldRec, KREF_DELETE_FLAG)))
{
goto Exit;
}
// Delete the old record - unless it is a modify
if( !pNewRec)
{
if( RC_BAD( rc = FSRecUpdate( pDb, pDictContLFile, NULL, uiDrn,
REC_UPD_DELETE)))
{
goto Exit;
}
}
}
// Add the new record, if any
if( pNewRec)
{
// Add the record's index keys
if( RC_BAD( rc = DDIxDictRecord( pDb, pDictIxLFile, uiDrn,
pNewRec, 0)))
{
goto Exit;
}
// Add or modify the record itself
if( RC_BAD( rc = FSRecUpdate( pDb, pDictContLFile, pNewRec,
uiDrn, (FLMUINT)((pOldRec)
? (FLMUINT)REC_UPD_MODIFY
: (FLMUINT)REC_UPD_ADD))))
{
goto Exit;
}
}
Exit:
if( RC_OK( rc))
{
*puiDrnRV = uiDrn;
}
if (pucKeyField)
{
f_free( &pucKeyField);
}
return( rc);
}
/****************************************************************************
Desc: Creates a collated type/name key for a dictionary record.
****************************************************************************/
FSTATIC RCODE DDMakeDictIxKey(
FDB * pDb,
FlmRecord * pRecord,
FLMBYTE * pKeyBuf,
FLMUINT * puiKeyLenRV)
{
RCODE rc = FERR_OK;
FLMUINT uiElmLen;
FLMUINT uiKeyLen = 0;
void * pvField = pRecord->root();
const FLMBYTE * pExportPtr;
// Collate the name
pExportPtr = pRecord->getDataPtr( pvField),
uiElmLen = MAX_KEY_SIZ - uiKeyLen;
if( RC_BAD( rc = KYCollateValue( &pKeyBuf [uiKeyLen], &uiElmLen,
pExportPtr, pRecord->getDataLength( pvField), FLM_TEXT_TYPE,
uiElmLen, NULL, NULL, pDb->pFile->FileHdr.uiDefaultLanguage,
FALSE, FALSE, FALSE, NULL)))
{
goto Exit;
}
uiKeyLen += uiElmLen;
Exit:
*puiKeyLenRV = uiKeyLen;
return( rc);
}
/****************************************************************************
Desc: Checks to make sure a dictionary name has not already been used.
****************************************************************************/
FSTATIC RCODE DDCheckNameConflict(
FDB * pDb,
LFILE * pDictIxLFile,
FlmRecord * pNewRec,
FLMUINT uiDrn,
FlmRecord * pOldRec)
{
RCODE rc = FERR_OK;
BTSK StackArray[ BH_MAX_LEVELS];
BTSK * pStack;
FLMBYTE BtKeyBuf[ MAX_KEY_SIZ];
FLMBYTE IxKeyBuf[ MAX_KEY_SIZ];
FLMUINT uiKeyLen;
void * pvField;
FSInitStackCache( &StackArray [0], BH_MAX_LEVELS);
if (RC_BAD( rc = DDMakeDictIxKey( pDb, pNewRec, IxKeyBuf, &uiKeyLen)))
{
goto Exit;
}
StackArray[0].pKeyBuf = BtKeyBuf;
pStack = StackArray;
if (RC_BAD( rc = FSBtSearch( pDb, pDictIxLFile, &pStack,
IxKeyBuf, uiKeyLen, 0L)))
{
if (rc == FERR_EOF_HIT)
{
// No matching dictionary-name key exists yet, so there is no
// name conflict. Treat end-of-index as a successful miss.
rc = FERR_OK;
}
else
{
goto Exit;
}
}
if (pStack->uiCmpStatus == BT_EQ_KEY)
{
FLMUINT uiElmDoman;
DIN_STATE DinState;
FLMUINT uiFoundDrn;
// If this is an ADD (!pOldRec), or the record found is different than
// the one being updated, we have a problem.
uiFoundDrn = FSRefFirst( pStack, &DinState, &uiElmDoman);
if ((!pOldRec) || (uiFoundDrn != uiDrn))
{
pvField = pNewRec->root();
pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN;
pDb->Diag.uiDrn = uiDrn;
rc = (pNewRec->getFieldID( pvField) == FLM_RESERVED_TAG)
? RC_SET( FERR_CANNOT_RESERVE_NAME)
: RC_SET( FERR_DUPLICATE_DICT_NAME);
goto Exit;
}
}
Exit:
FSReleaseStackCache( StackArray, BH_MAX_LEVELS, FALSE);
return( rc);
}
/****************************************************************************
Desc: Checks to make sure a dictionary DRN has not already been used.
****************************************************************************/
FSTATIC RCODE DDCheckIDConflict(
FDB * pDb,
LFILE * pDictContLFile,
FLMUINT uiDrn)
{
RCODE rc = FERR_OK;
FlmRecord * pOldRec = NULL;
// Read to see if there is an existing record.
// NOTE: Deliberately not bringing into cache if not found.
if( RC_BAD( rc = FSReadRecord( pDb, pDictContLFile, uiDrn,
&pOldRec, NULL, NULL)))
{
if (rc == FERR_NOT_FOUND)
{
rc = FERR_OK;
}
else
{
goto Exit;
}
}
if( pOldRec)
{
void * pvField = pOldRec->root();
rc = ( pOldRec->getFieldID( pvField) == FLM_RESERVED_TAG)
? RC_SET( FERR_ID_RESERVED)
: RC_SET( FERR_DUPLICATE_DICT_REC);
}
Exit:
if( pOldRec)
{
pOldRec->Release();
}
return( rc);
}
/****************************************************************************
Desc: Generate a key for an index record and add or delete it from
the index.
****************************************************************************/
FSTATIC RCODE DDIxDictRecord(
FDB * pDb,
LFILE * pDictIxLFile,
FLMUINT uiDrn,
FlmRecord * pRecord,
FLMUINT uiFlags)
{
RCODE rc;
union
{
FLMBYTE KeyBuf [sizeof( KREF_ENTRY) + MAX_KEY_SIZ];
KREF_ENTRY KrefEntry;
};
FLMUINT uiKeyLen;
flmAssert( pDictIxLFile->uiLfNum > 0 &&
pDictIxLFile->uiLfNum < FLM_UNREGISTERED_TAGS); // Sanity check
KrefEntry.ui16IxNum = (FLMUINT16)pDictIxLFile->uiLfNum;
KrefEntry.uiDrn = uiDrn;
KrefEntry.uiFlags = uiFlags;
KrefEntry.uiTrnsSeq = 1;
// Add or delete the key/reference
if (RC_BAD( rc = DDMakeDictIxKey( pDb, pRecord,
&KeyBuf [sizeof( KREF_ENTRY)], &uiKeyLen)))
{
goto Exit;
}
KrefEntry.ui16KeyLen = (FLMUINT16)uiKeyLen;
if( RC_BAD( rc = FSRefUpdate( pDb, pDictIxLFile, &KrefEntry)))
{
goto Exit;
}
Exit:
return( rc);
}
/***************************************************************************
Desc: Get the field information. Try shared first and then local.
****************************************************************************/
RCODE fdictGetField(
FDICT * pDict,
FLMUINT uiFieldNum, // [in] Field Number to look up
FLMUINT * puiFieldType, // [out] Optional
IFD ** ppFirstIfd, // [out] Optional
FLMUINT * puiFieldState) // [out] Optional
{
RCODE rc = FERR_OK;
ITT nonstandardItt;
ITT * pItt;
if( pDict && pDict->pIttTbl && uiFieldNum < pDict->uiIttCnt)
{
pItt = &pDict->pIttTbl[ uiFieldNum];
// Is it really a field?
if( ! ITT_IS_FIELD( pItt))
{
rc = RC_SET( FERR_BAD_FIELD_NUM);
goto Exit;
}
}
else
{
// Check if the field is a FLAIM dictionary field.
// Most of these fields are TEXT fields.
if( (uiFieldNum >= FLM_DICT_FIELD_NUMS)
&& (uiFieldNum <= FLM_LAST_DICT_FIELD_NUM))
{
// Most of the dictionary fields are text type.
// KYBUILD now doesn't verify unregistered or dictionary fields types.
pItt = &nonstandardItt;
nonstandardItt.uiType = FLM_TEXT_TYPE;
nonstandardItt.pvItem = NULL;
}
else if( uiFieldNum >= FLM_UNREGISTERED_TAGS)
{
pItt = &nonstandardItt;
nonstandardItt.uiType = FLM_TEXT_TYPE;
nonstandardItt.pvItem = NULL;
}
else
{
rc = RC_SET( FERR_BAD_FIELD_NUM);
goto Exit;
}
}
if( puiFieldType)
{
*puiFieldType = ITT_FLD_GET_TYPE( pItt);
}
if( ppFirstIfd)
{
*ppFirstIfd = (IFD *)pItt->pvItem;
}
if( puiFieldState)
{
*puiFieldState = ITT_FLD_GET_STATE( pItt);
}
Exit:
return( rc);
}
/***************************************************************************
Desc: Get the encryption information.
****************************************************************************/
RCODE fdictGetEncInfo(
FDB * pDb,
FLMUINT uiEncId, // [in] Encryption definition to look up
FLMUINT * puiEncType, // [out] Optional
FLMUINT * puiEncState // [out] Optional
)
{
RCODE rc = FERR_OK;
ITT * pItt;
FDICT * pDict = pDb->pDict;
FlmRecord * pRecord = NULL;
void * pvField = NULL;
FLMUINT uiEncState;
FLMUINT uiEncType;
if ( pDb->pFile->bInLimitedMode)
{
flmAssert( 0);
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
if( pDict && pDict->pIttTbl && uiEncId < pDict->uiIttCnt)
{
pItt = &pDict->pIttTbl[ uiEncId];
// Is it really an encryption definition?
if( ! ITT_IS_ENCDEF( pItt))
{
rc = RC_SET( FERR_BAD_ENCDEF_ID);
goto Exit;
}
uiEncType = ((F_CCS *)pItt->pvItem)->getEncType();
// Get the Encryption record and determine the state.
if (RC_BAD( rc = FlmRecordRetrieve( (HFDB)pDb,
FLM_DICT_CONTAINER,
uiEncId,
FO_EXACT,
&pRecord,
NULL)))
{
goto Exit;
}
pvField = pRecord->find( pRecord->root(),
FLM_STATE_TAG);
if (pvField)
{
const char * pDataPtr = (const char *)pRecord->getDataPtr( pvField);
if (f_strnicmp( pDataPtr, "chec", 4) == 0)
{
uiEncState = ITT_ENC_STATE_CHECKING;
}
else if (f_strnicmp( pDataPtr, "purg", 4) == 0)
{
uiEncState = ITT_ENC_STATE_PURGE;
}
else if (f_strnicmp( pDataPtr, "acti", 4) == 0)
{
uiEncState = ITT_ENC_STATE_ACTIVE;
}
else
{
uiEncState = ITT_ENC_STATE_UNUSED;
}
}
else
{
uiEncState = ITT_ENC_STATE_UNUSED;
}
}
else
{
rc = RC_SET( FERR_BAD_ENCDEF_ID);
goto Exit;
}
if( puiEncType)
{
*puiEncType = uiEncType;
}
if( puiEncState)
{
*puiEncState = uiEncState;
}
Exit:
if (pRecord)
{
pRecord->Release();
}
return( rc);
}
/***************************************************************************
Desc: Get the Container given a container number.
****************************************************************************/
RCODE fdictGetContainer(
FDICT * pDict,
FLMUINT uiContNum,
LFILE ** ppLFile)
{
ITT * pItt;
if( pDict && uiContNum < pDict->uiIttCnt && pDict->pIttTbl)
{
pItt = &pDict->pIttTbl[ uiContNum];
// Is it really a container?
if( !ITT_IS_CONTAINER( pItt))
{
return( RC_SET( FERR_BAD_CONTAINER));
}
if( ppLFile)
{
*ppLFile = (LFILE *) pItt->pvItem;
}
}
else
{
// Hard coded container - data is [0], dictionary is [1].
if( uiContNum == FLM_DATA_CONTAINER)
{
if( ppLFile)
{
*ppLFile = &pDict->pLFileTbl[ LFILE_DATA_CONTAINER_OFFSET];
}
}
else if( uiContNum == FLM_DICT_CONTAINER)
{
if( ppLFile)
{
*ppLFile = &pDict->pLFileTbl[ LFILE_DICT_CONTAINER_OFFSET];
}
}
else if( uiContNum == FLM_TRACKER_CONTAINER)
{
if( ppLFile)
{
*ppLFile = &pDict->pLFileTbl[ LFILE_TRACKER_CONTAINER_OFFSET];
}
}
else
{
return( RC_SET( FERR_BAD_CONTAINER));
}
}
return( FERR_OK);
}
/***************************************************************************
Desc: Get the IXD, LFILE and IFD information given an index number.
****************************************************************************/
RCODE fdictGetIndex(
FDICT * pDict,
FLMBOOL bInLimitedMode,
FLMUINT uiIxNum,
LFILE ** ppLFile, // [out] optional
IXD ** ppIxd, // [out] optional
FLMBOOL bOfflineOk)
{
RCODE rc = FERR_OK;
ITT * pItt;
LFILE * pLFile;
IXD * pIxd;
if( ppIxd)
{
*ppIxd = NULL;
}
if( ppLFile)
{
*ppLFile = NULL;
}
if( pDict && uiIxNum < pDict->uiIttCnt && pDict->pIttTbl)
{
pItt = &pDict->pIttTbl[ uiIxNum];
// Is it really a container?
if( !ITT_IS_INDEX( pItt))
{
rc = RC_SET( FERR_BAD_IX);
goto Exit;
}
pLFile = (LFILE *) pItt->pvItem;
pIxd = pLFile->pIxd;
if( ppLFile)
{
*ppLFile = pLFile;
}
if( ppIxd)
{
*ppIxd = pIxd;
}
// If the index is suspended the IXD_OFFLINE flag
// will be set, so it is sufficient to just test
// the IXD_OFFLINE for both suspended and offline
// conditions.
if( (pIxd->uiFlags & IXD_OFFLINE) && !bOfflineOk)
{
rc = RC_SET( FERR_INDEX_OFFLINE);
goto Exit;
}
// An encrypted index that cannot be decrypted is as good as
// offline.
if ( pIxd->uiEncId && bInLimitedMode && !bOfflineOk)
{
rc = RC_SET( FERR_INDEX_OFFLINE);
goto Exit;
}
}
else if (uiIxNum == FLM_DICT_INDEX)
{
pLFile = pDict->pLFileTbl + LFILE_DICT_INDEX_OFFSET;
if( ppLFile)
{
*ppLFile = pLFile;
}
if( ppIxd)
{
*ppIxd = pLFile->pIxd;
}
}
else
{
rc = RC_SET( FERR_BAD_IX);
goto Exit;
}
Exit:
return( rc);
}
/***************************************************************************
Desc: Given a IXD ID (index drn), returns the next Index Def (IXD).
****************************************************************************/
RCODE fdictGetNextIXD(
FDICT * pDict,
FLMUINT uiIndexNum,
IXD ** ppIxd)
{
RCODE rc = FERR_OK;
IXD * pIxd = NULL;
flmAssert( pDict && pDict->uiIxdCnt);
for( uiIndexNum++; uiIndexNum < pDict->uiIttCnt; uiIndexNum++)
{
ITT * pItt = &pDict->pIttTbl[ uiIndexNum];
if( ITT_IS_INDEX( pItt))
{
LFILE * pLFile = (LFILE *) pItt->pvItem;
pIxd = pLFile->pIxd;
break;
}
}
// Special case -- return the dictionary index
if( !pIxd && uiIndexNum < FLM_DICT_INDEX)
{
pIxd = pDict->pIxdTbl;
}
if( pIxd)
{
// Check to see if the index is offline. Still return *ppIxd.
if( pIxd->uiFlags & IXD_OFFLINE)
{
rc = RC_SET( FERR_INDEX_OFFLINE);
goto Exit;
}
}
else
{
rc = RC_SET( FERR_EOF_HIT);
goto Exit;
}
Exit:
if( ppIxd)
{
*ppIxd = pIxd;
}
return( rc);
}
/****************************************************************************
Desc: Rebuild the dictionary tables reading in all dictionary
records.
****************************************************************************/
RCODE fdictRebuild(
FDB * pDb)
{
RCODE rc = FERR_OK;
TDICT tDict;
FLMUINT uiCount;
IXD * pIxd;
FLMBOOL bTDictInitialized = FALSE;
FLMBOOL bSuspended;
FLMUINT uiOnlineTransId;
// Allocate a new FDICT structure for reading the local dictionary
// into memory.
// At this point, pDb better not be pointing to a dictionary.
flmAssert( pDb->pDict == NULL);
if( RC_BAD( rc = flmAllocDict( &pDb->pDict)))
{
goto Exit;
}
if( !pDb->pDict->pLFileTbl)
{
// Read the local dictionary into memory.
if( RC_BAD(rc = fdictReadLFiles( pDb, pDb->pDict)))
{
goto Exit;
}
// For a database create the LFiles still are not created.
if( pDb->pDict->pLFileTbl->uiLfNum == 0)
{
goto Exit;
}
}
bTDictInitialized = TRUE;
if( RC_BAD( rc = fdictInitTDict( pDb, &tDict)))
{
goto Exit;
}
if( RC_BAD( rc = fdictProcessAllDictRecs( pDb, &tDict)))
{
goto Exit;
}
if( RC_BAD( rc = fdictBuildTables( &tDict, FALSE, FALSE)))
{
goto Exit;
}
// Loop through the IXD and set the uiLastDrnIndexed value.
uiCount = pDb->pDict->uiIxdCnt;
for( pIxd = pDb->pDict->pIxdTbl; uiCount--; pIxd++)
{
// Ignore any errors in case we are rebuilding.
if( RC_BAD( flmGetIxTrackerInfo( pDb, pIxd->uiIndexNum,
&pIxd->uiLastContainerIndexed,
&pIxd->uiLastDrnIndexed, &uiOnlineTransId, &bSuspended)))
{
goto Exit;
}
if( bSuspended)
{
pIxd->uiFlags |= (IXD_SUSPENDED | IXD_OFFLINE);
}
else if( uiOnlineTransId == TRANS_ID_OFFLINE)
{
pIxd->uiFlags |= IXD_OFFLINE;
}
}
Exit:
if( bTDictInitialized)
{
tDict.pool.poolFree();
}
return( rc );
}
/****************************************************************************
Desc: Initializes and sets up a TDICT structure.
****************************************************************************/
RCODE fdictInitTDict(
FDB * pDb,
TDICT * pTDict)
{
RCODE rc = FERR_OK;
f_memset( pTDict, 0, sizeof( TDICT));
pTDict->pool.smartPoolInit( &g_TDictPoolStats);
pTDict->pDb = pDb;
pTDict->uiVersionNum = pDb->pFile->FileHdr.uiVersionNum;
pTDict->uiDefaultLanguage =
pDb->pFile->FileHdr.uiDefaultLanguage;
pTDict->pDict = pDb->pDict;
if( RC_BAD(rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER,
&pTDict->pLFile )))
goto Exit;
Exit:
return( rc);
}
/****************************************************************************
Desc: Build all of the dictionary tables given the temporary dictionary
(pTDict) that was built in ddprep.
Note: There are two ways this will be called. The first is when
we are building a dictionary from scratch. The second is ONLY
when a new field definition or container is added, or an index's
state is changed.
****************************************************************************/
RCODE fdictBuildTables(
TDICT * pTDict,
FLMBOOL bRereadLFiles,
FLMBOOL bNewDict)
{
RCODE rc = FERR_OK;
DDENTRY * pEntry;
TFIELD * pTField;
FLMUINT uiEntryNum;
TENCDEF * pTEncDef;
if( RC_BAD( rc = fdictReallocAllTables( pTDict)))
{
goto Exit;
}
// Go through and add each new item to the dictionary.
for( pEntry = pTDict->pFirstEntry
; pEntry
; pEntry = pEntry->pNextEntry )
{
uiEntryNum = pEntry->uiEntryNum;
switch( pEntry->uiType)
{
case 0: // Field
{
pTField = (TFIELD *) pEntry->vpDef;
fdictAddItem( pTDict, uiEntryNum, pTField->uiFldInfo);
break;
}
case ITT_INDEX_TYPE:
{
fdictAddItem( pTDict, uiEntryNum, ITT_INDEX_TYPE);
if( RC_BAD( rc = fdictAddIndex( pTDict, pEntry )))
{
goto Exit;
}
break;
}
case ITT_CONTAINER_TYPE:
{
fdictAddItem( pTDict, uiEntryNum, ITT_CONTAINER_TYPE);
// rc = fdictAddLFile( pTDict, pEntry ); Already done.
break;
}
case ITT_ENCDEF_TYPE:
{
if (!pTDict->pDb->pFile)
{
flmAssert( 0);
rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE);
goto Exit;
}
else
{
fdictAddItem( pTDict, uiEntryNum, ITT_ENCDEF_TYPE);
pTEncDef = (TENCDEF *) pEntry->vpDef;
// Need to add a new CCS.
if (RC_BAD( rc = fdictAddNewCCS(pTDict, pTEncDef, uiEntryNum)))
{
goto Exit;
}
}
break;
}
default:
{
break;
}
}
}
if( pTDict->uiNewIfds || bNewDict)
{
if( RC_BAD( rc = fdictFixupIfdPointers( pTDict->pDict,
bNewDict ? 0 : (pTDict->uiTotalIfds - pTDict->uiNewIfds))))
{
goto Exit;
}
}
if( bRereadLFiles)
{
if( RC_BAD( rc = fdictReadLFiles( pTDict->pDb, pTDict->pDict)))
{
goto Exit;
}
}
if( RC_BAD( rc = fdictFixupLFileTbl( pTDict->pDict)))
{
goto Exit;
}
Exit:
return( rc );
}
/****************************************************************************
Desc: Fixup pointers in tables of copied dictionary. This is called after
a copy of one dictionary to another, or after a dictionary's tables
have been reallocated.
****************************************************************************/
FSTATIC void fdictFixupPointers(
FDICT * pNewDict,
FDICT * pOldDict
)
{
FLMUINT uiPos;
FLMUINT uiOffset;
LFILE * pOldLFile;
LFILE * pNewLFile;
IFD * pOldIfd;
IFD * pNewIfd;
IXD * pOldIxd;
IXD * pNewIxd;
ITT * pOldItt;
ITT * pNewItt;
// Fixup anything that points to LFILE entries.
if (pNewDict->pLFileTbl && pNewDict->pLFileTbl != pOldDict->pLFileTbl)
{
// Fixup pItt->pvItem pointers for indexes and containers
for (uiPos = 0, pOldItt = pOldDict->pIttTbl,
pNewItt = pNewDict->pIttTbl;
uiPos < pOldDict->uiIttCnt;
uiPos++, pOldItt++, pNewItt++)
{
if (ITT_IS_CONTAINER( pOldItt) || ITT_IS_INDEX( pOldItt))
{
if (pOldItt->pvItem)
{
LFILE * pTmpLFile;
pTmpLFile = (LFILE *)(pOldItt->pvItem);
uiOffset = (FLMUINT)(pTmpLFile - pOldDict->pLFileTbl);
pTmpLFile = pNewDict->pLFileTbl + uiOffset;
pNewItt->pvItem = (void *)pTmpLFile;
}
else
{
flmAssert( pNewItt->pvItem == NULL);
}
}
else if (ITT_IS_ENCDEF( pOldItt))
{
if (pOldItt->pvItem)
{
pNewItt->pvItem = pOldItt->pvItem;
((F_CCS *)pNewItt->pvItem)->AddRef();
}
else
{
flmAssert( pNewItt->pvItem == NULL);
}
}
}
}
// Fixup anything that points to IXD entries
if (pNewDict->pIxdTbl && pNewDict->pIxdTbl != pOldDict->pIxdTbl)
{
// Fixup pLFile->pIxd pointers
for (uiPos = 0, pOldLFile = pOldDict->pLFileTbl,
pNewLFile = pNewDict->pLFileTbl;
uiPos < pOldDict->uiLFileCnt;
uiPos++, pOldLFile++, pNewLFile++)
{
if (pOldLFile->pIxd)
{
uiOffset = (FLMUINT)(pOldLFile->pIxd - pOldDict->pIxdTbl);
pNewLFile->pIxd = pNewDict->pIxdTbl + uiOffset;
}
else
{
flmAssert( pNewLFile->pIxd == NULL);
}
}
// Fixup pIfd->pIxd pointers
for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl,
pNewIfd = pNewDict->pIfdTbl;
uiPos < pOldDict->uiIfdCnt;
uiPos++, pOldIfd++, pNewIfd++)
{
if (pOldIfd->pIxd)
{
uiOffset = (FLMUINT)(pOldIfd->pIxd - pOldDict->pIxdTbl);
pNewIfd->pIxd = pNewDict->pIxdTbl + uiOffset;
}
else
{
flmAssert( pNewIfd->pIxd == NULL);
}
}
}
// Fixup anything that points to IFD entries
if (pNewDict->pIfdTbl && pNewDict->pIfdTbl != pOldDict->pIfdTbl)
{
// Fixup pIfd->pNextInChain pointers
for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl,
pNewIfd = pNewDict->pIfdTbl;
uiPos < pOldDict->uiIfdCnt;
uiPos++, pOldIfd++, pNewIfd++)
{
if (pOldIfd->pNextInChain)
{
uiOffset = (FLMUINT)(pOldIfd->pNextInChain - pOldDict->pIfdTbl);
pNewIfd->pNextInChain = pNewDict->pIfdTbl + uiOffset;
}
else
{
flmAssert( pNewIfd->pNextInChain == NULL);
}
}
// Fixup pIxd->pFirstIfd pointers
for (uiPos = 0, pOldIxd = pOldDict->pIxdTbl,
pNewIxd = pNewDict->pIxdTbl;
uiPos < pOldDict->uiIxdCnt;
uiPos++, pOldIxd++, pNewIxd++)
{
if (pOldIxd->pFirstIfd)
{
uiOffset = (FLMUINT)(pOldIxd->pFirstIfd - pOldDict->pIfdTbl);
pNewIxd->pFirstIfd = pNewDict->pIfdTbl + uiOffset;
}
else
{
flmAssert( pNewIxd->pFirstIfd == NULL);
}
}
// Fixup pItt->pvItem pointers
for (uiPos = 0, pOldItt = pOldDict->pIttTbl,
pNewItt = pNewDict->pIttTbl;
uiPos < pOldDict->uiIttCnt;
uiPos++, pOldItt++, pNewItt++)
{
if (ITT_IS_FIELD( pOldItt))
{
if (pOldItt->pvItem)
{
IFD * pTmpIfd;
pTmpIfd = (IFD *)(pOldItt->pvItem);
uiOffset = (FLMUINT)(pTmpIfd - pOldDict->pIfdTbl);
pTmpIfd = pNewDict->pIfdTbl + uiOffset;
pNewItt->pvItem = (void *)pTmpIfd;
}
else
{
flmAssert( pNewItt->pvItem == NULL);
}
}
}
}
// Fixup anything that points to field path entries
if (pNewDict->pFldPathsTbl && pNewDict->pFldPathsTbl != pOldDict->pFldPathsTbl)
{
// Fixup pIfd->pFieldPathCToP and pIfd->pFieldPathPToC pointers
for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl,
pNewIfd = pNewDict->pIfdTbl;
uiPos < pOldDict->uiIfdCnt;
uiPos++, pOldIfd++, pNewIfd++)
{
if (pOldIfd->pFieldPathCToP)
{
uiOffset = (FLMUINT)(pOldIfd->pFieldPathCToP - pOldDict->pFldPathsTbl);
pNewIfd->pFieldPathCToP = pNewDict->pFldPathsTbl + uiOffset;
}
else
{
flmAssert( pNewIfd->pFieldPathCToP == NULL);
}
if (pOldIfd->pFieldPathPToC)
{
uiOffset = (FLMUINT)(pOldIfd->pFieldPathPToC - pOldDict->pFldPathsTbl);
pNewIfd->pFieldPathPToC = pNewDict->pFldPathsTbl + uiOffset;
}
else
{
flmAssert( pNewIfd->pFieldPathPToC == NULL);
}
}
}
}
/****************************************************************************
Desc: Allocate all of the dictionary tables based on the counts that
were incremented in pTDict. Coded to add new fields, indexes or
container, but not to modify or delete anything!
****************************************************************************/
FSTATIC RCODE fdictReallocAllTables(
TDICT * pTDict)
{
RCODE rc = FERR_OK;
FDICT OldDict;
FDICT * pDict = pTDict->pDict;
// Save a copy of the old dictionary's pointers and counters
// Easiest way to do this is to simply copy the structure.
f_memcpy( &OldDict, pDict, sizeof( FDICT));
if( pTDict->pLastEntry
&& pTDict->pLastEntry->uiEntryNum >= pDict->uiIttCnt
&& pTDict->pLastEntry->uiEntryNum < FLM_RESERVED_TAG_NUMS)
{
ITT * pItt;
FLMUINT uiNewCount;
uiNewCount = pTDict->pLastEntry->uiEntryNum + 1 - pDict->uiIttCnt;
if (uiNewCount)
{
// Must fake out so that we don't lose the old table.
pDict->pIttTbl = NULL;
if( RC_BAD( rc = fdictReallocTbl( sizeof( ITT), pDict->uiIttCnt,
uiNewCount, (void **) &pDict->pIttTbl)))
{
goto Exit;
}
pTDict->uiTotalItts = pDict->uiIttCnt + uiNewCount;
// Copy the table to the new location (because of fake out above)
if( OldDict.uiIttCnt)
{
f_memcpy( pDict->pIttTbl, OldDict.pIttTbl,
sizeof( ITT) * OldDict.uiIttCnt);
}
// Initialize the new items to empty.
pItt = pDict->pIttTbl + pDict->uiIttCnt;
for( ;uiNewCount--; pItt++)
{
pItt->uiType = ITT_EMPTY_SLOT;
pItt->pvItem = NULL;
}
}
}
if (pTDict->uiNewIxds)
{
// Must fake out so that we don't lose the old table.
pDict->pIxdTbl = NULL;
if( RC_BAD( rc = fdictReallocTbl( sizeof( IXD), pDict->uiIxdCnt,
pTDict->uiNewIxds, (void **)&pDict->pIxdTbl)))
{
goto Exit;
}
pTDict->uiTotalIxds = pDict->uiIxdCnt + pTDict->uiNewIxds;
// Copy the table to the new location (because of fake out above)
if( OldDict.uiIxdCnt)
{
f_memcpy( pDict->pIxdTbl, OldDict.pIxdTbl,
sizeof( IXD) * OldDict.uiIxdCnt);
}
}
if (pTDict->uiNewIfds)
{
// Must fake out so that we don't lose the old table.
pDict->pIfdTbl = NULL;
if( RC_BAD( rc = fdictReallocTbl( sizeof( IFD), pDict->uiIfdCnt,
pTDict->uiNewIfds, (void **)&pDict->pIfdTbl)))
{
goto Exit;
}
pTDict->uiTotalIfds = pDict->uiIfdCnt + pTDict->uiNewIfds;
// Copy the table to the new location (because of fake out above)
if( OldDict.uiIfdCnt)
{
f_memcpy( pDict->pIfdTbl, OldDict.pIfdTbl,
sizeof( IFD) * OldDict.uiIfdCnt);
}
}
if (pTDict->uiNewFldPaths)
{
// Must fake out so that we don't lose the old table.
pDict->pFldPathsTbl = NULL;
if( RC_BAD( rc = fdictReallocTbl( sizeof( FLMUINT), pDict->uiFldPathsCnt,
pTDict->uiNewFldPaths, (void **)&pDict->pFldPathsTbl)))
{
goto Exit;
}
pTDict->uiTotalFldPaths = pDict->uiFldPathsCnt + pTDict->uiNewFldPaths;
// Copy the table to the new location (because of fake out above)
if( OldDict.uiFldPathsCnt)
{
f_memcpy( pDict->pFldPathsTbl, OldDict.pFldPathsTbl,
sizeof( FLMUINT) * OldDict.uiFldPathsCnt);
}
}
fdictFixupPointers( pDict, &OldDict);
Exit:
// Free any old tables where a new table was allocated.
if (OldDict.pLFileTbl != pDict->pLFileTbl)
{
f_free( &OldDict.pLFileTbl);
}
if (OldDict.pIttTbl != pDict->pIttTbl)
{
f_free( &OldDict.pIttTbl);
}
if (OldDict.pIxdTbl != pDict->pIxdTbl)
{
f_free( &OldDict.pIxdTbl);
}
if (OldDict.pIfdTbl != pDict->pIfdTbl)
{
f_free( &OldDict.pIfdTbl);
}
if (OldDict.pFldPathsTbl != pDict->pFldPathsTbl)
{
f_free( &OldDict.pFldPathsTbl);
}
return( rc );
}
/****************************************************************************
Desc: Allocate or reallocate a table.
****************************************************************************/
FSTATIC RCODE fdictReallocTbl(
FLMUINT uiElementSize,
FLMUINT uiTblSize,
FLMUINT uiAddElements,
void ** ppvTblRV)
{
RCODE rc = FERR_OK;
// Does the table need to grow?
if( uiAddElements)
{
if( *ppvTblRV)
{
if( RC_BAD( rc = f_recalloc(
uiElementSize * (uiTblSize + uiAddElements),
ppvTblRV)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_calloc(
uiElementSize * (uiTblSize + uiAddElements),
ppvTblRV)))
{
goto Exit;
}
}
}
Exit:
return( rc );
}
/****************************************************************************
Desc: Add a new item to the item type table.
****************************************************************************/
FSTATIC void fdictAddItem(
TDICT * pTDict,
FLMUINT uiFieldNum,
FLMUINT uiFieldType)
{
FDICT * pDict = pTDict->pDict;
ITT * pItt;
if( uiFieldNum < FLM_RESERVED_TAG_NUMS)
{
pItt = pDict->pIttTbl + uiFieldNum;
pItt->uiType = uiFieldType;
pItt->pvItem = NULL;
if( uiFieldNum >= pDict->uiIttCnt)
{
pDict->uiIttCnt = uiFieldNum + 1;
}
}
}
/****************************************************************************
Desc: Add the new IXD, IFD, field paths and LFILE for the index.
****************************************************************************/
FSTATIC RCODE fdictAddIndex(
TDICT * pTDict,
DDENTRY * pEntry)
{
RCODE rc = FERR_OK;
FDICT * pDict = pTDict->pDict;
FLMUINT uiIndexNum = pEntry->uiEntryNum;
IXD * pIxd;
IFD * pIfd;
FLMUINT * pFirstPToCFld;
FLMUINT * pFirstCToPFld;
FLMUINT * pCurFld;
FLMUINT * pTempFld;
TIXD * pTIxd;
TIFD * pTIfd;
TIFP * pTIfp;
// The index numbers in the IXD array do not need to be in any order.
// Just add all of the index information to the end of the table.
pIxd = pDict->pIxdTbl + pDict->uiIxdCnt++;
pIxd->uiIndexNum = uiIndexNum;
pTIxd = (TIXD *) pEntry->vpDef;
pIxd->uiContainerNum = pTIxd->uiContainerNum;
pIxd->uiNumFlds = pTIxd->uiNumFlds;
pIxd->uiFlags = pTIxd->uiFlags;
pIxd->uiLanguage = pTIxd->uiLanguage;
pIxd->uiLastContainerIndexed = 0xFFFFFFFF;
pIxd->uiLastDrnIndexed = DRN_LAST_MARKER;
pIxd->uiEncId = pTIxd->uiEncId;
// Setup the IFD elements and the field paths.
pIxd->pFirstIfd = pIfd = pDict->pIfdTbl + pDict->uiIfdCnt;
pDict->uiIfdCnt += pIxd->uiNumFlds;
for( pTIfd = pTIxd->pNextTIfd; pTIfd; pIfd++, pTIfd = pTIfd->pNextTIfd)
{
// This is a good place to set the IFD_LAST flag.
// Could/Should be done in ddprep.c
if( pTIfd->pNextTIfd == NULL)
pTIfd->uiFlags |= IFD_LAST;
pIfd->uiIndexNum = uiIndexNum;
pIfd->pIxd = pIxd;
pIfd->uiFlags = pTIfd->uiFlags;
pIfd->uiLimit = pTIfd->uiLimit;
pIfd->uiCompoundPos = pTIfd->uiCompoundPos;
// The pTIfp->pNextTIfp are linked from parent to child.
pTIfp = pTIfd->pTIfp;
pCurFld = pDict->pFldPathsTbl + pDict->uiFldPathsCnt;
pFirstPToCFld = pFirstCToPFld = pCurFld;
pIfd->pFieldPathPToC = pFirstPToCFld;
do
{
*pCurFld++ = pTIfp->uiFldNum;
pTIfp = pTIfp->pNextTIfp;
} while( pTIfp);
pIfd->uiFldNum = *(pCurFld-1);
pTempFld = pCurFld - 1;
// Null Terminate
*pCurFld++ = 0;
pTIfp = pTIfd->pTIfp;
if( pTIfp->pNextTIfp) // If more than one field make the CToP path.
{
pFirstCToPFld = pCurFld;
while( pTempFld != pFirstPToCFld)
{
*pCurFld++ = *pTempFld--;
}
*pCurFld++ = *pTempFld;
*pCurFld++ = 0;
}
pIfd->pFieldPathCToP = pFirstCToPFld;
pDict->uiFldPathsCnt += pCurFld - pFirstPToCFld;
}
return( rc );
}
/****************************************************************************
Desc: Fixup the IFD chain and the pIfd->pIxd pointers.
****************************************************************************/
FSTATIC RCODE fdictFixupIfdPointers(
FDICT * pDict,
FLMUINT uiIfdStartOffset)
{
RCODE rc = FERR_OK;
FLMUINT uiCount;
IFD * pIfd;
ITT * pItt;
ITT * pIttTbl = pDict->pIttTbl;
// Go through the IFD list and setup the pNextInChain pointers
// making sure that the required fields are first.
for( uiCount = pDict->uiIfdCnt - uiIfdStartOffset,
pIfd = pDict->pIfdTbl + uiIfdStartOffset;
uiCount; uiCount--, pIfd++)
{
IFD * pPrevInChain;
IFD * pTempIfd;
if( pIfd->uiFldNum >= pDict->uiIttCnt)
{
if( pIfd->uiFldNum < FLM_RESERVED_TAG_NUMS)
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
continue;
}
else
{
pItt = pIttTbl + pIfd->uiFldNum;
if( !ITT_IS_FIELD( pItt))
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
}
// Move the field type to the pIfd->uiFlags
IFD_SET_FIELD_TYPE( pIfd, ITT_FLD_GET_TYPE( pItt));
// Need to include 'any', 'use', 'parent' tags as valid tags.
if( !pItt->pvItem)
{
pItt->pvItem = (void *) pIfd;
}
else
{
// Follow the chain and index at the front or rear depending on
// if the field is required within the set.
pTempIfd = (IFD *) pItt->pvItem;
if( (pIfd->uiFlags & IFD_REQUIRED_IN_SET)
|| !(pTempIfd->uiFlags & IFD_REQUIRED_IN_SET))
{
pIfd->pNextInChain = pTempIfd;
pItt->pvItem = (void *) pIfd;
}
else
{
// Not required in set and first IFD is required in set.
// Look for first not required IFD in the chain.
pPrevInChain = pTempIfd;
pTempIfd = pTempIfd->pNextInChain;
for( ; pTempIfd; pTempIfd = pTempIfd->pNextInChain)
{
if( !(pTempIfd->uiFlags & IFD_REQUIRED_IN_SET))
break;
pPrevInChain = pTempIfd;
}
pIfd->pNextInChain = pPrevInChain->pNextInChain;
pPrevInChain->pNextInChain = pIfd;
}
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Fixup the ITT pointers into the LFILE elements and all of
the IXD pointers in the LDICT.
****************************************************************************/
RCODE fdictFixupLFileTbl(
FDICT * pDict)
{
RCODE rc = FERR_OK;
FLMUINT uiCount;
LFILE * pLFile;
IXD * pIxd;
ITT * pItt;
ITT * pIttTbl = pDict->pIttTbl;
FLMUINT uiIttCnt = pDict->uiIttCnt;
for( uiCount = pDict->uiLFileCnt, pLFile = pDict->pLFileTbl
; uiCount; uiCount--, pLFile++)
{
if( pLFile->uiLfNum != FLM_DATA_CONTAINER
&& pLFile->uiLfNum != FLM_DICT_CONTAINER
&& pLFile->uiLfNum != FLM_DICT_INDEX
&& pLFile->uiLfNum != FLM_TRACKER_CONTAINER)
{
pItt = pIttTbl + pLFile->uiLfNum;
if( uiIttCnt <= pLFile->uiLfNum ||
(pLFile->uiLfType == LF_CONTAINER && !ITT_IS_CONTAINER( pItt)))
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
if( pLFile->uiLfType == LF_INDEX && !ITT_IS_INDEX( pItt))
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
pItt->pvItem = pLFile;
}
else if( pLFile->uiLfNum == FLM_DICT_INDEX)
{
// The first IXD should be the dictionary index.
if( pDict->pIxdTbl && pDict->pIxdTbl->uiIndexNum == FLM_DICT_INDEX)
{
pLFile->pIxd = pDict->pIxdTbl;
}
}
}
// Now that all of the indexes/containers in the ITT table point
// to the LFILE entries, fixup the LFILE to point to the IXD entries.
for( uiCount = pDict->uiIxdCnt, pIxd = pDict->pIxdTbl;
uiCount; uiCount--, pIxd++)
{
if( uiIttCnt <= pIxd->uiIndexNum)
{
if( pIxd->uiIndexNum != FLM_DICT_INDEX)
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
}
else
{
pItt = pIttTbl + pIxd->uiIndexNum;
pLFile = (LFILE *) pItt->pvItem;
if( !pLFile)
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
pLFile->pIxd = pIxd;
}
// Verify that the pIxd->uiContainerNum is actually a container.
// A value of 0 means that the index is on ALL containers.
if (pIxd->uiContainerNum)
{
if( uiIttCnt <= pIxd->uiContainerNum)
{
if( pIxd->uiContainerNum != FLM_DATA_CONTAINER
&& pIxd->uiContainerNum != FLM_DICT_CONTAINER
&& pIxd->uiContainerNum != FLM_TRACKER_CONTAINER)
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
}
else
{
pItt = pIttTbl + pIxd->uiContainerNum;
if( !ITT_IS_CONTAINER( pItt))
{
rc = RC_SET( FERR_BAD_REFERENCE);
goto Exit;
}
}
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Add a new CCS reference to the item type table. If a key is included
we can use it, otherwise we will have to generate one.
****************************************************************************/
FSTATIC RCODE fdictAddNewCCS(
TDICT * pTDict,
TENCDEF * pTEncDef,
FLMUINT uiRecNum)
{
RCODE rc = FERR_OK;
FDICT * pDict = pTDict->pDict;
ITT * pItt;
F_CCS * pCcs = NULL;
FDB * pDb = pTDict->pDb;
F_CCS * pDbWrappingKey;
if( uiRecNum >= FLM_RESERVED_TAG_NUMS)
{
goto Exit;
}
if (!pDb->pFile->bInLimitedMode)
{
pDbWrappingKey = pDb->pFile->pDbWrappingKey;
flmAssert( pTEncDef);
if ((pCcs = f_new F_CCS) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
// Setup the F_CCS.
if (RC_BAD( rc = pCcs->init( FALSE, pTEncDef->uiAlgType )))
{
goto Exit;
}
if (!pTEncDef->uiLength)
{
flmAssert( 0);
rc = RC_SET( FERR_MISSING_ENC_KEY);
goto Exit;
}
// We need to set the key information. This also unwraps the key and stores the
// handle.
if( RC_BAD( rc = pCcs->setKeyFromStore( pTEncDef->pucKeyInfo,
(FLMUINT32)pTEncDef->uiLength, NULL, pDbWrappingKey)))
{
goto Exit;
}
}
// Save the CCS object in the ITT table.
pItt = pDict->pIttTbl + uiRecNum;
pItt->pvItem = (void *)pCcs;
pCcs = NULL;
if( uiRecNum >= pDict->uiIttCnt)
{
pDict->uiIttCnt = uiRecNum + 1;
}
Exit:
if (pCcs)
{
delete pCcs;
}
return( rc);
}
/****************************************************************************
Desc: Copies an existing dictionary to a new dictionary. This does not
fix up all of the ITT's pvItem pointers (including the
pFirstIfd pointer of fields in the ITT table). To clone the
dictionary, call fdictCloneDict.
****************************************************************************/
RCODE fdictCopySkeletonDict(
FDB * pDb)
{
RCODE rc = FERR_OK;
FDICT * pNewDict = NULL;
FDICT * pOldDict = pDb->pDict;
FLMUINT uiTblSize;
FLMUINT uiPos;
LFILE * pLFile;
IXD * pIxd;
ITT * pItt;
ITT * pNewIttTbl = NULL;
FLMUINT uiNewIttTblLen = 0;
LFILE * pNewDictIndexLFile = NULL;
FLMUINT * pOldFieldPathsTbl = NULL;
FLMUINT * pNewFieldPathsTbl = NULL;
if( RC_BAD( rc = f_calloc( (FLMUINT)sizeof( FDICT), &pNewDict)))
{
goto Exit;
}
pNewDict->pNext = pNewDict->pPrev = NULL;
pNewDict->pFile = NULL;
pNewDict->uiUseCount = 1;
// Nothing to do is not a legal state.
if( !pOldDict)
{
flmAssert( pOldDict != NULL);
pDb->pDict = pNewDict;
goto Exit;
}
// ITT Table
if( (uiTblSize = pNewDict->uiIttCnt = pOldDict->uiIttCnt) != 0)
{
if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( ITT), &pNewDict->pIttTbl)))
{
goto Exit;
}
pNewIttTbl = pNewDict->pIttTbl;
uiNewIttTblLen = uiTblSize;
f_memcpy( pNewDict->pIttTbl, pOldDict->pIttTbl,
uiTblSize * sizeof( ITT));
// Clear out all of the pointer values.
pItt = pNewDict->pIttTbl;
for( uiPos = 0; uiPos < uiTblSize; uiPos++, pItt++)
{
if ( pItt->uiType == ITT_ENCDEF_TYPE && !pDb->pFile->bInLimitedMode)
{
flmAssert( pItt->pvItem);
((F_CCS *)pItt->pvItem)->AddRef();
}
else
{
pItt->pvItem = NULL;
}
}
}
// LFILE Table
if( (uiTblSize = pNewDict->uiLFileCnt = pOldDict->uiLFileCnt) != 0)
{
if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( LFILE),
&pNewDict->pLFileTbl)))
{
goto Exit;
}
f_memcpy( pNewDict->pLFileTbl, pOldDict->pLFileTbl,
uiTblSize * sizeof( LFILE));
for( pLFile = pNewDict->pLFileTbl; uiTblSize--; pLFile++)
{
if( pLFile->uiLfNum < FLM_RESERVED_TAG_NUMS)
{
// WARNING: The code must make a new LFILE
// before the dictionary is aware of it.
if( pLFile->uiLfNum < uiNewIttTblLen)
{
pItt = pNewIttTbl + pLFile->uiLfNum;
pItt->pvItem = (void *) pLFile;
}
}
else if( pLFile->uiLfNum == FLM_DICT_INDEX)
{
pNewDictIndexLFile = pLFile;
}
}
}
// IXD Table
if( (uiTblSize = pNewDict->uiIxdCnt = pOldDict->uiIxdCnt) != 0)
{
if( RC_BAD( rc = f_alloc(
uiTblSize * sizeof( IXD), &pNewDict->pIxdTbl)))
{
goto Exit;
}
f_memcpy( pNewDict->pIxdTbl, pOldDict->pIxdTbl,
uiTblSize * sizeof( IXD));
// Fixup all of the pointers to the IXD.
for( pIxd = pNewDict->pIxdTbl; uiTblSize--; pIxd++)
{
if( pIxd->uiIndexNum != FLM_DICT_INDEX)
{
pItt = pNewIttTbl + pIxd->uiIndexNum;
pLFile = (LFILE *) pItt->pvItem;
pLFile->pIxd = pIxd;
}
else if( pNewDictIndexLFile)
{
pNewDictIndexLFile->pIxd = pIxd;
}
}
}
// Field Paths Table
if( (uiTblSize = pNewDict->uiFldPathsCnt = pOldDict->uiFldPathsCnt) != 0)
{
if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( FLMUINT),
&pNewDict->pFldPathsTbl)))
{
goto Exit;
}
f_memcpy( pNewDict->pFldPathsTbl, pOldDict->pFldPathsTbl,
uiTblSize * sizeof( FLMUINT));
pOldFieldPathsTbl = pOldDict->pFldPathsTbl;
pNewFieldPathsTbl = pNewDict->pFldPathsTbl;
}
// IFD Table
if( (uiTblSize = pNewDict->uiIfdCnt = pOldDict->uiIfdCnt) != 0)
{
IFD * pIfd;
FLMUINT uiLastIndexNum;
FLMUINT uiOffset;
if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( IFD),
&pNewDict->pIfdTbl)))
{
goto Exit;
}
f_memcpy( pNewDict->pIfdTbl, pOldDict->pIfdTbl,
uiTblSize * sizeof( IFD));
// Fixup all pFirstIfd pointers, backlinks to the pIxd and fldPathTbls.
// Set all of the IfdChain values to NULL to be fixed up later.
pIfd = pNewDict->pIfdTbl;
uiLastIndexNum = 0;
for( uiPos = 0; uiPos < uiTblSize; uiPos++, pIfd++)
{
pIfd->pNextInChain = NULL;
if( pIfd->uiIndexNum != FLM_DICT_INDEX)
{
pItt = pNewIttTbl + pIfd->uiIndexNum;
pLFile = (LFILE *) pItt->pvItem;
pIxd = pLFile->pIxd;
}
else
{
pIxd = pNewDictIndexLFile->pIxd;
}
pIfd->pIxd = pIxd;
if( uiLastIndexNum != pIfd->uiIndexNum)
{
pIxd->pFirstIfd = pIfd;
uiLastIndexNum = pIfd->uiIndexNum;
}
// Fixup the field paths.
flmAssert( pNewFieldPathsTbl != NULL);
uiOffset = pIfd->pFieldPathCToP - pOldFieldPathsTbl;
pIfd->pFieldPathCToP = pNewFieldPathsTbl + uiOffset;
uiOffset = pIfd->pFieldPathPToC - pOldFieldPathsTbl;
pIfd->pFieldPathPToC = pNewFieldPathsTbl + uiOffset;
}
}
f_mutexLock( gv_FlmSysData.hShareMutex);
flmUnlinkFdbFromDict( pDb);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
pDb->pDict = pNewDict;
pNewDict = NULL;
Exit:
if( RC_BAD( rc) && pNewDict)
{
// Undo all of the allocations on the new table.
if( pNewDict->pLFileTbl)
{
f_free( &pNewDict->pLFileTbl);
}
if( pNewDict->pIttTbl)
{
f_free( &pNewDict->pIttTbl);
}
if( pNewDict->pIxdTbl)
{
f_free( &pNewDict->pIxdTbl);
}
if( pNewDict->pIfdTbl)
{
f_free( &pNewDict->pIfdTbl);
}
if( pNewDict->pFldPathsTbl)
{
f_free( &pNewDict->pFldPathsTbl);
}
f_free( &pNewDict);
}
return( rc);
}
/****************************************************************************
Desc: Creates a new version of the current dictionary and fixes up all
pointers
****************************************************************************/
RCODE fdictCloneDict(
FDB * pDb)
{
RCODE rc = FERR_OK;
TDICT tDict;
FLMBOOL bTDictInitialized = FALSE;
if( RC_BAD( rc = fdictCopySkeletonDict( pDb)))
{
goto Exit;
}
bTDictInitialized = TRUE;
if( RC_BAD( rc = fdictInitTDict( pDb, &tDict)))
{
goto Exit;
}
if( RC_BAD( rc = fdictBuildTables( &tDict, FALSE, TRUE)))
{
goto Exit;
}
pDb->uiFlags |= FDB_UPDATED_DICTIONARY;
Exit:
if( bTDictInitialized)
{
tDict.pool.poolFree();
}
// If we allocated an FDICT and there was an error, free the FDICT.
if( (RC_BAD( rc)) && (pDb->pDict))
{
flmFreeDict( pDb->pDict);
pDb->pDict = NULL;
}
return( rc);
}