//-------------------------------------------------------------------------
// Desc:	Dictionary access routiones.
// Tabs:	3
//
//		Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved.
//
//		This program is free software; you can redistribute it and/or
//		modify it under the terms of version 2 of the GNU General Public
//		License as published by the Free Software Foundation.
//
//		This program is distributed in the hope that it will be useful,
//		but WITHOUT ANY WARRANTY; without even the implied warranty of
//		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//		GNU General Public License for more details.
//
//		You should have received a copy of the GNU General Public License
//		along with this program; if not, contact Novell, Inc.
//
//		To contact Novell about this file by physical or electronic mail,
//		you may find current contact information at www.novell.com
//
// $Id: fdict.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------

#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)))
	{
		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);
}
