//-------------------------------------------------------------------------
// Desc:	Delete element from b-tree block.
// Tabs:	3
//
//		Copyright (c) 1991-2001,2003-2006 Novell, Inc. All Rights Reserved.
//
//		This program is free software; you can redistribute it and/or
//		modify it under the terms of version 2 of the GNU General Public
//		License as published by the Free Software Foundation.
//
//		This program is distributed in the hope that it will be useful,
//		but WITHOUT ANY WARRANTY; without even the implied warranty of
//		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//		GNU General Public License for more details.
//
//		You should have received a copy of the GNU General Public License
//		along with this program; if not, contact Novell, Inc.
//
//		To contact Novell about this file by physical or electronic mail,
//		you may find current contact information at www.novell.com
//
// $Id: fsdelelm.cpp 12284 2006-01-19 14:54:14 -0700 (Thu, 19 Jan 2006) dsanders $
//-------------------------------------------------------------------------

#include "flaimsys.h"

/***************************************************************************
Desc:		Delete the current element from a b-tree block and write block.
			The stack must point to the next element after the deleted element.
			The next element could be in a different block so be careful.
Notes:	The order of the high level if's is VERY important.
			The order of changing the blocks is not important because of logging.
*****************************************************************************/
RCODE FSBtDelete(
	FDB *			pDb,
	LFILE *		pLFile,
	BTSK **	 	pStackRV)
{
	RCODE			rc;
	BTSK *		pStack = *pStackRV;
	SCACHE *		pTmpSCache;
	FLMBYTE *	pBlk;
	FLMBOOL		bReleaseTmpCache = FALSE;
	FLMUINT		uiNextBlk;
	FLMUINT		uiPrevBlk;
	FLMUINT		uiLastElm;			// Offset of last element in a block
	FLMUINT		uiElmOvhd = pStack->uiElmOvhd;
	FLMBYTE		pLEMBuffer[12];	// Last element marker buffer

	// Log block before modifying it

	if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
	{
		goto Exit;
	}

	// If this is a non-leaf positioning index, update parent counts.

	if (pLFile->pIxd && (pLFile->pIxd->uiFlags & IXD_POSITIONING))
	{
		if (pStack->uiLevel)
		{
			FLMUINT		uiRefCount;
			FLMBYTE*		pElm = pStack->pBlk + pStack->uiCurElm;

			// Reduce the counts from the parent up.

			uiRefCount = FB2UD( &pElm[BNE_CHILD_COUNT]);
			
			if (RC_BAD( rc = FSChangeBlkCounts( pDb, pStack, 
				(FLMINT) (0 - uiRefCount))))
			{
				goto Exit;
			}
		}
	}

	if (RC_BAD( rc = FSBlkDelElm( pStack)))
	{
		goto Exit;
	}

	// Take care of deletion of the ONLY element in the block.  There is NO
	// WAY this could be a root or right end block because of the last
	// element marker (LEM) takes up 2 or 4 bytes

	if (pStack->uiBlkEnd == BH_OVHD)
	{

		// Free up this empty block and fixup the NEXT/PREV links.

		uiNextBlk = FB2UD( &pStack->pBlk[BH_NEXT_BLK]);
		
		rc = FSBlockFixLinks( pDb, pLFile, pStack->pSCache);
		
		pStack->pSCache = NULL;
		pStack->pBlk = NULL;
		
		if (RC_BAD( rc))
		{
			goto Exit;
		}

		// Remove the element that pointed to this block

		if (RC_OK( rc = FSDelParentElm( pDb, pLFile, &pStack)))
		{
			pStack->uiBlkAddr = uiNextBlk;

			// Read the next block and set stack to point to next element

			if (RC_OK( rc = FSGetBlock( pDb, pLFile, uiNextBlk, pStack)))
			{
				pStack->uiCurElm = BH_OVHD;
				FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM);
			}
		}

		*pStackRV = pStack;
	}

	// Deleting a RIGHT MOST B-tree block (along the right side). Check if
	// only the LEM (last element marker) is left in the block. A block
	// CANNOT contain just a LEM. Move the LEM and free the block. There are
	// 3 cases to watch for:
	//
	//			EMPTY B-TREE - free the root/block save next record in lfArea
	//			EMPTY ROOT - free root and goto child and make that root
	//			EMPTY BLOCK - cannot contain only a LEM - move to previous block
	//
	// Must not move last DRN Marker - there is no need to do this.

	else if ((FB2UD( &pStack->pBlk[BH_NEXT_BLK]) == BT_END) &&
				((pStack->uiBlkEnd == BH_OVHD + uiElmOvhd)))
	{

		// EMPTY B-TREE Level is not in the current LFILE version so check
		// if the root block is the same as this block.

		if ((pStack->uiBlkType == BHT_LEAF) &&
			 (pLFile->uiRootBlk == pStack->uiBlkAddr))	// Root is leaf
		{

			// Return the empty root block to the system
			// Code supports empty b-trees. We have gone back and forth on
			// returning empty root blocks to the system. Setup stack for
			// emtpy state and modify the LFILE on disk. The data record
			// b-tree can NEVER be empty because of the next record DRN
			// record should always hang around. ALL CALLING ROUTINES MUST
			// CHECK for STACK->uiBlkAddr == BT_END.

			pStack->uiBlkAddr = BT_END;
			{

				// Get the next DRN and save in the LFD

				FLMBYTE *		ptr = &pStack->pBlk[BH_OVHD];
				
				ptr += BBE_GETR_KL( ptr) + BBE_KEY;
				pLFile->uiNextDrn = (FLMUINT) FB2UD( ptr);
			}

			rc = FSBlockFree( pDb, pStack->pSCache);
			
			pStack->pSCache = NULL;
			pStack->pBlk = NULL;
			
			if (RC_BAD( rc))
			{
				goto Exit;
			}

			pLFile->uiRootBlk = BT_END;
			rc = flmLFileWrite( pDb, pLFile);
		}

		// EMPTY ROOT BLOCK Remove root block and set new root block to
		// child.

		else if (pLFile->uiRootBlk == pStack->uiBlkAddr)
		{

			// Obtain child block and set to lfArea to assign new root block

			uiNextBlk = FSChildBlkAddr( pStack);

			rc = FSBlockFree( pDb, pStack->pSCache);
			
			pStack->pSCache = NULL;
			pStack->pBlk = NULL;

			if (RC_BAD( rc))
			{
				goto Exit;
			}

			pLFile->uiRootBlk = uiNextBlk;

			if (RC_BAD( rc = flmLFileWrite( pDb, pLFile)))
			{
				goto Exit;
			}

			// Assign the new root block

			if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiNextBlk, NULL,
						  &pTmpSCache)))
			{
				goto Exit;
			}

			bReleaseTmpCache = TRUE;

			if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pTmpSCache)))
			{
				goto Exit;
			}

			// Set the root block flag on this block & mark dirty

			BH_SET_ROOT_BLK( pTmpSCache->pucBlk);
			ScaReleaseCache( pTmpSCache, FALSE);
			bReleaseTmpCache = FALSE;

			// It is possible to add an element so that a new root block
			// gets created AND THEN Deleted.  Because of this
			// the ROOT block in the stack MUST always be pStack[0]. The
			// three lines below do this.

			shiftN( (FLMBYTE*) pStack + sizeof(BTSK),
					 sizeof(BTSK) * pStack->uiLevel,			// think uiLevel + 1 - 1
					 0 - (sizeof(BTSK)));

			// We don't want a pointer to the same shared cache in two
			// different elements of the pStack. NULL out the one that was
			// moved down.

			(pStack + pStack->uiLevel + 1)->pSCache = NULL;
			(pStack + pStack->uiLevel + 1)->pBlk = NULL;
			pStack--;
			*pStackRV = pStack;

			// Don't worry about positioning to the next element - root gone
		}
		else
		{
			// ONLY LAST ELEMENT MARKER (LEM) REMAINS (leaf or (non-leaf but
			// NOT ROOT)). A block must NEVER contain only the last element
			// marker (LEM). This may free up a leaf block or a non-leaf
			// block but never a root block (root checks above.) This is the
			// most complex case. Move the last element marker (LEM) to the
			// end of the previous block. Be carefull to delete the correct
			// element in the parent block. The algorithm states that there
			// MUST ALWAYS be room to insert the LEM into a block without
			// splitting that block. Because of logging the block
			// modifications do not need to be flushed in a specific order to
			// prevent corruption.

			uiPrevBlk = FB2UD( &pStack->pBlk[BH_PREV_BLK]);

			// Save the last element marker, may be a non-leaf element

			f_memcpy( pLEMBuffer, &pStack->pBlk[BH_OVHD], uiElmOvhd);

			// Free the block

			rc = FSBlockFree( pDb, pStack->pSCache);
			
			pStack->pSCache = NULL;
			pStack->pBlk = NULL;
			
			if (RC_BAD( rc))
			{
				goto Exit;
			}

			// Pop stack - point to parent

			pStack--;

			// Modify the parent blocks last element marker (LEM) to point to
			// the new last block. THEN delete the previous element that also
			// points to the new last block.  Read the parent block.
			
			if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
			{
				goto Exit;
			}

			if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
			{
				goto Exit;
			}

			// Change where element points to in the last element marker (LEM)

			FSSetChildBlkAddr( BLK_ELM_ADDR( pStack, pStack->uiCurElm), uiPrevBlk,
									pStack->uiElmOvhd);

			// FSBtPrevElm may return -1 which is NOT OK

			if (RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack)))
			{
				rc = (rc == FERR_BT_END_OF_DATA) ? RC_SET( FERR_BTREE_ERROR) : rc;
				goto Exit;
			}

			// Delete the element that points to the new last block

			if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack)))
			{
				goto Exit;
			}

			pStack++;

			// Read in the new last block Move in the old LEM to the end of
			// the block Position pStack to LEM

			if (RC_BAD( rc = FSGetBlock( pDb, pLFile, uiPrevBlk, pStack)))
			{
				goto Exit;
			}

			// Log block before modifying

			if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack)))
			{
				goto Exit;
			}

			pBlk = pStack->pBlk;

			// Set the pStack elements

			uiLastElm = pStack->uiBlkEnd;
			pStack->uiCurElm = uiLastElm;

			// Place the LEM (last element marker) in the block

			f_memcpy( &pBlk[uiLastElm], pLEMBuffer, uiElmOvhd);

			// Set next block pointer to BT_END

			UD2FBA( BT_END, &pBlk[BH_NEXT_BLK]);

			// Adjust end pointers

			pStack->uiBlkEnd = uiLastElm + uiElmOvhd;
			UW2FBA( (FLMUINT16) pStack->uiBlkEnd, &pBlk[BH_BLK_END]);

			*pStackRV = pStack;
		}
	}

	// Deleted the LAST (but not only) element in the block. The parent
	// block must have its current element changed to reflect the new last
	// element key in this block. The algoritm MUST insert the next last
	// element key and THEN delete the old key or you may have a new root
	// block which is your next block! Comentary: Some b-tree algorithms
	// have key markers that are shorter in the non-leaf blocks. This
	// right-end non-leaf trunctaion is a very good idea, but ALL of the
	// b-tree code has to conform to this rule and it is not a trivial
	// thing to support.

	else if (pStack->uiBlkEnd == pStack->uiCurElm)
	{
		pBlk = pStack->pBlk;

		// Double check in case of corrupt this is not a right most block

		if (FB2UD( &pBlk[BH_NEXT_BLK]) != BT_END)
		{
			rc = FSNewLastBlkElm( pDb, pLFile, &pStack, 
										 FSNLBE_LESS | FSNLBE_POSITION);
			*pStackRV = pStack;
		}
	}

	// Else - normal delete - everything should be correct and the stack is
	// positioned to the next element after the deleted element. The
	// pKeyBuf[] however, is NOT always set to be the current elms key.
	// Check to see if there is room to make a 3/2 combine and do it.

	else if (pStack->uiBlkEnd <=
					(FFILE_MIN_FILL * pDb->pFile->FileHdr.uiBlockSize / 100))
	{
		rc = FSCombineBlks( pDb, pLFile, &pStack);
		*pStackRV = pStack;	// Stack may have changed
	}

Exit:

	if (bReleaseTmpCache)
	{
		ScaReleaseCache( pTmpSCache, FALSE);
	}

	return (rc);
}

/****************************************************************************
Desc:  	Delete the parent element from where you are at in the stack.
Notes: 	In the future we should either pin down the current block or
			reread it in.
****************************************************************************/
RCODE FSDelParentElm(
	FDB *			pDb,
	LFILE *		pLFile,
	BTSK **	 	pStackRV)
{
	RCODE		rc;
	BTSK *	pStack = *pStackRV;

	pStack--;
	
	if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
	{
		goto Exit;
	}

	// Ignore status value from FSBtScanTo - just position for delete

	if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, (FLMUINT) 0)))
	{
		goto Exit;
	}

	// Call will position to the next element which pts to the next blk
	
	rc = FSBtDelete( pDb, pLFile, &pStack);

Exit:

	pStack++;							// Go down the pStack to the original level
	*pStackRV = pStack;
	
	return (rc);
}

/****************************************************************************
Desc:  	There is a new last block element, store key in parent and
			remove current element if( uiFlags & FSNLBE_LESS or GREATER is set
Notes:	Take note of the uiFlags - they can change what is done
****************************************************************************/
RCODE FSNewLastBlkElm(
	FDB *			pDb,
	LFILE *		pLFile,
	BTSK **	 	pStackRV,
	FLMUINT		uiFlags) // FSNLBE_GREATER, *_LESS, CK_COMBINE, POSITION or 0
{
	RCODE			rc;
	BTSK*			pStack = *pStackRV;
	FLMBYTE *	pBlk = pStack->pBlk;
	FLMBYTE *	pCurElm;							// Points to the current last element
	FLMUINT		uiOldCurElm = pStack->uiCurElm;
	FLMUINT		uiNextBlk = 0;
	FLMUINT		uiDomain = 0;					// Domain is index reference range
	FLMUINT		uiElmLen;						// Element length
	FLMUINT		uiKeyLen;
	FLMUINT		uiNewElmOvhd;
	FLMUINT		uiRefCount;
	FLMBYTE *	pKey;
	FLMBYTE		pElmBuffer[MAX_KEY_SIZ + BNE_KEY_COUNTS_START + BNE_DOMAIN_LEN];

	uiNewElmOvhd = (pStack - 1)->uiElmOvhd;
	uiElmLen = uiNewElmOvhd;

	if (uiNewElmOvhd == BNE_DATA_OVHD)
	{
		pKey = pElmBuffer;
	}
	else
	{
		pElmBuffer[BBE_PKC] = 0;				// Set PKC, DOMAIN to zero
		pElmBuffer[BBE_KL] = 0;
		pKey = pElmBuffer + uiNewElmOvhd;

		// Only update the counts if the inserting a key and not replacing.

		if (uiNewElmOvhd == BNE_KEY_COUNTS_START)
		{
			if (RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiBlkEnd,
						  NULL, NULL, &uiRefCount)))
			{
				goto Exit;
			}

			UD2FBA( (FLMUINT32)uiRefCount, &pElmBuffer[BNE_CHILD_COUNT]);
		}
	}

	// Build the pElmBuffer[] with the new last key in the block

	FSSetChildBlkAddr( pElmBuffer, pStack->uiBlkAddr, uiNewElmOvhd);

	if ((uiNextBlk = FB2UD( &pBlk[BH_NEXT_BLK])) == BT_END)
	{
		if (uiNewElmOvhd == BNE_DATA_OVHD)
		{
			UD2FBA( 0xFFFFFFFF, pElmBuffer);
		}

		uiKeyLen = 0;
		uiElmLen = uiNewElmOvhd;
		goto no_domain;
	}

	// else - fall through

	pStack->uiCurElm = pStack->uiBlkEnd;	// Position past last element
	FSBtPrevElm( pDb, pLFile, pStack);		// Build full key in pKeyBuf[]
	uiKeyLen = pStack->uiKeyLen;

	// Copy the key

	if (uiNewElmOvhd == BNE_DATA_OVHD)
	{
		flmCopyDrnKey( pElmBuffer, pStack->pKeyBuf);
	}
	else if (uiKeyLen)
	{
		f_memcpy( &pElmBuffer[uiNewElmOvhd], pStack->pKeyBuf, uiKeyLen);
		BBE_SET_KL( pElmBuffer, uiKeyLen);
		uiElmLen += uiKeyLen;
	}

	// If this is a boundqed index reference set then store DOMAIN

	pCurElm = CURRENT_ELM( pStack);

	uiDomain = (pLFile->uiLfType == LF_INDEX) 
							? FSGetDomain( &pCurElm, pStack->uiElmOvhd) 
							: (FLMUINT) 0;
							
	if (uiDomain)
	{
		BNE_SET_DOMAIN( pElmBuffer);
		
		pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain >> 16);
		pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain >> 8);
		pElmBuffer[uiElmLen++] = (FLMBYTE) (uiDomain & 0xFF);
	}

no_domain:

	// Go to the parent block, insert new element and delete the old last
	// element in the block. Pinning the current block is not suggested
	// because you could pin number of (levels - 1) blocks.

	pStack--;
	if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
	{
		goto Exit;
	}

	// If greater you should be positioned AFTER the matching element

	if (pStack->uiBlkEnd > BH_OVHD)			// Don't call if NO elements
	{

		// Set up the pStack elements for insert - passing keyLen sets up
		// pStack

		if (RC_BAD( rc = FSBtScanTo( pStack, pKey, uiKeyLen, uiDomain)))
		{
			goto Exit;
		}
	}
	else
	{
		pStack->uiPrevElmPKC = pStack->uiPKC = 0;
	}

	// Insert the element into the parent block - watch for splits!

	if (RC_OK( rc = FSBtInsert( pDb, pLFile, &pStack, pElmBuffer, uiElmLen)))
	{
		if (uiFlags & FSNLBE_LESS)
		{
			
			// Go to the next element, may read a new block!

			if (RC_OK( rc = FSBtNextElm( pDb, pLFile, pStack)))
			{
				if (RC_OK( rc = FSBtDelete( pDb, pLFile, &pStack)))
				{
					
					// Now position to the current element - back one

					if (!(uiFlags & FSNLBE_POSITION))
					{
						rc = FSBtPrevElm( pDb, pLFile, pStack);
						rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
					}
				}
				else
				{
					rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
				}
			}
		}
		else if (uiFlags & FSNLBE_GREATER)
		{
			if (RC_OK( rc = FSBtPrevElm( pDb, pLFile, pStack)))
			{
				if (RC_OK( rc = FSBtDelete( pDb, pLFile, &pStack)))
				{

					// Position to the next element if flag is set

					if (uiFlags & FSNLBE_POSITION)
					{
						rc = FSBtNextElm( pDb, pLFile, pStack);
						rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc;
					}
				}
			}
		}
	}

Exit:

	// Pop the pStack and position to next element in the block that you
	// expect to be positioned to. You should be positioned to the correct
	// parent element.

	pStack++;
	*pStackRV = pStack;							// Update caller's pStack
	
	if (RC_OK( rc))
	{
		if ((uiFlags & FSNLBE_POSITION) && (uiNextBlk != BT_END))
		{
			pStack->uiBlkAddr = uiNextBlk;
			uiOldCurElm = BH_OVHD;
		}

		// Read the next block and set pStack.

		if (RC_OK( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack)))
		{
			pStack->uiCurElm = uiOldCurElm;	// Restore original curElm value
			FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM);
		}
	}

	return (rc);
}

/***************************************************************************
Desc:		Delete the current element from a b-tree block without writing block.
			The pStack must point to the next element after the deleted element.
Notes:	Code handles deletion of an element at any level of the b-tree.
*****************************************************************************/
RCODE FSBlkDelElm(
	BTSK *		pStack)					// Stack of variables for each level
{
	RCODE			rc;
	FLMBYTE*		pDelElm;					// Points to deleted element
	FLMBYTE*		pCurElm;					// Points to current elm to move down
	FLMBYTE*		pBlk = pStack->pBlk; // Points to block for speed

	FLMUINT		uiCurElmOfs;			// Current (next) element's offset
	FLMUINT		uiDelElmPkc;			// # of carry bytes for deleted elm
	FLMUINT		uiCurElmPkc;			// Current element's Prev key count
	FLMUINT		uiCurElmKeyLen;		// Current element's key length
	FLMUINT		uiOldCurElm = pStack->uiCurElm;
	FLMUINT		uiElmOvhd = pStack->uiElmOvhd;
	FLMINT		iDelElmSize;			// Deleted element's size
	FLMINT		iPkcLost;				// Number bytes to expand for next elm

	pDelElm = &pBlk[pStack->uiCurElm];

	if (RC_OK( rc = FSBlkNextElm( pStack)))
	{

		// Setup to remove what pDelElm is pointing to. This is NOT the last
		// element in the block so move down the rest of the block data.

		uiCurElmOfs = pStack->uiCurElm;
		iDelElmSize = (FLMINT) (uiCurElmOfs - uiOldCurElm);
		pCurElm = &pBlk[uiCurElmOfs];

		if (pStack->uiBlkType != BHT_NON_LEAF_DATA)
		{
			uiDelElmPkc = (FLMUINT) (BBE_GET_PKC( pDelElm));
			uiCurElmPkc = (FLMUINT) (BBE_GET_PKC( pCurElm));

			// If current element uses bytes from the deleted element...

			if (uiCurElmPkc > uiDelElmPkc)
			{
				iPkcLost = (FLMINT) (uiCurElmPkc - uiDelElmPkc);

				// Create the new deleted element and setup for the shiftN()
				// below moving pCurElm.

				uiCurElmPkc -= iPkcLost;
				uiCurElmKeyLen = (FLMUINT) (BBE_GET_KL( pCurElm) + iPkcLost);

				BBE_SET_PKC( pDelElm, uiCurElmPkc); // Clears all bits
				BBE_SET_KL( pDelElm, uiCurElmKeyLen);
				*pDelElm |= (FLMBYTE) (BBE_IS_FIRST_LAST( pCurElm));

				if (pStack->uiBlkType == BHT_LEAF)
				{
					BBE_SET_RL( pDelElm, BBE_GET_RL( pCurElm));
				}
				else
				{
					f_memcpy( pDelElm + BNE_CHILD_BLOCK, pCurElm + BNE_CHILD_BLOCK,
								uiElmOvhd - BNE_CHILD_BLOCK);
				}

				// Adjust iDelElmSize and uiCurElmOfs to delete current element
				// overhead

				iDelElmSize -= iPkcLost;
				uiCurElmOfs += uiElmOvhd;

				// Move any extra bytes from the deleted element refered in
				// curElm

				f_memcpy( pDelElm + uiElmOvhd, &pStack->pKeyBuf[uiDelElmPkc],
							iPkcLost);

				// Fall through and copy the rest of the block down

			}
		}

		// Shift down - starting at pCurElm

		shiftN( &pBlk[uiCurElmOfs], pStack->uiBlkEnd - uiCurElmOfs,
				 (FLMINT) (0 - iDelElmSize));

		pStack->uiBlkEnd -= iDelElmSize;
	}
	else if (rc == FERR_BT_END_OF_DATA)
	{
		rc = FERR_OK;
		if (pStack->uiCurElm == pStack->uiBlkEnd)
		{	
			// Deleted last element in blk
			
			pStack->uiBlkEnd = uiOldCurElm;
		}
		else
		{

			// Need to move the last element marker (LEM) down;
			// This should only be used on leaf blocks
			
			pBlk[uiOldCurElm] = BBE_FIRST_FLAG | BBE_LAST_FLAG;
			pBlk[uiOldCurElm + BBE_KL] = pBlk[uiOldCurElm + BBE_RL] = 0;
			pStack->uiBlkEnd = uiOldCurElm + uiElmOvhd;
		}
	}
	else
	{
		goto Exit;
	}

	// Modify the element end offset & restore curElm & prevElm on pStack

	UW2FBA( (FLMUINT16) pStack->uiBlkEnd, &pBlk[BH_BLK_END]);
	pStack->uiCurElm = uiOldCurElm;
	
Exit:

	return (rc);
}

/****************************************************************************
Desc:  	Sets the parent element's child block value
****************************************************************************/
void FSSetChildBlkAddr(
	FLMBYTE *	pElement,
	FLMUINT		uiBlkAddr,
	FLMUINT		uiBlkOvhd)
{
	FLMBYTE *	pChildAddr;

	if (uiBlkOvhd == BNE_KEY_START || uiBlkOvhd == BNE_KEY_COUNTS_START)
	{
		pChildAddr = pElement + BNE_CHILD_BLOCK;
		UD2FBA( (FLMUINT32)uiBlkAddr, pChildAddr);
	}
	else if (uiBlkOvhd == BNE_DATA_OVHD)
	{
		pChildAddr = pElement + BNE_DATA_CHILD_BLOCK;
		UD2FBA( (FLMUINT32)uiBlkAddr, pChildAddr);
	}

	return;
}
