/*
 * DBFNSX RDD
 *
 * Copyright 2008 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * 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; see the file LICENSE.txt.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */


#define HB_NSX_SIX_STRICT
#define HB_NSX_CLEAR_UNUSED
/* #define HB_NSX_EXTERNAL_PAGEBUFFER */

/*
#define HB_NSX_DEBUG
#define HB_NSX_DEBUG_EXT
*/

#include "hbrddnsx.h"
#include "hbapiitm.h"
#include "hbapierr.h"
#include "hbinit.h"
#include "hbapilng.h"
#include "hbvm.h"
#include "hbset.h"
#include "hbstack.h"
#include "hbmath.h"
#include "rddsys.ch"
#include "hbregex.h"

static RDDFUNCS  nsxSuper;
static HB_USHORT s_uiRddId;

/* temporary casts to suppress 32/64-bit Windows warnings */
#define HB_USHORTCAST  HB_USHORT
#define HB_INTCAST     int

#define hb_nsxKeyFree( K )                hb_xfree( K )
#define hb_nsxFileOffset( I, B )          ( ( HB_FOFFSET ) ( B ) << ( ( I )->LargeFile ? NSX_PAGELEN_BITS : 0 ) )
#define hb_nsxGetRecSize( r )             ( ( r ) < 0x10000 ? 2 : ( ( r ) < 0x1000000 ? 3 : 4 ) )
#define hb_nsxPageBuffer( p )             ( ( p )->data.buffer )
#define hb_nsxIsLeaf( p )                 ( ( ( p )->data.buffer[ 0 ] & NSX_LEAFPAGE ) != 0 )
#define hb_nsxIsRoot( p )                 ( ( ( p )->data.buffer[ 0 ] & NSX_ROOTPAGE ) != 0 )
#define hb_nsxPageType( p )               ( hb_nsxPageBuffer( p )[ 0 ] )
#define hb_nsxSetPageType( p, t )         do { hb_nsxPageBuffer( p )[ 0 ] = ( t ); } while( 0 )
#define hb_nsxGetKeyRecSize( p )          ( hb_nsxPageBuffer( p )[ 1 ] )
#define hb_nsxGetKeyRecSizePtr( p )       ( ( p )[ 1 ] )
#define hb_nsxSetKeyRecSize( p, n )       do { hb_nsxPageBuffer( p )[ 1 ] = ( n ); } while( 0 )
#define hb_nsxGetBranchKeyPtr( p, l, n )  ( hb_nsxPageBuffer( p ) + ( n ) * ( ( l ) + 8 ) + 8 )
#define hb_nsxBranchKeyVal( p )           ( ( p ) + 8 )
#define hb_nsxBranchKeySize( p, l )       ( ( l ) + 8 )
/*
 * The helper functions (endian dependent) - on big endian machines
 * or RISC with strict alignment it's much better to use functions
 * then macros to inform compiler that can count complex parameters
 * only once.
 * On other machines it should not cause noticeable differences because
 * most of modern C compilers auto inline small functions
 */
#if defined( HB_LITTLE_ENDIAN ) && ! defined( HB_STRICT_ALIGNMENT ) && 1

#define hb_nsxGetKeyCount( p )            HB_GET_LE_UINT16( hb_nsxPageBuffer( p ) + 2 )
#define hb_nsxSetKeyCount( p, n )         HB_PUT_LE_UINT16( hb_nsxPageBuffer( p ) + 2, ( n ) )

#define hb_nsxLeafGetFreeOffset( p )      HB_GET_LE_UINT16( hb_nsxPageBuffer( p ) + 4 )
#define hb_nsxLeafSetFreeOffset( p, n )   HB_PUT_LE_UINT16( hb_nsxPageBuffer( p ) + 4, ( n ) )

#define hb_nsxGetLowerPage( p )           HB_GET_LE_UINT32( hb_nsxPageBuffer( p ) + 4 )
#define hb_nsxSetLowerPage( p, n )        HB_PUT_LE_UINT32( hb_nsxPageBuffer( p ) + 4, ( n ) )

#define hb_nsxGetKeyPage( p, l, n )       HB_GET_LE_UINT32( hb_nsxGetBranchKeyPtr( p, l, n ) )
#define hb_nsxSetKeyPage( p, l, n, u )    HB_PUT_LE_UINT32( hb_nsxGetBranchKeyPtr( p, l, n ), ( u ) )

#define hb_nsxGetKeyRec( p, l, n )        HB_GET_LE_UINT32( hb_nsxGetBranchKeyPtr( p, l, n ) + 4 )
#define hb_nsxSetKeyRec( p, l, n, r )     HB_PUT_LE_UINT32( hb_nsxGetBranchKeyPtr( p, l, n ) + 4, ( r ) )

#define hb_nsxGetKeyVal( p, l, n )        ( hb_nsxGetBranchKeyPtr( p, l, n ) + 8 )

#define hb_nsxBranchKeyPage( p )          HB_GET_LE_UINT32( p )
#define hb_nsxBranchKeyRec( p )           HB_GET_LE_UINT32( ( p ) + 4 )
#define hb_nsxBranchKeySetPage( p, u )    do { HB_PUT_LE_UINT32( ( p ), ( u ) ); } while( 0 )
#define hb_nsxBranchKeySetRec( p, u )     do { HB_PUT_LE_UINT32( ( p ) + 4, ( u ) ); } while( 0 )

#else

static HB_USHORT hb_nsxGetKeyCount( LPPAGEINFO pPage )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 2;

   return HB_GET_LE_UINT16( ptr );
}

static void hb_nsxSetKeyCount( LPPAGEINFO pPage, HB_USHORT uiKeys )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 2;

   HB_PUT_LE_UINT16( ptr, uiKeys );
}

static HB_ULONG hb_nsxGetLowerPage( LPPAGEINFO pPage )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 4;

   return HB_GET_LE_UINT32( ptr );
}

static void hb_nsxSetLowerPage( LPPAGEINFO pPage, HB_ULONG ulPage )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 4;

   HB_PUT_LE_UINT32( ptr, ulPage );
}

static HB_ULONG hb_nsxGetKeyPage( LPPAGEINFO pPage, HB_USHORT uiLen, HB_USHORT uiKey )
{
   HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, uiLen, uiKey );

   return HB_GET_LE_UINT32( ptr );
}

static void hb_nsxSetKeyPage( LPPAGEINFO pPage, HB_USHORT uiLen, HB_USHORT uiKey, HB_ULONG ulPage )
{
   HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, uiLen, uiKey );

   HB_PUT_LE_UINT32( ptr, ulPage );
}

static HB_ULONG hb_nsxGetKeyRec( LPPAGEINFO pPage, HB_USHORT uiLen, HB_USHORT uiKey )
{
   HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, uiLen, uiKey ) + 4;

   return HB_GET_LE_UINT32( ptr );
}

static void hb_nsxSetKeyRec( LPPAGEINFO pPage, HB_USHORT uiLen, HB_USHORT uiKey, HB_ULONG ulRec )
{
   HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, uiLen, uiKey ) + 4;

   HB_PUT_LE_UINT32( ptr, ulRec );
}

static HB_UCHAR * hb_nsxGetKeyVal( LPPAGEINFO pPage, HB_USHORT uiLen, HB_USHORT uiKey )
{
   return hb_nsxGetBranchKeyPtr( pPage, uiLen, uiKey ) + 8;
}

static void hb_nsxBranchKeySetPage( HB_UCHAR * ptr, HB_ULONG ulPage )
{
   HB_PUT_LE_UINT32( ptr, ulPage );
}

static void hb_nsxBranchKeySetRec( HB_UCHAR * ptr, HB_ULONG ulRec )
{
   ptr += 4;
   HB_PUT_LE_UINT32( ptr, ulRec );
}

static HB_ULONG hb_nsxBranchKeyPage( HB_UCHAR * ptr )
{
   return HB_GET_LE_UINT32( ptr );
}

static HB_ULONG hb_nsxBranchKeyRec( HB_UCHAR * ptr )
{
   ptr += 4;
   return HB_GET_LE_UINT32( ptr );
}

static HB_USHORT hb_nsxLeafGetFreeOffset( LPPAGEINFO pPage )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 4;

   return HB_GET_LE_UINT16( ptr );
}

static void hb_nsxLeafSetFreeOffset( LPPAGEINFO pPage, HB_USHORT uiOffset )
{
   HB_UCHAR * ptr = hb_nsxPageBuffer( pPage ) + 4;

   HB_PUT_LE_UINT16( ptr, uiOffset );
}

#endif

/* #define HB_NSX_NO_CORRUPT_PROTECT */

static HB_USHORT hb_nsxLeafGetKey( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiOffset,
                                   HB_UCHAR * bPrevValue, HB_ULONG * pulRecNo )
{
   HB_UCHAR * ptr = ( HB_UCHAR * ) hb_nsxPageBuffer( pPage );
   HB_UCHAR ucRecLen = hb_nsxGetKeyRecSizePtr( ptr ), ucSize;

#ifndef HB_NSX_NO_CORRUPT_PROTECT
   /* protection against corrupted NSX files */
   if( ucRecLen + uiOffset >= pPage->uiOffset )
      return 0;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

   switch( ucRecLen )
   {
      case 1:
         *pulRecNo = ptr[ uiOffset++ ];
         break;
      case 2:
         *pulRecNo = HB_GET_LE_UINT16( &ptr[ uiOffset ] );
         uiOffset += 2;
         break;
      case 3:
         *pulRecNo = HB_GET_LE_UINT24( &ptr[ uiOffset ] );
         uiOffset += 3;
         break;
      case 4:
         *pulRecNo = HB_GET_LE_UINT32( &ptr[ uiOffset ] );
         uiOffset += 4;
         break;
      default:
         /* protection against corrupted NSX files */
         return 0;
   }
   ucSize = ptr[ uiOffset++ ];
   if( ucSize != ucRecLen + 1 ) /* key value is not fully duplicated */
   {
      HB_UCHAR len = ( HB_UCHAR ) pTag->KeyLength;
      HB_UCHAR ucDupCount;

      /* ucSize = 0 is a special case when RecLen is 4 and KeySize is 250
       * in such case ucSize - ( ucRecLen + 2 ) gives 250 = NSX_MAXKEYLEN
       */
      ucSize -= ucRecLen + 2;

#ifndef HB_NSX_NO_CORRUPT_PROTECT
      /* protection against corrupted NSX files */
      if( ucSize > NSX_MAXKEYLEN || uiOffset + ucSize >= pPage->uiOffset )
         return 0;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

      ucDupCount = ptr[ uiOffset++ ];
      if( ucSize + ucDupCount == len )
      {
         /* key value is stored as raw data and can be copied as is */
         memcpy( &bPrevValue[ ucDupCount ], &ptr[ uiOffset ], ucSize );
         uiOffset += ucSize;
      }
#ifndef HB_NSX_NO_CORRUPT_PROTECT
      /* protection against corrupted NSX files */
      else if( ucSize + ucDupCount > len )
         return 0;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

      else
      {
         while( ucSize-- )
         {
            HB_UCHAR uc = ptr[ uiOffset++ ];

            if( uc == NSX_RLE_CHAR )
            {
               HB_UCHAR ucRepl;

#ifndef HB_NSX_NO_CORRUPT_PROTECT
               /* protection against corrupted NSX files */
               if( ! ucSize-- )
                  return 0;
#else
               --ucSize;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

               if( ( ucRepl = ptr[ uiOffset++ ] ) != 1 )
               {
#ifndef HB_NSX_NO_CORRUPT_PROTECT
                  /* protection against corrupted NSX files */
                  if( ! ucSize-- || ucRepl + ucDupCount > len )
                     return 0;
#else
                  --ucSize;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

                  uc = ptr[ uiOffset++ ];
                  while( ucRepl-- )
                     bPrevValue[ ucDupCount++ ] = uc;
                  continue;
               }
            }

#ifndef HB_NSX_NO_CORRUPT_PROTECT
            /* protection against corrupted NSX files */
            if( ucDupCount >= len )
               return 0;
#endif /* HB_NSX_NO_CORRUPT_PROTECT */

            bPrevValue[ ucDupCount++ ] = uc;
         }

         while( ucDupCount < len )
            bPrevValue[ ucDupCount++ ] = pTag->TrailChar;
      }
   }
   return uiOffset;
}

static HB_USHORT hb_nsxLeafPutKey( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiOffset,
                                   HB_UCHAR * bPrevValue, HB_UCHAR * pKeyValue, HB_ULONG ulRecNo )
{
   HB_UCHAR * ptr = ( HB_UCHAR * ) hb_nsxPageBuffer( pPage ) + uiOffset,
            * pDst, * pSrc, * pEnd;
   HB_UCHAR ucSize = hb_nsxGetKeyRecSize( pPage ), ucDupCount = 0,
            ucLen = ( HB_UCHAR ) pTag->KeyLength;
   int iMax;

   if( uiOffset + ucSize >= NSX_PAGELEN )
      return 0;

   switch( ucSize )
   {
      case 1:
         *ptr++ = ( HB_UCHAR ) ulRecNo;
         break;
      case 2:
         HB_PUT_LE_UINT16( ptr, ulRecNo );
         ptr += 2;
         break;
      case 3:
         HB_PUT_LE_UINT24( ptr, ulRecNo );
         ptr += 3;
         break;
      default:
         HB_PUT_LE_UINT32( ptr, ulRecNo );
         ptr += 4;
         break;
   }
   ++ucSize;   /* Size */

   if( bPrevValue )
   {
      while( bPrevValue[ ucDupCount ] == pKeyValue[ ucDupCount ] )
      {
         ++ucDupCount;
         if( --ucLen == 0 )
         {
            *ptr = ucSize;
            return uiOffset + ucSize;
         }
      }
   }
   ++ucSize;   /* DupCount */

   if( uiOffset + ucSize > NSX_PAGELEN )
      return 0;

   ptr[ 1 ] = ucDupCount;

   pSrc = &pKeyValue[ ucDupCount ];
   pEnd = pSrc + ucLen;

   while( pEnd[ -1 ] == pTag->TrailChar )
   {
      if( --pEnd == pSrc )
      {
         *ptr = ucSize;
         return uiOffset + ucSize;
      }
   }

   pDst = ptr + 2;
   iMax = NSX_PAGELEN - uiOffset - ucSize + 1;
   if( iMax > ( int ) ucLen )
      iMax = ucLen;
   if( iMax > 0 )
   {
      while( pSrc < pEnd )
      {
         HB_UCHAR uc = *pSrc++;
         if( uc == NSX_RLE_CHAR )
         {
            if( pSrc < pEnd && *pSrc == NSX_RLE_CHAR )
            {
               HB_UCHAR ucRepl = 2;
               if( ( iMax -= 3 ) <= 0 )
                  break;
               ++pSrc;
               while( pSrc < pEnd && *pSrc == NSX_RLE_CHAR )
               {
                  ++pSrc;
                  ++ucRepl;
               }
               *pDst++ = NSX_RLE_CHAR;
               *pDst++ = ucRepl;
               *pDst++ = NSX_RLE_CHAR;
            }
            else
            {
               if( ( iMax -= 2 ) <= 0 )
                  break;
               *pDst++ = NSX_RLE_CHAR;
               *pDst++ = 1;
            }
         }
         else if( pEnd - pSrc > 2 &&
                  *pSrc == uc && pSrc[ 1 ] == uc && pSrc[ 2 ] == uc )
         {
            HB_UCHAR ucRepl = 4;
            if( ( iMax -= 3 ) <= 0 )
               break;
            pSrc += 3;
            while( pSrc < pEnd && *pSrc == uc )
            {
               ++pSrc;
               ++ucRepl;
            }
            *pDst++ = NSX_RLE_CHAR;
            *pDst++ = ucRepl;
            *pDst++ = uc;
         }
         else if( --iMax == 0 )
            break;
         else
            *pDst++ = uc;
      }
      if( iMax > 0 )
      {
         ucSize += ( HB_UCHAR ) ( pDst - ( ptr + 2 ) );
         *ptr = ucSize;
         return uiOffset + ucSize;
      }
   }

   uiOffset += ucSize + ucLen;
   if( uiOffset > NSX_PAGELEN )
      return 0;
   memcpy( ptr + 2, &pKeyValue[ ucDupCount ], ucLen );
   *ptr = ucSize + ucLen;

   return uiOffset;
}

/*
 * generate Run-Time error
 */
static HB_ERRCODE hb_nsxErrorRT( NSXAREAP pArea,
                                 HB_ERRCODE errGenCode, HB_ERRCODE errSubCode,
                                 const char * szFileName, HB_ERRCODE errOsCode,
                                 HB_USHORT uiFlags, PHB_ITEM * pErrorPtr )
{
   HB_ERRCODE iRet = HB_FAILURE;

   if( hb_vmRequestQuery() == 0 )
   {
      PHB_ITEM pError;
      if( pErrorPtr )
      {
         if( ! *pErrorPtr )
            *pErrorPtr = hb_errNew();
         pError = *pErrorPtr;
      }
      else
         pError = hb_errNew();
      hb_errPutGenCode( pError, errGenCode );
      hb_errPutSubCode( pError, errSubCode );
      hb_errPutOsCode( pError, errOsCode );
      hb_errPutDescription( pError, hb_langDGetErrorDesc( errGenCode ) );
      if( szFileName )
         hb_errPutFileName( pError, szFileName );
      if( uiFlags )
         hb_errPutFlags( pError, uiFlags );
      iRet = SELF_ERROR( &pArea->dbfarea.area, pError );
      if( ! pErrorPtr )
         hb_errRelease( pError );
   }
   return iRet;
}

static void hb_nsxCorruptError( LPNSXINDEX pIndex )
{
   hb_nsxErrorRT( pIndex->pArea, EG_CORRUPTION, EDBF_CORRUPT,
                  pIndex->IndexName, 0, 0, NULL );
}

/*
 * create new index key
 */
static LPKEYINFO hb_nsxKeyNew( int keylen )
{
   LPKEYINFO pKey;

   pKey = ( LPKEYINFO ) hb_xgrab( sizeof( KEYINFO ) + keylen );
   pKey->val[ keylen ] = '\0';
   pKey->page = pKey->rec = 0;

   return pKey;
}

/*
 * copy index key, if dst is null create new dst key else destroy dst
 */
static LPKEYINFO hb_nsxKeyCopy( LPKEYINFO pKeyDest, LPKEYINFO pKey, int keylen )
{
   if( ! pKeyDest )
      pKeyDest = ( LPKEYINFO ) hb_xgrab( sizeof( KEYINFO ) + keylen );

   memcpy( pKeyDest, pKey, sizeof( KEYINFO ) + keylen );

   return pKeyDest;
}

/*
 * get NSX key type for given item
 */
static HB_BYTE hb_nsxItemType( PHB_ITEM pItem )
{
   switch( hb_itemType( pItem ) )
   {
      case HB_IT_STRING:
      case HB_IT_MEMO:
         return 'C';

      case HB_IT_INTEGER:
      case HB_IT_LONG:
      case HB_IT_DOUBLE:
         return 'N';

      case HB_IT_DATE:
         return 'D';

      case HB_IT_TIMESTAMP:
         return 'T';

      case HB_IT_LOGICAL:
         return 'L';

      default:
         return 'U';
   }
}

/*
 * convert NSX (Clipper) type of key expression to internal one
 */
static HB_UCHAR hb_nsxKeyType( HB_USHORT uiType, HB_BYTE * pbTrail )
{
   switch( uiType )
   {
      case NSX_TYPE_LNUM:
      case NSX_TYPE_DNUM:
         *pbTrail = '\0';
         return 'N';

      case NSX_TYPE_LDATE:
         *pbTrail = '\0';
         return 'D';

      case NSX_TYPE_TIMESTAMP:
         *pbTrail = '\0';
         return 'T';

      case NSX_TYPE_LOG:
         *pbTrail = '\0';
         return 'L';

      case NSX_TYPE_CHAR:
         *pbTrail = ' ';
         return 'C';

      default:
         *pbTrail = '\0';
         return 'U';
   }
}

/*
 * convert internal type of key expression to comparable type
 */
static HB_UCHAR hb_nsxKeyTypeCmp( HB_UCHAR ucType )
{
   return ucType == 'T' ? 'D' : ucType;
}

/*
 * convert internal type of key expression to NSX (Clipper) one
 */
static HB_USHORT hb_nsxKeyTypeRaw( HB_UCHAR ucType )
{
   switch( ucType )
   {
      case 'N':
         return NSX_TYPE_DNUM;

      case 'D':
         return NSX_TYPE_LDATE;

      case 'T':
         return NSX_TYPE_TIMESTAMP;

      case 'L':
         return NSX_TYPE_LOG;

      case 'C':
         return NSX_TYPE_CHAR;

      default:
         return '\0';
   }
}

/*
 * store Item in index key
 * TODO: uiType check and generate RT error if necessary
 *       probably not here or we will have to add parameter
 *       for scope key evaluation
 */
static LPKEYINFO hb_nsxKeyPutItem( LPKEYINFO pKey, PHB_ITEM pItem, HB_ULONG ulRecNo,
                                   LPTAGINFO pTag, HB_BOOL fTrans, HB_USHORT *puiLen )
{
   double d;
   HB_SIZE len;

   if( ! pKey )
      pKey = hb_nsxKeyNew( pTag->KeyLength );

   if( puiLen )
   {
      *puiLen = pTag->KeyLength;
      pKey->mode = NSX_CMP_PREFIX;
   }
   else
      pKey->mode = NSX_CMP_EXACT;

   switch( hb_nsxItemType( pItem ) )
   {
      case 'C':
         if( fTrans )
         {
            len = pTag->KeyLength;
            hb_cdpnDup2( hb_itemGetCPtr( pItem ), hb_itemGetCLen( pItem ),
                         ( char * ) pKey->val, &len,
                         hb_vmCDP(), pTag->pIndex->pArea->dbfarea.area.cdPage );
         }
         else
         {
            len = hb_itemGetCLen( pItem );
            if( len > ( HB_SIZE ) pTag->KeyLength )
               len = pTag->KeyLength;
            memcpy( pKey->val, hb_itemGetCPtr( pItem ), len );
         }
         if( len < ( HB_SIZE ) pTag->KeyLength )
         {
            memset( pKey->val + len, pTag->TrailChar, pTag->KeyLength - len );
            if( puiLen )
               *puiLen = ( HB_USHORT ) len;
         }
         break;
      case 'N':
         d = hb_itemGetND( pItem );
         HB_DBL2ORD( &d, pKey->val );
         break;
      case 'D':
         d = ( double ) hb_itemGetDL( pItem );
         HB_DBL2ORD( &d, pKey->val );
         if( puiLen && pTag->KeyType == 'T' )
            pKey->mode = NSX_CMP_DATE;
         break;
      case 'T':
         if( pTag->KeyType == 'D' )
            d = ( double ) hb_itemGetDL( pItem );
         else
            d = hb_itemGetTD( pItem );
         HB_DBL2ORD( &d, pKey->val );
         break;
      case 'L':
         pKey->val[ 0 ] = hb_itemGetL( pItem ) ? 'T' : 'F';
         break;
      default:
         memset( pKey->val, '\0', pTag->KeyLength );
   }
   pKey->rec = ulRecNo;
   pKey->page = 0;

   return pKey;
}

/*
 * get Item from index key
 */
static PHB_ITEM hb_nsxKeyGetItem( PHB_ITEM pItem, LPKEYINFO pKey,
                                  LPTAGINFO pTag, HB_BOOL fTrans )
{
   double d;

   if( pKey )
   {
      switch( pTag->KeyType )
      {
         case 'C':
            if( fTrans )
            {
               HB_SIZE nLen = pTag->KeyLength;
               char * pszVal = hb_cdpnDup( ( const char * ) pKey->val, &nLen,
                                           pTag->pIndex->pArea->dbfarea.area.cdPage, hb_vmCDP() );
               pItem = hb_itemPutCLPtr( pItem, pszVal, nLen );
            }
            else
            {
               pItem = hb_itemPutCL( pItem, ( char * ) pKey->val, pTag->KeyLength );
            }
            break;
         case 'N':
            HB_ORD2DBL( pKey->val, &d );
            pItem = hb_itemPutND( pItem, d );
            break;
         case 'D':
            HB_ORD2DBL( pKey->val, &d );
            pItem = hb_itemPutDL( pItem, ( long ) d );
            break;
         case 'T':
            HB_ORD2DBL( pKey->val, &d );
            pItem = hb_itemPutTD( pItem, d );
            break;
         case 'L':
            pItem = hb_itemPutL( pItem, pKey->val[ 0 ] == 'T' );
            break;
         default:
            if( pItem )
               hb_itemClear( pItem );
            else
               pItem = hb_itemNew( NULL );
      }
   }
   else if( pItem )
      hb_itemClear( pItem );
   else
      pItem = hb_itemNew( NULL );

   return pItem;
}

/*
 * evaluate key expression and create new Key from the result
 */
static LPKEYINFO hb_nsxEvalKey( LPKEYINFO pKey, LPTAGINFO pTag )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   PHB_ITEM pItem;
   PHB_CODEPAGE cdpTmp = hb_cdpSelect( pArea->dbfarea.area.cdPage );

   if( pTag->nField )
   {
      pItem = hb_itemNew( NULL );
      SELF_GETVALUE( &pArea->dbfarea.area, pTag->nField, pItem );
      pKey = hb_nsxKeyPutItem( pKey, pItem, pArea->dbfarea.ulRecNo, pTag, HB_FALSE, NULL );
      hb_itemRelease( pItem );
   }
   else
   {
      int iCurrArea = hb_rddGetCurrentWorkAreaNumber();

      if( iCurrArea != pArea->dbfarea.area.uiArea )
         hb_rddSelectWorkAreaNumber( pArea->dbfarea.area.uiArea );
      else
         iCurrArea = 0;

      pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem );
      pKey = hb_nsxKeyPutItem( pKey, pItem, pArea->dbfarea.ulRecNo, pTag, HB_FALSE, NULL );

      if( iCurrArea )
         hb_rddSelectWorkAreaNumber( iCurrArea );
   }

   hb_cdpSelect( cdpTmp );

   return pKey;
}

/*
 * evaluate conditional expression and return the logical result
 */
static HB_BOOL hb_nsxEvalCond( NSXAREAP pArea, PHB_ITEM pCondItem, HB_BOOL fSetWA )
{
   int iCurrArea = 0;
   HB_BOOL fRet;

   if( fSetWA )
   {
      iCurrArea = hb_rddGetCurrentWorkAreaNumber();
      if( iCurrArea != pArea->dbfarea.area.uiArea )
         hb_rddSelectWorkAreaNumber( pArea->dbfarea.area.uiArea );
      else
         iCurrArea = 0;
   }

   fRet = hb_itemGetL( hb_vmEvalBlockOrMacro( pCondItem ) );

   if( iCurrArea )
      hb_rddSelectWorkAreaNumber( iCurrArea );

   return fRet;
}

/*
 * evaluate seek/skip block: {| key, rec | ... }
 */
static HB_BOOL hb_nsxEvalSeekCond( LPTAGINFO pTag, PHB_ITEM pCondItem )
{
   HB_BOOL fRet;
   PHB_ITEM pKeyVal, pKeyRec;

   pKeyVal = hb_nsxKeyGetItem( NULL, pTag->CurKeyInfo, pTag, HB_TRUE );
   pKeyRec = hb_itemPutNInt( NULL, pTag->CurKeyInfo->rec );

   fRet = hb_itemGetL( hb_vmEvalBlockV( pCondItem, 2, pKeyVal, pKeyRec ) );

   hb_itemRelease( pKeyVal );
   hb_itemRelease( pKeyRec );

   return fRet;
}

/*
 * compare two values using Tag conditions (len & type)
 */
static int hb_nsxValCompare( LPTAGINFO pTag, const HB_UCHAR * val1, int len1,
                             const HB_UCHAR * val2, int len2, int iMode )
{
   int iLimit, iResult = 0;

   iLimit = ( len1 > len2 ) ? len2 : len1;

   if( pTag->KeyType == 'C' )
   {
      if( iLimit > 0 )
      {
         if( HB_CDP_ISBINSORT( pTag->pIndex->pArea->dbfarea.area.cdPage ) )
            iResult = memcmp( val1, val2, iLimit );
         else
            return -hb_cdpcmp( ( const char * ) val2, ( HB_SIZE ) len2,
                               ( const char * ) val1, ( HB_SIZE ) len1,
                               pTag->pIndex->pArea->dbfarea.area.cdPage, 0 );
      }

      if( iResult == 0 )
      {
         if( len1 > len2 )
            iResult = 1;
         else if( len1 < len2 && iMode == NSX_CMP_EXACT )
            iResult = -1;
      }
      else if( iResult > 0 )
         iResult = 1;
      else
         iResult = -1;
   }
   else if( iMode == NSX_CMP_DATE && iLimit == 8 )
   {
      double d1, d2;
      long l;

      HB_ORD2DBL( val1, &d1 );
      HB_ORD2DBL( val2, &d2 );
      l = ( long ) d1 - ( long ) d2;
      if( l < 0 )
         iResult = -1;
      else if( l > 0 )
         iResult = 1;
   }
   else
   {
      if( iLimit == 0 || ( iResult = memcmp( val1, val2, iLimit ) ) == 0 )
      {
         if( len1 > len2 )
            iResult = 1;
         else if( len1 < len2 )
            iResult = -1;
      }
      else if( iResult > 0 )
         iResult = 1;
      else
         iResult = -1;
   }
   return iResult;
}

/*
 * check if a given key is in top scope
 */
static HB_BOOL hb_nsxInTopScope( LPTAGINFO pTag, HB_UCHAR * key )
{
   PHB_NSXSCOPE pScope = pTag->fUsrDescend ? &pTag->bottom : &pTag->top;

   if( pScope->scopeKeyLen )
   {
      int i = hb_nsxValCompare( pTag, pScope->scopeKey->val, pScope->scopeKeyLen,
                                 key, pTag->KeyLength, pScope->scopeKey->mode );
      return pTag->fUsrDescend ? i >= 0 : i <= 0;
   }
   else
      return HB_TRUE;
}

/*
 * check if a given key is in bottom scope
 */
static HB_BOOL hb_nsxInBottomScope( LPTAGINFO pTag, HB_UCHAR * key )
{
   PHB_NSXSCOPE pScope = pTag->fUsrDescend ? &pTag->top : &pTag->bottom;

   if( pScope->scopeKeyLen )
   {
      int i = hb_nsxValCompare( pTag, pScope->scopeKey->val, pScope->scopeKeyLen,
                                key, pTag->KeyLength, pScope->scopeKey->mode );
      return pTag->fUsrDescend ? i <= 0 : i >= 0;
   }
   else
      return HB_TRUE;
}

/*
 * check if a given key is in current scope
 */
static HB_BOOL hb_nsxKeyInScope( LPTAGINFO pTag, LPKEYINFO pKey )
{
   return hb_nsxInTopScope( pTag, pKey->val ) &&
          hb_nsxInBottomScope( pTag, pKey->val );
}

/*
 * clear top or bottom scope
 */
static void hb_nsxTagClearScope( LPTAGINFO pTag, HB_USHORT nScope )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   PHB_NSXSCOPE pScope;

   /* resolve any pending scope relations first */
   if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( pTag->fUsrDescend )
      nScope = ( nScope == 0 ) ? 1 : 0;

   pScope = ( nScope == 0 ) ? &pTag->top : &pTag->bottom;

   if( pScope->scopeKey )
   {
      hb_nsxKeyFree( pScope->scopeKey );
      pScope->scopeKey = NULL;
   }
   if( pScope->scopeItem )
   {
      hb_itemRelease( pScope->scopeItem );
      pScope->scopeItem = NULL;
   }
   pScope->scopeKeyLen = 0;

   pTag->keyCount = 0;
}

/*
 * set top or bottom scope
 */
static void hb_nsxTagSetScope( LPTAGINFO pTag, HB_USHORT nScope, PHB_ITEM pItem )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   PHB_ITEM pScopeVal;

   /* resolve any pending scope relations first */
   if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->dbfarea.area );

   pScopeVal = ( hb_itemType( pItem ) & HB_IT_BLOCK ) ?
                           hb_vmEvalBlock( pItem ) : pItem;

   if( hb_nsxKeyTypeCmp( pTag->KeyType ) == hb_nsxKeyTypeCmp( hb_nsxItemType( pScopeVal ) ) )
   {
      PHB_NSXSCOPE pScope;
      HB_BOOL fTop = ( nScope == 0 );

      if( pTag->fUsrDescend )
         fTop = ! fTop;

      pScope = fTop ? &pTag->top : &pTag->bottom;

      pScope->scopeKey = hb_nsxKeyPutItem( pScope->scopeKey, pScopeVal,
               fTop ? NSX_IGNORE_REC_NUM : NSX_MAX_REC_NUM,
               pTag, HB_TRUE, &pScope->scopeKeyLen );

      if( pScope->scopeItem == NULL )
         pScope->scopeItem = hb_itemNew( NULL );
      hb_itemCopy( pScope->scopeItem, pItem );

      pTag->keyCount = 0;
   }
   else
   {
      hb_nsxTagClearScope( pTag, nScope );
   }
}

/*
 * get top or bottom scope item
 */
static void hb_nsxTagGetScope( LPTAGINFO pTag, HB_USHORT nScope, PHB_ITEM pItem )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   PHB_NSXSCOPE pScope;

   /* resolve any pending scope relations first */
   if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( pTag->fUsrDescend )
      nScope = ( nScope == 0 ) ? 1 : 0;

   pScope = ( nScope == 0 ) ? &pTag->top : &pTag->bottom;

   if( pScope->scopeItem )
      hb_itemCopy( pItem, pScope->scopeItem );
   else
      hb_itemClear( pItem );
}

/*
 * refresh top and bottom scope value if set as codeblock
 */
static void hb_nsxTagRefreshScope( LPTAGINFO pTag )
{
   PHB_ITEM pItem;

   /* resolve any pending scope relations first */
   if( pTag->pIndex->pArea->dbfarea.lpdbPendingRel &&
       pTag->pIndex->pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pTag->pIndex->pArea->dbfarea.area );

   if( hb_itemType( pTag->top.scopeItem ) & HB_IT_BLOCK )
   {
      pItem = hb_vmEvalBlock( pTag->top.scopeItem );
      pTag->top.scopeKey = hb_nsxKeyPutItem( pTag->top.scopeKey, pItem,
               pTag->top.scopeKey->rec, pTag, HB_TRUE, &pTag->top.scopeKeyLen );
   }
   if( hb_itemType( pTag->bottom.scopeItem ) & HB_IT_BLOCK )
   {
      pItem = hb_vmEvalBlock( pTag->bottom.scopeItem );
      pTag->bottom.scopeKey = hb_nsxKeyPutItem( pTag->bottom.scopeKey, pItem,
         pTag->bottom.scopeKey->rec, pTag, HB_TRUE, &pTag->bottom.scopeKeyLen );
   }
}

/*
 * an interface for fast check record number in record filter
 */
static HB_BOOL hb_nsxCheckRecordScope( NSXAREAP pArea, HB_ULONG ulRec )
{
   HB_LONG lRecNo = ( HB_LONG ) ulRec;

   if( SELF_COUNTSCOPE( &pArea->dbfarea.area, NULL, &lRecNo ) == HB_SUCCESS && lRecNo == 0 )
   {
      return HB_FALSE;
   }
   return HB_TRUE;
}

#ifdef HB_NSX_DEBUG
static void hb_nsxTagCheckBuffers( LPTAGINFO pTag )
{
   LPPAGEINFO pPage;
   HB_ULONG i;

   if( ( pTag->HdrChanged || pTag->pIndex->Changed ) && ! pTag->pIndex->lockWrite )
      hb_errInternal( 9999, "hb_nsxTagCheckBuffers: tag modified in unlocked index", NULL, NULL );

   for( i = 0; i < pTag->pIndex->ulPages; i++ )
   {
      pPage = pTag->pIndex->pages[ i ];
      if( pPage->Changed && ! pTag->pIndex->lockWrite )
         hb_errInternal( 9999, "hb_nsxTagCheckBuffers: page modified in unlocked index", NULL, NULL );
      if( pPage->iUsed )
         hb_errInternal( 9999, "hb_nsxTagCheckBuffers: page still allocated", NULL, NULL );
   }
}

static void hb_nsxPageCheckKeys( LPPAGEINFO pPage, LPTAGINFO pTag, int iPos, int iType )
{
   HB_UCHAR pKeyVal[ NSX_MAXKEYLEN ], pKeyPrev[ NSX_MAXKEYLEN ];
   HB_ULONG ulRecNo = 0, ulPrevRec;
   HB_USHORT uiOffset = NSX_LEAFKEYOFFSET, u;
   int i;

   if( hb_nsxIsLeaf( pPage ) && pPage->uiKeys )
   {
      /* We do not need real previous key value and we can use any */
      memset( pKeyVal, pTag->TrailChar, pTag->KeyLength );
      uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset, pKeyVal, &ulRecNo );
      if( uiOffset == 0 )
      {
         hb_nsxCorruptError( pTag->pIndex );
         return;
      }
   }

   for( u = 1; u < pPage->uiKeys; u++ )
   {
      if( hb_nsxIsLeaf( pPage ) )
      {
         memcpy( pKeyPrev, pKeyVal, pTag->KeyLength );
         ulPrevRec = ulRecNo;
         uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset, pKeyVal, &ulRecNo );
         if( uiOffset == 0 )
         {
            hb_nsxCorruptError( pTag->pIndex );
            return;
         }
         i = hb_nsxValCompare( pTag, pKeyPrev, pTag->KeyLength,
                               pKeyVal, pTag->KeyLength, NSX_CMP_EXACT );
         if( i == 0 )
            i = ulRecNo > ulPrevRec ? -1 : 1;

         if( i > 0 )
         {
            printf( "\r\nuiKeys=%d(%d/%d), (%d)[%.*s]<%ld>>(%d)[%.*s]<%ld>", pPage->uiKeys, iPos, iType,
                    u - 1, pTag->KeyLength, pKeyPrev, ulPrevRec, u, pTag->KeyLength, pKeyVal, ulRecNo );
            fflush( stdout );
            hb_errInternal( 9999, "hb_nsxPageCheckKeys: keys sorted wrong.", NULL, NULL );
         }
      }
      else
      {
         i = hb_nsxValCompare( pTag,
                               hb_nsxGetKeyVal( pPage, pTag->KeyLength, u - 1 ),
                               pTag->KeyLength,
                               hb_nsxGetKeyVal( pPage, pTag->KeyLength, u ),
                               pTag->KeyLength, NSX_CMP_EXACT );
         if( i == 0 )
            i = hb_nsxGetKeyRec( pPage, pTag->KeyLength, u ) >
                hb_nsxGetKeyRec( pPage, pTag->KeyLength, u - 1 ) ? -1 : 1;
         if( i > 0 )
         {
            printf( "\r\nuiKeys=%d(%d/%d), (%d)[%.*s]>(%d)[%.*s]", pPage->uiKeys, iPos, iType,
                    u - 1, pTag->KeyLength, hb_nsxGetKeyVal( pPage, pTag->KeyLength, u - 1 ),
                    u, pTag->KeyLength, hb_nsxGetKeyVal( pPage, pTag->KeyLength, u ) );
            fflush( stdout );
            hb_errInternal( 9999, "hb_nsxPageCheckKeys: keys sorted wrong.", NULL, NULL );
         }
      }
   }
   if( hb_nsxIsLeaf( pPage ) && pPage->uiOffset != uiOffset )
   {
      printf( "\r\npPage->uiOffset=%d, uiOffset=%d\r\n", pPage->uiOffset, uiOffset );
      fflush( stdout );
      hb_errInternal( 9999, "hb_nsxPageCheckKeys: wrong free offset in leaf page.", NULL, NULL );
   }
}
#endif

/*
 * read a given block from index file
 */
static HB_BOOL hb_nsxBlockRead( LPNSXINDEX pIndex, HB_ULONG ulBlock, void * buffer, int iSize )
{
   if( ! pIndex->lockRead && ! pIndex->lockWrite )
      hb_errInternal( 9103, "hb_nsxBlockRead on not locked index file.", NULL, NULL );

   if( hb_fileReadAt( pIndex->pFile, buffer, iSize,
                      hb_nsxFileOffset( pIndex, ulBlock ) ) != ( HB_SIZE ) iSize )
   {
      hb_nsxErrorRT( pIndex->pArea, EG_READ, EDBF_READ,
                     pIndex->IndexName, hb_fsError(), 0, NULL );
      return HB_FALSE;
   }
   return HB_TRUE;
}

/*
 * write a given block into index file
 */
static HB_BOOL hb_nsxBlockWrite( LPNSXINDEX pIndex, HB_ULONG ulBlock, const void * buffer, int iSize )
{
   if( ! pIndex->lockWrite )
      hb_errInternal( 9102, "hb_nsxBlockWrite on not locked index file.", NULL, NULL );

   if( hb_fileWriteAt( pIndex->pFile, buffer, iSize,
                       hb_nsxFileOffset( pIndex, ulBlock ) ) != ( HB_SIZE ) iSize )
   {
      hb_nsxErrorRT( pIndex->pArea, EG_WRITE, EDBF_WRITE,
                     pIndex->IndexName, hb_fsError(), 0, NULL );
      return HB_FALSE;
   }
   return HB_TRUE;
}

/*
 * write a given tag page to file
 */
static HB_BOOL hb_nsxPageSave( LPNSXINDEX pIndex, LPPAGEINFO pPage )
{
   hb_nsxSetKeyCount( pPage, pPage->uiKeys );
   if( hb_nsxIsLeaf( pPage ) )
   {
      hb_nsxLeafSetFreeOffset( pPage, pPage->uiOffset );
   }
   if( ! hb_nsxBlockWrite( pIndex, pPage->Page,
                           hb_nsxPageBuffer( pPage ), NSX_PAGELEN ) )
      return HB_FALSE;
   pPage->Changed = HB_FALSE;
   pIndex->fFlush = HB_TRUE;
   /* In shared mode we have to update counter in version field of
      NSXHEADER to signal for other stations that their index buffers
      have to be discarded */
   if( pIndex->fShared )
      pIndex->Changed = HB_TRUE;
   return HB_TRUE;
}

/*
 * discard all index buffers due to concurrent access
 */
static void hb_nsxDiscardBuffers( LPNSXINDEX pIndex )
{
   int i;

   pIndex->ulPages = pIndex->ulPageLast = 0;
   pIndex->pChanged = pIndex->pFirst = pIndex->pLast = NULL;
   for( i = 0; i < pIndex->iTags; i++ )
   {
      pIndex->lpTags[ i ]->RootBlock = 0;
      pIndex->lpTags[ i ]->stackLevel = 0;
      pIndex->lpTags[ i ]->CurKeyOffset = 0;
   }
   hb_fileFlush( pIndex->pFile, HB_FALSE );
}

/*
 * update tag flags
 */
static void hb_nsxTagUpdateFlags( LPTAGINFO pTag )
{
   pTag->Custom    = ( pTag->TagFlags & NSX_TAG_NOUPDATE ) != 0;
   pTag->ChgOnly   = ( pTag->TagFlags & NSX_TAG_CHGONLY  ) != 0;
   pTag->Partial   = ( pTag->TagFlags & NSX_TAG_PARTIAL  ) != 0;
   pTag->Template  = ( pTag->TagFlags & NSX_TAG_TEMPLATE ) != 0;
   pTag->MultiKey  = ( pTag->TagFlags & NSX_TAG_MULTIKEY ) != 0;
}

/*
 * check tag header in compound index
 */
static HB_BOOL hb_nsxTagHeaderCheck( LPTAGINFO pTag )
{
   if( ! pTag->RootBlock )
   {
      if( pTag->HeadBlock )
      {
         NSXTAGHEADERUPDT header;

         if( hb_nsxBlockRead( pTag->pIndex, pTag->HeadBlock, &header, sizeof( header ) ) )
         {
            if( header.Signature[ 0 ] == NSX_SIGNATURE )
            {
               pTag->TagFlags = header.TagFlags[ 0 ];
               pTag->RootBlock = HB_GET_LE_UINT32( header.RootPage );
               hb_nsxTagUpdateFlags( pTag );
            }
         }
      }
   }
   return pTag->RootBlock != 0;
}

static HB_ULONG hb_nsxTagRootBlock( LPTAGINFO pTag )
{
   if( ! pTag->RootBlock )
   {
      if( ! hb_nsxTagHeaderCheck( pTag ) )
      {
         hb_nsxCorruptError( pTag->pIndex );
         return 0;
      }
   }

   return pTag->RootBlock;
}

/*
 * free buffers for pages in the tag
 */
static void hb_nsxFreePageBuffer( LPNSXINDEX pIndex )
{
   HB_ULONG ul, ulMax = pIndex->ulPagesDepth;

   if( ulMax )
   {
      LPPAGEINFO * pPagePtr = pIndex->pages;

      for( ul = 0; ul < ulMax; ul++, pPagePtr++ )
      {
         if( *pPagePtr )
         {
#ifdef HB_NSX_EXTERNAL_PAGEBUFFER
            if( hb_nsxPageBuffer( *pPagePtr ) )
               hb_xfree( hb_nsxPageBuffer( *pPagePtr ) );
#endif
            hb_xfree( *pPagePtr );
         }
      }
      hb_xfree( pIndex->pages );
      pIndex->pages = NULL;
      pIndex->ulPages = pIndex->ulPageLast = pIndex->ulPagesDepth = 0;
      pIndex->pFirst = pIndex->pLast = pIndex->pChanged = NULL;
   }
}

/*
 * trunc index file, left only space for header
 */
static void hb_nsxIndexTrunc( LPNSXINDEX pIndex )
{
   if( ! pIndex->lockWrite )
      hb_errInternal( 9102, "hb_nsxIndexTrunc on not locked index file.", NULL, NULL );

   hb_nsxFreePageBuffer( pIndex );
   pIndex->Update = pIndex->Changed = pIndex->fFlush = HB_TRUE;
   pIndex->Version = 0;
   pIndex->FileSize = 0;
   pIndex->NextAvail = NSX_DUMMYNODE;
   hb_fileTruncAt( pIndex->pFile, NSX_PAGELEN );
}

/*
 * try to find given tag page in the buffer
 */
static LPPAGEINFO hb_nsxPageFind( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO * pPagePtr = pTag->pIndex->pages;
   HB_ULONG u;

   for( u = pTag->pIndex->ulPages; u; u--, pPagePtr++ )
   {
      if( *pPagePtr && ( *pPagePtr )->Page == ulPage )
         return *pPagePtr;
   }
   return NULL;
}

/*
 * try to find free space in buffer
 */
static LPPAGEINFO hb_nsxPageGetBuffer( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPNSXINDEX pIndex = pTag->pIndex;
   LPPAGEINFO * pPagePtr;

   if( pIndex->ulPages < pIndex->ulPagesDepth )
   {
      pPagePtr = &pIndex->pages[ pIndex->ulPages++ ];
   }
   else if( pIndex->pFirst )
   {
      LPPAGEINFO pPage = pIndex->pFirst;

      if( pPage->iUsed )
         hb_errInternal( 9999, "hb_nsxPageGetBuffer: page used.", NULL, NULL );
      if( pPage->Changed )
         hb_errInternal( 9999, "hb_nsxPageGetBuffer: page changed.", NULL, NULL );

      pIndex->pFirst = pPage->pNext;
      if( pIndex->pFirst )
         pIndex->pFirst->pPrev = NULL;
      else
         pIndex->pLast = NULL;
      pPage->pPrev = NULL;
      pPage->Page = ulPage;
      pPage->iUsed = 1;

      return pPage;
   }
   else if( pIndex->ulPagesDepth == 0 )
   {
      pIndex->ulPages = 1;
      pIndex->ulPageLast = 0;
      pIndex->ulPagesDepth = NSX_PAGE_BUFFER;
      pIndex->pages = ( LPPAGEINFO * ) hb_xgrabz( sizeof( LPPAGEINFO ) * NSX_PAGE_BUFFER );
      pPagePtr = &pIndex->pages[ 0 ];
   }
   else
   {
      HB_ULONG ul = pIndex->ulPageLast;
      for( ;; )
      {
         if( ++ul >= pIndex->ulPagesDepth )
            ul = 0;
         pPagePtr = &pIndex->pages[ ul ];
         if( ! ( *pPagePtr )->iUsed && ! ( *pPagePtr )->Changed )
         {
            pIndex->ulPageLast = ul;
            break;
         }
         if( ul == pIndex->ulPageLast )
         {
            ul = pIndex->ulPagesDepth;
            pIndex->ulPagesDepth += NSX_PAGE_BUFFER >> 1;
            pIndex->pages = ( LPPAGEINFO * ) hb_xrealloc( pIndex->pages,
                                 sizeof( LPPAGEINFO ) * pIndex->ulPagesDepth );
            memset( pIndex->pages + ul, 0,
                         ( NSX_PAGE_BUFFER >> 1 ) * sizeof( LPPAGEINFO ) );
            pIndex->ulPages++;
            pPagePtr = &pIndex->pages[ ul ];
            pIndex->ulPageLast = 0;
            break;
         }
      }
   }

   if( ! *pPagePtr )
      *pPagePtr = ( LPPAGEINFO ) hb_xgrabz( sizeof( HB_PAGEINFO ) );
#ifdef HB_NSX_EXTERNAL_PAGEBUFFER
   if( ! hb_nsxPageBuffer( *pPagePtr ) )
      hb_nsxPageBuffer( *pPagePtr ) = ( HB_UCHAR * ) hb_xgrabz( NSX_PAGELEN );
#endif
   ( *pPagePtr )->pPrev = NULL;
   ( *pPagePtr )->Page  = ulPage;
   ( *pPagePtr )->iUsed = 1;
   return *pPagePtr;
}

/*
 * mark used page as free
 */
static void hb_nsxPageRelease( LPTAGINFO pTag, LPPAGEINFO pPage )
{
   LPNSXINDEX pIndex = pTag->pIndex;

   if( --pPage->iUsed == 0 )
   {
      if( pPage->Changed )
      {
         if( ! pPage->pPrev )
         {
            pPage->pPrev = pPage;
            pPage->pNext = pIndex->pChanged;
            pIndex->pChanged = pPage;
         }
      }
      else if( pIndex->pLast )
      {
         pIndex->pLast->pNext = pPage;
         pPage->pPrev = pIndex->pLast;
         pPage->pNext = NULL;
         pIndex->pLast = pPage;
      }
      else
      {
         pPage->pNext = pPage->pPrev = NULL;
         pIndex->pFirst = pIndex->pLast = pPage;
      }
   }
   else if( pPage->iUsed < 0 )
      hb_errInternal( 9999, "hb_nsxPageRelease: unused page freed.", NULL, NULL );

#ifdef HB_NSX_DEBUG_EXT
   if( hb_nsxPageType( pPage ) != 'f' )
      hb_nsxPageCheckKeys( pPage, pTag, 0, 11 );
#endif
}

/*
 * load page from index file or the buffer
 */
static LPPAGEINFO hb_nsxPageLoad( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage;

   if( ! ulPage )
   {
      hb_nsxCorruptError( pTag->pIndex );
      return NULL;
   }

   pPage = hb_nsxPageFind( pTag, ulPage );
   if( pPage )
   {
      if( ! pPage->Changed && ! pPage->iUsed )
      {
         if( pPage->pNext )
            pPage->pNext->pPrev = pPage->pPrev;
         else
            pTag->pIndex->pLast = pPage->pPrev;
         if( pPage->pPrev )
         {
            pPage->pPrev->pNext = pPage->pNext;
            pPage->pPrev = NULL;
         }
         else
            pTag->pIndex->pFirst = pPage->pNext;
      }
      pPage->iUsed++;
   }
   else
   {
      pPage = hb_nsxPageGetBuffer( pTag, ulPage );
      pPage->Changed = HB_FALSE;
      if( ! hb_nsxBlockRead( pTag->pIndex, ulPage,
                             hb_nsxPageBuffer( pPage ), NSX_PAGELEN ) )
      {
         hb_nsxPageRelease( pTag, pPage );
         return NULL;
      }
      if( hb_nsxPageType( pPage ) != 'f' )
      {
         pPage->uiKeys = hb_nsxGetKeyCount( pPage );
         pPage->uiOffset = hb_nsxIsLeaf( pPage ) ?
                           hb_nsxLeafGetFreeOffset( pPage ) : 0;
         if( hb_nsxPageType( pPage ) > ( NSX_ROOTPAGE | NSX_LEAFPAGE ) ||
             pPage->uiOffset > NSX_PAGELEN ||
             ( ! hb_nsxIsLeaf( pPage ) && pPage->uiKeys > pTag->MaxKeys ) )
         {
            hb_nsxPageRelease( pTag, pPage );
            hb_nsxCorruptError( pTag->pIndex );
            return NULL;
         }
#ifdef HB_NSX_DEBUG_EXT
         hb_nsxPageCheckKeys( pPage, pTag, 0, 21 );
#endif
      }
   }
   return pPage;
}

/*
 * initialize empty page structure
 */
static void hb_nsxPageInit( LPPAGEINFO pPage )
{
   memset( pPage->data.buffer, 0, NSX_PAGELEN );
   pPage->uiKeys = pPage->uiOffset = 0;
}

/*
 * free the index page for future reuse
 */
static void hb_nsxPageFree( LPTAGINFO pTag, LPPAGEINFO pPage )
{
   hb_nsxSetPageType( pPage, 'f' );
#ifdef HB_NSX_EXTERNAL_PAGEBUFFER
   HB_PUT_LE_UINT32( pPage->data.rootHeader->FreePage, pTag->pIndex->NextAvail );
#else
   HB_PUT_LE_UINT32( pPage->data.rootHeader.FreePage, pTag->pIndex->NextAvail );
#endif
   pTag->pIndex->NextAvail = pPage->Page;
   pTag->pIndex->Changed = pPage->Changed = HB_TRUE;
}

/*
 * add given page to list of free pages
 */
static void hb_nsxPageAddFree( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage = hb_nsxPageGetBuffer( pTag, ulPage );

   pPage->Changed = HB_TRUE;
   hb_nsxPageInit( pPage );
   hb_nsxPageFree( pTag, pPage );
   hb_nsxPageSave( pTag->pIndex, pPage );
   hb_nsxPageRelease( pTag, pPage );
}

/*
 * allocate new page address
 */
static HB_ULONG hb_nsxPageAlloc( LPNSXINDEX pIndex )
{
   HB_ULONG ulPage;

   if( ! pIndex->FileSize )
   {
      HB_FOFFSET fOffset;
      fOffset = hb_fileSize( pIndex->pFile );
      pIndex->FileSize = ( HB_ULONG )
                     ( fOffset >> ( pIndex->LargeFile ? NSX_PAGELEN_BITS : 0 ) );
   }
   ulPage = pIndex->FileSize;
   pIndex->FileSize += pIndex->LargeFile ? 1 : NSX_PAGELEN;
   return ulPage;
}

/*
 * allocate new page in index file - reuse freed one or increase file
 */
static LPPAGEINFO hb_nsxPageNew( LPTAGINFO pTag, HB_BOOL fNull )
{
   LPPAGEINFO pPage;

   if( pTag->pIndex->NextAvail && pTag->pIndex->NextAvail != NSX_DUMMYNODE )
   {
      pPage = hb_nsxPageLoad( pTag, pTag->pIndex->NextAvail );
      if( ! pPage )
         return NULL;
      else
      {
#ifdef HB_NSX_EXTERNAL_PAGEBUFFER
         pTag->pIndex->NextAvail = HB_GET_LE_UINT32( pPage->data.rootHeader->FreePage );
#else
         pTag->pIndex->NextAvail = HB_GET_LE_UINT32( pPage->data.rootHeader.FreePage );
#endif
      }
   }
   else
   {
      pPage = hb_nsxPageGetBuffer( pTag, fNull ? 0 : hb_nsxPageAlloc( pTag->pIndex ) );
   }
   hb_nsxPageInit( pPage );
   pTag->pIndex->Changed = pPage->Changed = HB_TRUE;

   return pPage;
}

/*
 * get free page in index file
 */
static HB_ULONG hb_nsxPageGetFree( LPTAGINFO pTag )
{
   LPPAGEINFO pPage = hb_nsxPageNew( pTag, HB_FALSE );
   HB_ULONG ulPage = 0;

   if( pPage )
   {
      ulPage = pPage->Page;
      pPage->Changed = HB_FALSE;
      hb_nsxPageRelease( pTag, pPage );
   }
   return ulPage;
}

/*
 * SIX3 compatible template index expression detection
 */
static HB_BOOL hb_nsxIsTemplateFunc( const char * szKeyExpr )
{
   return hb_strnicmp( szKeyExpr, "sxChar(", 7 ) == 0 ||
          hb_strnicmp( szKeyExpr, "sxDate(", 7 ) == 0 ||
          hb_strnicmp( szKeyExpr, "sxNum(", 6 ) == 0 ||
          hb_strnicmp( szKeyExpr, "sxLog(", 6 ) == 0;
}

/*
 * create the new tag structure
 */
static LPTAGINFO hb_nsxTagNew( LPNSXINDEX pIndex, const char * szTagName,
                               const char * szKeyExpr, PHB_ITEM pKeyExpr,
                               HB_UCHAR ucKeyType, HB_USHORT uiKeyLen, HB_BYTE bTrail,
                               const char * szForExpr, PHB_ITEM pForExpr,
                               HB_BOOL fAscendKey, HB_BOOL fUnique, HB_BOOL fCustom )
{
   LPTAGINFO pTag;

   pTag = ( LPTAGINFO ) hb_xgrabz( sizeof( TAGINFO ) );
   pTag->TagName = hb_strndup( szTagName, NSX_TAGNAME );
   pTag->pIndex = pIndex;
   if( szKeyExpr )
      pTag->KeyExpr = hb_strndup( szKeyExpr, NSX_MAXEXPLEN );

   if( pForExpr && szForExpr )
      pTag->ForExpr = hb_strndup( szForExpr, NSX_MAXEXPLEN );

   pTag->nField = hb_rddFieldExpIndex( &pIndex->pArea->dbfarea.area, pTag->KeyExpr );
   pTag->pKeyItem = pKeyExpr;
   pTag->pForItem = pForExpr;
   pTag->AscendKey = fAscendKey;
   pTag->fUsrDescend = ! pTag->AscendKey;
   pTag->UniqueKey = fUnique;
   pTag->Custom = fCustom;
   pTag->MultiKey = fCustom && DBFAREA_DATA( &pIndex->pArea->dbfarea )->fMultiKey;
   pTag->KeyType = ucKeyType;
   pTag->KeyLength = uiKeyLen;
   pTag->TrailChar = bTrail;

   pTag->MaxKeys = ( NSX_PAGELEN - 8 ) / ( uiKeyLen + 8 );
   pTag->CurKeyInfo = hb_nsxKeyNew( pTag->KeyLength );

   return pTag;
}

/*
 * free from memory tag structure
 */
static void hb_nsxTagFree( LPTAGINFO pTag )
{
   if( pTag == pTag->pIndex->pArea->lpCurTag )
      pTag->pIndex->pArea->lpCurTag = NULL;
   hb_xfree( pTag->TagName );
   if( pTag->KeyExpr )
      hb_xfree( pTag->KeyExpr );
   if( pTag->ForExpr )
      hb_xfree( pTag->ForExpr );
   if( pTag->pKeyItem )
      hb_vmDestroyBlockOrMacro( pTag->pKeyItem );
   if( pTag->pForItem )
      hb_vmDestroyBlockOrMacro( pTag->pForItem );
   if( pTag->HotKeyInfo )
      hb_nsxKeyFree( pTag->HotKeyInfo );
   hb_nsxKeyFree( pTag->CurKeyInfo );
   hb_nsxTagClearScope( pTag, 0 );
   hb_nsxTagClearScope( pTag, 1 );
   if( pTag->stack )
   {
      while( pTag->stackSize-- )
      {
         if( pTag->stack[ pTag->stackSize ].value )
            hb_xfree( pTag->stack[ pTag->stackSize ].value );
      }
      hb_xfree( pTag->stack );
   }
   hb_xfree( pTag );
}

/*
 * delete tag from compound index
 */
static void hb_nsxTagDelete( LPTAGINFO pTag )
{
   LPNSXINDEX pIndex = pTag->pIndex;
   int i;

   for( i = 0; i < pIndex->iTags; i++ )
   {
      if( pTag == pIndex->lpTags[ i ] )
      {
         while( ++i < pIndex->iTags )
            pIndex->lpTags[ i - 1 ] = pIndex->lpTags[ i ];
         if( --pIndex->iTags )
            pIndex->lpTags = ( LPTAGINFO * ) hb_xrealloc( pIndex->lpTags,
                                       sizeof( LPTAGINFO ) * pIndex->iTags );
         else
            hb_xfree( pIndex->lpTags );
         break;
      }
   }
   hb_nsxTagFree( pTag );
   pIndex->pArea->fSetTagNumbers = HB_TRUE;
}

/*
 * add tag to compound index
 */
static HB_ERRCODE hb_nsxTagAdd( LPNSXINDEX pIndex, LPTAGINFO pTag )
{
   if( pIndex->iTags >= NSX_MAXTAGS )
      return HB_FAILURE;

   if( pIndex->iTags )
      pIndex->lpTags = ( LPTAGINFO * ) hb_xrealloc( pIndex->lpTags,
                                 sizeof( LPTAGINFO ) * ( pIndex->iTags + 1 ) );
   else
      pIndex->lpTags = ( LPTAGINFO * ) hb_xgrab( sizeof( LPTAGINFO ) );

   pIndex->lpTags[ pIndex->iTags++ ] = pTag;
   pIndex->pArea->fSetTagNumbers = HB_TRUE;
   return HB_SUCCESS;
}

/*
 * create new tag and load it from index file
 */
static LPTAGINFO hb_nsxTagLoad( LPNSXINDEX pIndex, HB_ULONG ulBlock,
                                const char * szTagName, LPNSXTAGHEADER lpNSX )
{
   LPTAGINFO pTag;
   PHB_ITEM pKeyExp, pForExp = NULL;
   HB_USHORT uiUnique, uiDescend, uiKeySize;
   HB_UCHAR ucType, ucTrail;

   uiUnique = HB_GET_LE_UINT16( lpNSX->Unique );
   uiDescend = HB_GET_LE_UINT16( lpNSX->Descend );
   uiKeySize = HB_GET_LE_UINT16( lpNSX->KeySize );
   ucType = hb_nsxKeyType( HB_GET_LE_UINT16( lpNSX->KeyType ), &ucTrail );

   if( lpNSX->Signature[ 0 ] != NSX_SIGNATURE ||
       uiUnique > 1 || uiDescend > 1 || ucType == 'U' ||
       uiKeySize == 0 || uiKeySize > NSX_MAXKEYLEN || lpNSX->KeyExpr[ 0 ] < 0x20 )
      return NULL;

   if( SELF_COMPILE( &pIndex->pArea->dbfarea.area, ( const char * ) lpNSX->KeyExpr ) == HB_FAILURE )
      return NULL;
   pKeyExp = pIndex->pArea->dbfarea.area.valResult;
   pIndex->pArea->dbfarea.area.valResult = NULL;

   if( lpNSX->ForExpr[ 0 ] >= 0x20 )
   {
      if( SELF_COMPILE( &pIndex->pArea->dbfarea.area, ( const char * ) lpNSX->ForExpr ) == HB_FAILURE )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         return NULL;
      }
      pForExp = pIndex->pArea->dbfarea.area.valResult;
      pIndex->pArea->dbfarea.area.valResult = NULL;
   }
   pTag = hb_nsxTagNew( pIndex, szTagName,
                        ( const char * ) lpNSX->KeyExpr, pKeyExp,
                        ucType, uiKeySize, ucTrail,
                        ( const char * ) lpNSX->ForExpr, pForExp,
                        uiDescend == 0, uiUnique != 0,
                        ( lpNSX->TagFlags[ 0 ] & NSX_TAG_NOUPDATE ) != 0 );

   pTag->TagFlags = lpNSX->TagFlags[ 0 ];
   hb_nsxTagUpdateFlags( pTag );
   pTag->HeadBlock = ulBlock;
   pTag->RootBlock = HB_GET_LE_UINT32( lpNSX->RootPage );

   return pTag;
}

/*
 * add tag into NSX header
 */
static void hb_nsxIndexTagAdd( LPNSXINDEX pIndex, LPTAGINFO pTag )
{
   int iTags = HB_GET_LE_UINT16( pIndex->HeaderBuff.TagCount ), i;
   LPNSXTAGITEM pTagItem = pIndex->HeaderBuff.TagList;

   for( i = 0; i < iTags; pTagItem++, i++ )
   {
      if( ! hb_strnicmp( ( const char * ) pTagItem->TagName, pTag->TagName, NSX_TAGNAME ) )
         break;
   }
   if( i == iTags )
   {
      ++iTags;
      HB_PUT_LE_UINT16( pIndex->HeaderBuff.TagCount, iTags );
      hb_strncpy( ( char * ) pTagItem->TagName, pTag->TagName, NSX_TAGNAME );
   }
   HB_PUT_LE_UINT32( pTagItem->TagOffset, pTag->HeadBlock );
   pIndex->Update = HB_TRUE;
}

/*
 * delete tag from NSX header
 */
static void hb_nsxIndexTagDel( LPNSXINDEX pIndex, const char * szTagName )
{
   int iTags = HB_GET_LE_UINT16( pIndex->HeaderBuff.TagCount ), i;
   LPNSXTAGITEM pTagItem = pIndex->HeaderBuff.TagList;

   for( i = 0; i < iTags; pTagItem++, i++ )
   {
      if( ! hb_strnicmp( ( const char * ) pTagItem->TagName, szTagName, NSX_TAGNAME ) )
      {
         memmove( pTagItem, pTagItem + 1, ( iTags - i ) * sizeof( NSXTAGITEM ) );
         memset( pTagItem + iTags - 1, 0, sizeof( NSXTAGITEM ) );
         --iTags;
         HB_PUT_LE_UINT16( pIndex->HeaderBuff.TagCount, iTags );
         pIndex->Update = HB_TRUE;
         break;
      }
   }
}

/*
 * find tag header block in NSX header
 */
static HB_ULONG hb_nsxIndexTagFind( LPNSXROOTHEADER lpNSX, const char * szTagName )
{
   int iTags = HB_GET_LE_UINT16( lpNSX->TagCount ), i;
   LPNSXTAGITEM pTagItem = lpNSX->TagList;

   for( i = 0; i < iTags; pTagItem++, i++ )
   {
      if( ! hb_strnicmp( ( const char * ) pTagItem->TagName, szTagName, NSX_TAGNAME ) )
         return HB_GET_LE_UINT32( pTagItem->TagOffset );
   }
   return NSX_DUMMYNODE;
}

/*
 * Write tag header
 */
static HB_ERRCODE hb_nsxTagHeaderSave( LPTAGINFO pTag )
{
   LPNSXINDEX pIndex = pTag->pIndex;
   NSXTAGHEADER Header;
   int iSize = NSX_TAGHEAD_HEADSIZE;

   if( ! pTag->HeadBlock )
   {
      pTag->HeadBlock = hb_nsxPageGetFree( pTag );
      if( ! pTag->HeadBlock )
         return HB_FAILURE;
      hb_nsxIndexTagAdd( pIndex, pTag );
   }

   Header.Signature[ 0 ] = NSX_SIGNATURE;
   Header.TagFlags[ 0 ]  = ( pTag->Partial  ? NSX_TAG_PARTIAL  : 0 ) |
                           ( pTag->Template ? NSX_TAG_TEMPLATE : 0 ) |
                           ( pTag->ChgOnly  ? NSX_TAG_CHGONLY  : 0 ) |
                           ( pTag->Custom   ? NSX_TAG_NOUPDATE : 0 ) |
                           ( pTag->MultiKey ? NSX_TAG_MULTIKEY : 0 );
   HB_PUT_LE_UINT32( Header.RootPage, pTag->RootBlock );

   if( pIndex->Update )
   {
      HB_USHORT type = hb_nsxKeyTypeRaw( pTag->KeyType );
      int iLen;

      memset( ( HB_BYTE * ) &Header + NSX_TAGHEAD_HEADSIZE, 0,
              sizeof( Header ) - NSX_TAGHEAD_HEADSIZE );

      HB_PUT_LE_UINT16( Header.KeyType, type );
      HB_PUT_LE_UINT16( Header.KeySize,  pTag->KeyLength );
      Header.Unique[ 0 ]  = pTag->UniqueKey ? 1 : 0;
      Header.Descend[ 0 ] = pTag->AscendKey ? 0 : 1;

      iLen = ( int ) strlen( pTag->KeyExpr );
      if( iLen > NSX_MAXEXPLEN )
         iLen = NSX_MAXEXPLEN;
      memcpy( Header.KeyExpr, pTag->KeyExpr, iLen );
      if( pTag->ForExpr )
      {
         iLen = ( int ) strlen( pTag->ForExpr );
         if( iLen > NSX_MAXEXPLEN )
            iLen = NSX_MAXEXPLEN;
         memcpy( Header.ForExpr, pTag->ForExpr, iLen );
      }
      iSize = sizeof( Header );
   }

   if( ! hb_nsxBlockWrite( pIndex, pTag->HeadBlock, &Header, iSize ) )
      return HB_FAILURE;

   pTag->HdrChanged = HB_FALSE;
   pIndex->Changed = pIndex->fFlush = HB_TRUE;

   return HB_SUCCESS;
}

/*
 * create new index structure
 */
static LPNSXINDEX hb_nsxIndexNew( NSXAREAP pArea )
{
   LPNSXINDEX pIndex;

   pIndex = ( LPNSXINDEX ) hb_xgrabz( sizeof( NSXINDEX ) );

   pIndex->pFile = NULL;
   pIndex->pArea = pArea;
   return pIndex;
}

/*
 * close the index file and free from memory index and tag structures
 */
static void hb_nsxIndexFree( LPNSXINDEX pIndex )
{
   hb_nsxFreePageBuffer( pIndex );
   if( pIndex->iTags )
   {
      int i;
      for( i = 0; i < pIndex->iTags; i++ )
         hb_nsxTagFree( pIndex->lpTags[ i ] );
      hb_xfree( pIndex->lpTags );
   }
   if( pIndex->pFile )
   {
      hb_fileClose( pIndex->pFile );
      if( pIndex->fDelete )
      {
         hb_fileDelete( pIndex->RealName ? pIndex->RealName : pIndex->IndexName );
      }
   }
   if( pIndex->IndexName )
      hb_xfree( pIndex->IndexName );
   if( pIndex->RealName )
      hb_xfree( pIndex->RealName );
   pIndex->pArea->fSetTagNumbers = HB_TRUE;
   hb_xfree( pIndex );
}

/*
 * Write tag header
 */
static HB_ERRCODE hb_nsxIndexHeaderSave( LPNSXINDEX pIndex )
{
   int iSize = pIndex->Update ? NSX_PAGELEN : NSX_ROOTHEAD_HEADSIZE;

   pIndex->Version++;
   pIndex->Version &= 0xFFFF;
   pIndex->HeaderBuff.Signature[ 0 ]  = pIndex->LargeFile ?
                                        NSX_SIGNATURE_LARGE : NSX_SIGNATURE;
   pIndex->HeaderBuff.IndexFlags[ 0 ] = 0;
   HB_PUT_LE_UINT16( pIndex->HeaderBuff.TagCount, pIndex->iTags );
   HB_PUT_LE_UINT16( pIndex->HeaderBuff.Version,  pIndex->Version );
   HB_PUT_LE_UINT32( pIndex->HeaderBuff.FreePage, pIndex->NextAvail );
   HB_PUT_LE_UINT32( pIndex->HeaderBuff.FileSize, pIndex->FileSize );

   if( ! hb_nsxBlockWrite( pIndex, 0, &pIndex->HeaderBuff, iSize ) )
      return HB_FAILURE;

   pIndex->Changed = pIndex->Update = HB_FALSE;

   return HB_SUCCESS;
}

/*
 * load new tags from index file
 */
static HB_ERRCODE hb_nsxIndexLoad( LPNSXINDEX pIndex )
{
   HB_BYTE signature;

   if( ! pIndex->fValidHeader )
   {
      if( ! hb_nsxBlockRead( pIndex, 0, &pIndex->HeaderBuff, NSX_PAGELEN ) )
         return HB_FAILURE;
      pIndex->fValidHeader = HB_TRUE;
   }

   signature = pIndex->HeaderBuff.Signature[ 0 ];
   if( ( signature != NSX_SIGNATURE && signature != NSX_SIGNATURE_LARGE ) ||
       pIndex->HeaderBuff.IndexFlags[ 0 ] != 0 )
   {
      hb_nsxCorruptError( pIndex );
      return HB_FAILURE;
   }
   else
   {
      NSXTAGHEADER tagbuffer;
      int iTags = HB_GET_LE_UINT16( pIndex->HeaderBuff.TagCount );
      LPNSXTAGITEM pTagItem = pIndex->HeaderBuff.TagList;

      if( iTags > NSX_MAXTAGS )
         return HB_FAILURE;

      pIndex->LargeFile = signature == NSX_SIGNATURE_LARGE;
      pIndex->Version   = HB_GET_LE_UINT16( pIndex->HeaderBuff.Version );
      pIndex->NextAvail = HB_GET_LE_UINT32( pIndex->HeaderBuff.FreePage );
      pIndex->FileSize  = HB_GET_LE_UINT32( pIndex->HeaderBuff.FileSize );

      for( pIndex->iTags = 0; pIndex->iTags < iTags; pTagItem++ )
      {
         HB_ULONG ulBlock = HB_GET_LE_UINT32( pTagItem->TagOffset );
         LPTAGINFO pTag;
         if( ulBlock == 0 || pTagItem->TagName[ 0 ] <= 0x20 )
            return HB_FAILURE;
         if( ! hb_nsxBlockRead( pIndex, ulBlock,
                                &tagbuffer, sizeof( NSXTAGHEADER ) ) )
            return HB_FAILURE;
         pTag = hb_nsxTagLoad( pIndex, ulBlock, ( const char * ) pTagItem->TagName, &tagbuffer );
         if( ! pTag )
            return HB_FAILURE;
         hb_nsxTagAdd( pIndex, pTag );
      }
   }

   return HB_SUCCESS;
}

/*
 * read index header and check for concurrent access
 */
static HB_ERRCODE hb_nsxIndexHeaderRead( LPNSXINDEX pIndex )
{
   if( ! hb_nsxBlockRead( pIndex, 0, &pIndex->HeaderBuff, NSX_PAGELEN ) )
      return HB_FAILURE;

   if( ( pIndex->FileSize ? pIndex->HeaderBuff.Signature[ 0 ] !=
            ( HB_UCHAR ) ( pIndex->LargeFile ? NSX_SIGNATURE_LARGE : NSX_SIGNATURE ) :
         ( pIndex->HeaderBuff.Signature[ 0 ] != NSX_SIGNATURE &&
           pIndex->HeaderBuff.Signature[ 0 ] != NSX_SIGNATURE_LARGE ) ) ||
       pIndex->HeaderBuff.IndexFlags[ 0 ] != 0 )
   {
      hb_nsxCorruptError( pIndex );
      return HB_FAILURE;
   }
   else
   {
      HB_ULONG ulVersion, ulNext, ulFileSize;

      ulVersion = HB_GET_LE_UINT16( pIndex->HeaderBuff.Version );
      ulNext = HB_GET_LE_UINT32( pIndex->HeaderBuff.FreePage );
      ulFileSize = HB_GET_LE_UINT32( pIndex->HeaderBuff.FileSize );

      if( pIndex->Version != ulVersion || pIndex->NextAvail != ulNext ||
          pIndex->FileSize != ulFileSize )
      {
         int i;
         hb_nsxDiscardBuffers( pIndex );
         pIndex->Version = ulVersion;
         pIndex->NextAvail = ulNext;
         pIndex->FileSize = ulFileSize;
         for( i = 0; i < pIndex->iTags; i++ )
         {
            pIndex->lpTags[ i ]->HeadBlock =
               hb_nsxIndexTagFind( &pIndex->HeaderBuff, pIndex->lpTags[ i ]->TagName );
            if( ! pIndex->lpTags[ i ]->HeadBlock )
               pIndex->lpTags[ i ]->RootBlock = 0;
         }
      }
   }

   return HB_SUCCESS;
}

/*
 * write modified pages to index file
 */
static void hb_nsxIndexFlush( LPNSXINDEX pIndex )
{
   int i;

   while( pIndex->pChanged )
   {
      LPPAGEINFO pPage = pIndex->pChanged;
      pIndex->pChanged = pPage->pNext;
      if( pPage->Changed )
      {
         hb_nsxPageSave( pIndex, pPage );
         ++pPage->iUsed;
         hb_nsxPageRelease( pIndex->lpTags[ 0 ], pPage );
      }
      else
         hb_errInternal( 9999, "hb_nsxIndexFlush: unchaged page in the list.", NULL, NULL );
   }

   for( i = 0; i < pIndex->iTags; i++ )
      if( pIndex->lpTags[ i ]->HdrChanged )
         hb_nsxTagHeaderSave( pIndex->lpTags[ i ] );
   if( pIndex->Changed )
      hb_nsxIndexHeaderSave( pIndex );
}

/*
 * lock index for reading (shared lock)
 */
static HB_BOOL hb_nsxIndexLockRead( LPNSXINDEX pIndex )
{
   HB_BOOL fOK;

   if( pIndex->lockRead > 0 || pIndex->lockWrite > 0 || ! pIndex->fShared ||
       HB_DIRTYREAD( &pIndex->pArea->dbfarea ) )
   {
      fOK = HB_TRUE;
      pIndex->lockRead++;
   }
   else
   {
      fOK = hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_LOCK | FLX_SHARED | FLX_WAIT, HB_FALSE,
                               &pIndex->lockData );
      /* if fOK then check VERSION field in NSXHEADER and
       * if it has been changed then discard all page buffers
       */
      if( fOK )
      {
         pIndex->lockRead++;
         if( hb_nsxIndexHeaderRead( pIndex ) != HB_SUCCESS )
         {
            pIndex->lockRead--;
            hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_UNLOCK, HB_FALSE, &pIndex->lockData );
            return HB_FALSE;
         }
      }
   }
   if( ! fOK )
      hb_nsxErrorRT( pIndex->pArea, EG_LOCK, EDBF_LOCK, pIndex->IndexName, hb_fsError(), 0, NULL );

   return fOK;
}

/*
 * lock index for writing (exclusive lock)
 */
static HB_BOOL hb_nsxIndexLockWrite( LPNSXINDEX pIndex, HB_BOOL fCheck )
{
   HB_BOOL fOK;

   if( pIndex->fReadonly )
      hb_errInternal( 9101, "hb_nsxIndexLockWrite: readonly index.", NULL, NULL );

   if( pIndex->lockRead )
      hb_errInternal( 9105, "hb_nsxIndexLockWrite: writeLock after readLock.", NULL, NULL );

   if( pIndex->lockWrite > 0 || ! pIndex->fShared )
   {
      fOK = HB_TRUE;
      pIndex->lockWrite++;
   }
   else
   {
      fOK = hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_LOCK | FLX_EXCLUSIVE | FLX_WAIT, HB_FALSE,
                               &pIndex->lockData );
      /* if fOK then check VERSION field in NSXHEADER and
       * if it has been changed then discard all page buffers
       */
      if( fOK )
      {
         pIndex->lockWrite++;
         if( fCheck && hb_nsxIndexHeaderRead( pIndex ) != HB_SUCCESS )
         {
            pIndex->lockWrite--;
            hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_UNLOCK, HB_FALSE, &pIndex->lockData );
            return HB_FALSE;
         }
      }
   }
   if( ! fOK )
      hb_nsxErrorRT( pIndex->pArea, EG_LOCK, EDBF_LOCK, pIndex->IndexName, hb_fsError(), 0, NULL );

   return fOK;
}

/*
 * remove index read lock (shared lock)
 */
static HB_BOOL hb_nsxIndexUnLockRead( LPNSXINDEX pIndex )
{
   HB_BOOL fOK;

#ifdef HB_NSX_DEBUG
   int i;
   for( i = 0; i < pIndex->iTags; i++ )
      hb_nsxTagCheckBuffers( pIndex->lpTags[ i ] );
#endif

   pIndex->lockRead--;
   if( pIndex->lockRead < 0 )
      hb_errInternal( 9106, "hb_nsxIndexUnLockRead: bad count of locks.", NULL, NULL );

   if( pIndex->lockRead || pIndex->lockWrite || ! pIndex->fShared ||
       HB_DIRTYREAD( &pIndex->pArea->dbfarea ) )
   {
      fOK = HB_TRUE;
   }
   else
   {
      pIndex->fValidHeader = HB_FALSE;
      fOK = hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_UNLOCK, HB_FALSE, &pIndex->lockData );
   }
   if( ! fOK )
      hb_errInternal( 9108, "hb_nsxIndexUnLockRead: unlock error.", NULL, NULL );

   return fOK;
}

/*
 * remove index write lock (exclusive lock)
 */
static HB_BOOL hb_nsxIndexUnLockWrite( LPNSXINDEX pIndex )
{
   HB_BOOL fOK;

#ifdef HB_NSX_DEBUG
   int i;
   for( i = 0; i < pIndex->iTags; i++ )
      hb_nsxTagCheckBuffers( pIndex->lpTags[ i ] );
#endif

   if( pIndex->lockWrite <= 0 )
      hb_errInternal( 9106, "hb_nsxIndexUnLockWrite: bad count of locks.", NULL, NULL );
   if( pIndex->lockRead )
      hb_errInternal( 9105, "hb_nsxIndexUnLockWrite: writeUnLock before readUnLock.", NULL, NULL );

   hb_nsxIndexFlush( pIndex );
   pIndex->lockWrite--;

   if( pIndex->lockWrite || ! pIndex->fShared )
   {
      fOK = HB_TRUE;
   }
   else
   {
      hb_fileFlush( pIndex->pFile, HB_TRUE );
      pIndex->fValidHeader = HB_FALSE;
      fOK = hb_dbfLockIdxFile( &pIndex->pArea->dbfarea, pIndex->pFile,
                               FL_UNLOCK, HB_FALSE, &pIndex->lockData );
   }
   if( ! fOK )
      hb_errInternal( 9108, "hb_nsxIndexUnLockWrite: unlock error.", NULL, NULL );

   return fOK;
}

/*
 * lock tag for reading (shared lock)
 */
static HB_BOOL hb_nsxTagLockRead( LPTAGINFO pTag )
{
   HB_BOOL fOK = HB_FALSE;

   if( hb_nsxIndexLockRead( pTag->pIndex ) )
   {
      fOK = hb_nsxTagHeaderCheck( pTag );
      if( ! fOK )
      {
         hb_nsxIndexUnLockRead( pTag->pIndex );
         hb_nsxCorruptError( pTag->pIndex );
      }
   }
   return fOK;
}

/*
 * lock tag for writing (exclusive lock)
 */
static HB_BOOL hb_nsxTagLockWrite( LPTAGINFO pTag )
{
   HB_BOOL fOK = HB_FALSE;

   if( hb_nsxIndexLockWrite( pTag->pIndex, HB_TRUE ) )
   {
      fOK = hb_nsxTagHeaderCheck( pTag );
      if( ! fOK )
      {
         hb_nsxIndexUnLockWrite( pTag->pIndex );
         hb_nsxCorruptError( pTag->pIndex );
      }
   }
   return fOK;
}

/*
 * remove tag read lock (shared lock)
 */
static HB_BOOL hb_nsxTagUnLockRead( LPTAGINFO pTag )
{
   return hb_nsxIndexUnLockRead( pTag->pIndex );
}

/*
 * remove tag write lock (exclusive lock)
 */
static HB_BOOL hb_nsxTagUnLockWrite( LPTAGINFO pTag )
{
   return hb_nsxIndexUnLockWrite( pTag->pIndex );
}

/*
 * retrieve previous key value from upper branch page
 */
static HB_BOOL hb_nsxTagGetPrevKey( LPTAGINFO pTag, HB_UCHAR * pKeyVal, int iLevel )
{
   while( --iLevel >= 0 )
   {
      if( pTag->stack[ iLevel ].ikey != 0 )
      {
         memcpy( pKeyVal, pTag->stack[ iLevel ].value, pTag->KeyLength );
         return HB_TRUE;
      }
   }
   #if 0
   memset( pKeyVal, pTag->TrailChar, pTag->KeyLength );
   #endif
   return HB_FALSE;
}

/*
 * decode key from leaf page into given buffer
 */
static HB_BOOL hb_nsxPageGetLeafKey( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiKey,
                                     HB_UCHAR * pKeyVal, HB_ULONG * pulRecNo )
{
   HB_USHORT uiOffset = NSX_LEAFKEYOFFSET;

   hb_nsxTagGetPrevKey( pTag, pKeyVal, pTag->stackLevel - 1 );
   do
   {
      uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset,
                                   pKeyVal, pulRecNo );
      if( uiOffset == 0 )
      {
         hb_nsxCorruptError( pTag->pIndex );
         *pulRecNo = 0;
         return HB_FALSE;
      }
   }
   while( uiKey-- );

   return HB_TRUE;
}

/*
 * retrieve key from page
 */
static HB_BOOL hb_nsxTagGetCurKey( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiKey )
{
   if( hb_nsxIsLeaf( pPage ) )
   {
      if( uiKey >= pPage->uiKeys )
      {
         pTag->CurKeyInfo->rec = pTag->CurKeyInfo->page = 0;
      }
      else
      {
         if( pTag->CurKeyInfo->rec == 0 ||
             pTag->CurKeyInfo->page != pPage->Page ||
             uiKey < pTag->CurKeyNo || pTag->CurKeyOffset == 0 )
         {
            pTag->CurKeyOffset = NSX_LEAFKEYOFFSET;
            pTag->CurKeyNo = ( HB_USHORT ) -1;
            hb_nsxTagGetPrevKey( pTag, pTag->CurKeyInfo->val, pTag->stackLevel - 1 );
         }
         pTag->CurKeyInfo->page = pPage->Page;

         while( pTag->CurKeyNo != uiKey )
         {
            pTag->CurKeyOffset = hb_nsxLeafGetKey( pTag, pPage,
                                                   pTag->CurKeyOffset,
                                                   pTag->CurKeyInfo->val,
                                                   &pTag->CurKeyInfo->rec );
            if( pTag->CurKeyOffset == 0 )
            {
               hb_nsxCorruptError( pTag->pIndex );
               pTag->CurKeyInfo->rec = 0;
               return HB_FALSE;
            }
            pTag->CurKeyNo++;
         }
      }
   }
   else if( uiKey && uiKey <= pPage->uiKeys )
   {
      --uiKey;
      memcpy( pTag->CurKeyInfo->val,
              hb_nsxGetKeyVal( pPage, pTag->KeyLength, uiKey ), pTag->KeyLength );
      pTag->CurKeyInfo->rec = hb_nsxGetKeyRec( pPage, pTag->KeyLength, uiKey );
      pTag->CurKeyInfo->page = pPage->Page;
   }
   else
      pTag->CurKeyInfo->rec = pTag->CurKeyInfo->page = 0;

   return HB_TRUE;
}

/*
 * set next page and key in page path
 */
static void hb_nsxTagSetPageStack( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiKey )
{

   if( pTag->stackLevel == pTag->stackSize )
   {
      if( pTag->stackSize == 0 )
      {
         pTag->stackSize = NSX_STACKSIZE;
         pTag->stack = ( LPTREESTACK ) hb_xgrabz( sizeof( TREE_STACK ) * NSX_STACKSIZE );
      }
      else
      {
         pTag->stack = ( LPTREESTACK ) hb_xrealloc( pTag->stack,
                  sizeof( TREE_STACK ) * ( pTag->stackSize + NSX_STACKSIZE ) );
         memset( pTag->stack + sizeof( TREE_STACK ) * pTag->stackSize, 0,
                 sizeof( TREE_STACK ) * NSX_STACKSIZE );
         pTag->stackSize += NSX_STACKSIZE;
      }
   }

   if( ! hb_nsxIsLeaf( pPage ) && uiKey )
   {
      if( ! pTag->stack[ pTag->stackLevel ].value )
         pTag->stack[ pTag->stackLevel ].value = ( HB_UCHAR * ) hb_xgrab( pTag->KeyLength );
      memcpy( pTag->stack[ pTag->stackLevel ].value,
              hb_nsxGetKeyVal( pPage, pTag->KeyLength, uiKey - 1 ),
              pTag->KeyLength );
   }
   pTag->stack[ pTag->stackLevel ].page = pPage->Page;
   pTag->stack[ pTag->stackLevel++ ].ikey = uiKey;
}

/*
 * go down from the given index page to the first key
 */
static LPPAGEINFO hb_nsxPageTopMove( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage;

   for( ;; )
   {
      pPage = hb_nsxPageLoad( pTag, ulPage );
      if( ! pPage )
         return NULL;
      hb_nsxTagSetPageStack( pTag, pPage, 0 );
      if( hb_nsxIsLeaf( pPage ) )
      {
         if( pPage->uiKeys == 0 && pTag->stackLevel > 1 )
         {
            hb_nsxPageRelease( pTag, pPage );
            hb_nsxCorruptError( pTag->pIndex );
            return NULL;
         }
         break;
      }
      ulPage = hb_nsxGetLowerPage( pPage );
      hb_nsxPageRelease( pTag, pPage );
   }

   return pPage;
}

/*
 * go down from the given index page to the last key
 */
static LPPAGEINFO hb_nsxPageBottomMove( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage;

   for( ;; )
   {
      pPage = hb_nsxPageLoad( pTag, ulPage );
      if( ! pPage )
         return NULL;
      if( hb_nsxIsLeaf( pPage ) )
      {
         hb_nsxTagSetPageStack( pTag, pPage, ( pPage->uiKeys > 0 ? pPage->uiKeys - 1 : 0 ) );
         if( pPage->uiKeys == 0 && pTag->stackLevel > 1 && ! pTag->pIndex->pArea->pSort )
         {
            hb_nsxPageRelease( pTag, pPage );
            hb_nsxCorruptError( pTag->pIndex );
            return NULL;
         }
         break;
      }
      hb_nsxTagSetPageStack( pTag, pPage, pPage->uiKeys );
      ulPage = pPage->uiKeys ? hb_nsxGetKeyPage( pPage, pTag->KeyLength, pPage->uiKeys - 1 ) :
                               hb_nsxGetLowerPage( pPage );
      hb_nsxPageRelease( pTag, pPage );
   }

   return pPage;
}

/*
 * set page path to the first key in tag
 */
static HB_BOOL hb_nsxTagTopKey( LPTAGINFO pTag )
{
   LPPAGEINFO pPage;
   HB_BOOL fFound;

   pTag->stackLevel = 0;
   pPage = hb_nsxPageTopMove( pTag, hb_nsxTagRootBlock( pTag ) );
   if( ! pPage )
      return HB_FALSE;
   fFound = hb_nsxTagGetCurKey( pTag, pPage, 0 ) && pPage->uiKeys != 0;
   hb_nsxPageRelease( pTag, pPage );
   return fFound;
}

/*
 * set page path to the last key in tag
 */
static HB_BOOL hb_nsxTagBottomKey( LPTAGINFO pTag )
{
   LPPAGEINFO pPage;
   HB_BOOL fFound;

   pTag->stackLevel = 0;
   pPage = hb_nsxPageBottomMove( pTag, hb_nsxTagRootBlock( pTag ) );
   if( ! pPage )
      return HB_FALSE;
   fFound = hb_nsxTagGetCurKey( pTag, pPage,
                                pTag->stack[ pTag->stackLevel - 1 ].ikey ) &&
            pPage->uiKeys != 0;
   hb_nsxPageRelease( pTag, pPage );
   return fFound;
}

/*
 * update page path to the next key in tag
 */
static HB_BOOL hb_nsxTagNextKey( LPTAGINFO pTag )
{
   int iLevel = pTag->stackLevel - 1;

   if( iLevel >= 0 )
   {
      LPPAGEINFO pPage;
      HB_ULONG ulPage;
      HB_BOOL fFound;

      pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
      if( ! pPage )
         return HB_FALSE;
      if( ! hb_nsxIsLeaf( pPage ) )
      {
         ulPage = pTag->stack[ iLevel ].ikey == 0 ? 0 :
                  hb_nsxGetKeyPage( pPage, pTag->KeyLength, pTag->stack[ iLevel ].ikey - 1 );
         hb_nsxPageRelease( pTag, pPage );
         pPage = hb_nsxPageTopMove( pTag, ulPage );
         if( ! pPage )
            return HB_FALSE;
      }
      else if( pTag->stack[ iLevel ].ikey + 1 < pPage->uiKeys )
         pTag->stack[ iLevel ].ikey++;
      else
      {
         for( ;; )
         {
            hb_nsxPageRelease( pTag, pPage );
            if( --iLevel < 0 )
               return HB_FALSE;
            pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
            if( ! pPage )
               return HB_FALSE;
            if( pPage->uiKeys && pTag->stack[ iLevel ].ikey < ( HB_SHORT ) pPage->uiKeys )
            {
               if( ! pTag->stack[ iLevel ].value )
                  pTag->stack[ iLevel ].value = ( HB_UCHAR * ) hb_xgrab( pTag->KeyLength );
               memcpy( pTag->stack[ iLevel ].value,
                       hb_nsxGetKeyVal( pPage, pTag->KeyLength,
                                        pTag->stack[ iLevel ].ikey ),
                       pTag->KeyLength );
               pTag->stack[ iLevel ].ikey++;
               break;
            }
         }
         pTag->stackLevel = ( HB_USHORTCAST ) iLevel + 1;
      }
      fFound = hb_nsxTagGetCurKey( pTag, pPage,
                                   pTag->stack[ pTag->stackLevel - 1 ].ikey );
      hb_nsxPageRelease( pTag, pPage );
      return fFound;
   }
   return HB_FALSE;
}

/*
 * update page path to the previous key in tag
 */
static HB_BOOL hb_nsxTagPrevKey( LPTAGINFO pTag )
{
   int iLevel = pTag->stackLevel - 1;

   if( iLevel >= 0 )
   {
      LPPAGEINFO pPage;
      HB_ULONG ulPage;
      HB_BOOL fFound;

      pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
      if( ! pPage )
         return HB_FALSE;

      if( ! hb_nsxIsLeaf( pPage ) )
      {
         ulPage = pTag->stack[ iLevel ].ikey == 0 ? 0 :
                  pTag->stack[ iLevel ].ikey == 1 ? hb_nsxGetLowerPage( pPage ) :
                  hb_nsxGetKeyPage( pPage, pTag->KeyLength, pTag->stack[ iLevel ].ikey - 2 );
         hb_nsxPageRelease( pTag, pPage );
         if( --pTag->stack[ iLevel ].ikey )
         {
            if( ! pTag->stack[ iLevel ].value )
               pTag->stack[ iLevel ].value = ( HB_UCHAR * ) hb_xgrab( pTag->KeyLength );
            memcpy( pTag->stack[ iLevel ].value,
                    hb_nsxGetKeyVal( pPage, pTag->KeyLength,
                                     pTag->stack[ iLevel ].ikey - 1 ),
                    pTag->KeyLength );
         }
         pPage = hb_nsxPageBottomMove( pTag, ulPage );
         if( ! pPage )
            return HB_FALSE;
      }
      else if( pTag->stack[ iLevel ].ikey )
         pTag->stack[ iLevel ].ikey--;
      else
      {
         for( ;; )
         {
            hb_nsxPageRelease( pTag, pPage );
            if( --iLevel < 0 )
               return HB_FALSE;
            pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
            if( ! pPage )
               return HB_FALSE;
            if( pTag->stack[ iLevel ].ikey )
               break;
         }
         pTag->stackLevel = ( HB_USHORTCAST ) iLevel + 1;
      }

      fFound = hb_nsxTagGetCurKey( pTag, pPage,
                                   pTag->stack[ pTag->stackLevel - 1 ].ikey );
      hb_nsxPageRelease( pTag, pPage );
      return fFound;
   }
   return HB_FALSE;
}

/*
 * find a key value in page
 */
static int hb_nsxPageKeyFind( LPTAGINFO pTag, LPPAGEINFO pPage,
                              HB_UCHAR * key, HB_SHORT keylen, int mode,
                              HB_BOOL fLast, HB_ULONG ulRecNo, HB_BOOL * fStop )
{
   int iBegin, iEnd, iLast, k, i;
   HB_ULONG ulRec;

   *fStop = HB_FALSE;

   if( pPage->uiKeys == 0 )
      return 0;
   else if( hb_nsxIsLeaf( pPage ) )
   {
      HB_USHORT uiOffset = NSX_LEAFKEYOFFSET, u;
      HB_UCHAR pKeyVal[ NSX_MAXKEYLEN ];

      hb_nsxTagGetPrevKey( pTag, pKeyVal, pTag->stackLevel );
      for( u = 0; u < pPage->uiKeys; ++u )
      {
         uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset,
                                      pKeyVal, &ulRec );
         if( uiOffset == 0 )
         {
            hb_nsxCorruptError( pTag->pIndex );
            break;
         }
         k = hb_nsxValCompare( pTag, key, keylen,
                               pKeyVal, pTag->KeyLength, mode );
         if( k == 0 )
         {
            if( ulRecNo != 0 )
            {
               if( ulRecNo < ulRec )
                  k = -1;
               else if( ulRecNo > ulRec )
                  k = 1;
               else
               {
                  *fStop = HB_TRUE;
                  return u;
               }
            }
         }

         if( k < 0 )
            break;
         else if( k == 0 && ! fLast )
         {
            *fStop = HB_TRUE;
            break;
         }
      }
      return u;
   }
   else
   {
      iBegin = 0;
      iLast = pPage->uiKeys;
      iEnd = iLast - 1;

      while( iBegin <= iEnd )
      {
         i = ( iBegin + iEnd ) >> 1;
         k = hb_nsxValCompare( pTag, key, keylen,
                               hb_nsxGetKeyVal( pPage, pTag->KeyLength, i ),
                               pTag->KeyLength, mode );
         if( k == 0 )
         {
            if( ulRecNo != 0 )
            {
               ulRec = hb_nsxGetKeyRec( pPage, pTag->KeyLength, i );
               if( ulRecNo < ulRec )
                  k = -1;
               else if( ulRecNo > ulRec )
                  k = 1;
               else
               {
                  *fStop = HB_TRUE;
                  return i + 1;
               }
            }
         }

         if( fLast ? k >= 0 : k > 0 )
            iBegin = i + 1;
         else
         {
            if( k == 0 )
               *fStop = HB_TRUE;
            iLast = i;
            iEnd = i - 1;
         }
      }
      return iLast;
   }
}

/*
 * set page path to given key in tag
 */
static HB_BOOL hb_nsxTagKeyFind( LPTAGINFO pTag, LPKEYINFO pKey, HB_USHORT uiLen )
{
   LPPAGEINFO pPage;
   HB_ULONG ulPage, ulRecNo = 0;
   int iKey;
   HB_BOOL fStop = HB_FALSE, fLast = HB_FALSE, fOut;

   if( pKey->rec == NSX_MAX_REC_NUM )         /* for seek last */
      fLast = HB_TRUE;
   else if( pKey->rec != NSX_IGNORE_REC_NUM ) /* for key del, add and current key */
      ulRecNo = pKey->rec;
   /* else -> normal seek */

   pTag->stackLevel = 0;

   ulPage = hb_nsxTagRootBlock( pTag );
   if( ! ulPage )
   {
      pTag->TagBOF = pTag->TagEOF = HB_TRUE;
      pTag->CurKeyInfo->rec = 0;
      return HB_FALSE;
   }

   for( ;; )
   {
      pPage = hb_nsxPageLoad( pTag, ulPage );
      if( ! pPage )
         return HB_FALSE;

      iKey = hb_nsxPageKeyFind( pTag, pPage, pKey->val, uiLen, pKey->mode,
                                fLast, ulRecNo, &fStop );
      hb_nsxTagSetPageStack( pTag, pPage, ( HB_USHORTCAST ) iKey );
      if( ( fStop && ulRecNo ) || hb_nsxIsLeaf( pPage ) )
         break;

      ulPage = iKey == 0 ? hb_nsxGetLowerPage( pPage ) :
                           hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 );
      hb_nsxPageRelease( pTag, pPage );
   }

   fOut = ! hb_nsxTagGetCurKey( pTag, pPage, ( HB_USHORTCAST ) iKey );
   hb_nsxPageRelease( pTag, pPage );
   if( fOut )
      return HB_FALSE;

   if( ulRecNo == 0 )
   {
      if( fLast )
      {
         if( ! hb_nsxTagPrevKey( pTag ) )
         {
            fOut = HB_TRUE;
            fStop = HB_FALSE;
         }
         else
         {
            fStop = hb_nsxValCompare( pTag, pKey->val, uiLen, pTag->CurKeyInfo->val,
                                      pTag->KeyLength, pKey->mode ) == 0;
         }
      }
      else if( ! fStop && pTag->CurKeyInfo->rec == 0 )
      {
         if( ! hb_nsxTagNextKey( pTag ) )  /* Tag EOF */
         {
            fOut = HB_TRUE;
            fStop = HB_FALSE;
         }
         else
         {
            fStop = hb_nsxValCompare( pTag, pKey->val, uiLen,
                                      pTag->CurKeyInfo->val, pTag->KeyLength,
                                      pKey->mode ) == 0;
         }
      }
   }

   pTag->TagBOF = pTag->TagEOF = fOut || pTag->CurKeyInfo->rec == 0;

   return fStop;
}


/* ************************************************************************* */
/* tag update functions */
/* ************************************************************************* */

/*
 * add key to branch page
 */
static void hb_nsxPageKeyAdd( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiPos,
                              LPKEYINFO pKey )
{
   HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, uiPos );

   if( uiPos < pPage->uiKeys )
   {
      memmove( hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, uiPos + 1 ), ptr,
               hb_nsxBranchKeySize( pPage, pTag->KeyLength ) *
               ( pPage->uiKeys - uiPos ) );
   }

   hb_nsxBranchKeySetPage( ptr, pKey->page );
   hb_nsxBranchKeySetRec( ptr, pKey->rec );
   memcpy( hb_nsxBranchKeyVal( ptr ), pKey->val, pTag->KeyLength );
   pPage->Changed = HB_TRUE;
   pPage->uiKeys++;
}

/*
 * split single branch page into two and return key to the new one
 */
static LPKEYINFO hb_nsxPageSplit( LPTAGINFO pTag, LPPAGEINFO pPage,
                                  LPKEYINFO pKey, HB_USHORT uiPos )
{
   LPPAGEINFO pNewPage;
   HB_USHORT n, uiKeys, uiHalf, uiLen;

   pNewPage = hb_nsxPageNew( pTag, HB_FALSE );
   if( ! pNewPage )
   {
      hb_nsxKeyFree( pKey );
      return NULL;
   }
   hb_nsxSetKeyRecSize( pNewPage, 4 );

   uiLen = hb_nsxBranchKeySize( pPage, pTag->KeyLength );
   uiKeys = pPage->uiKeys;
   uiHalf = ( uiKeys + 1 ) >> 1;

   if( uiHalf < uiPos )
      uiHalf++;

   if( uiHalf < uiPos )
   {
      n = uiPos - uiHalf;
      if( n )
         memcpy( hb_nsxGetBranchKeyPtr( pNewPage, pTag->KeyLength, 0 ),
                 hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, uiHalf ),
                 uiLen * n );
      pNewPage->uiKeys = n;
      hb_nsxPageKeyAdd( pTag, pNewPage, n, pKey );
      n = uiKeys - uiPos;
      if( n )
      {
         memcpy( hb_nsxGetBranchKeyPtr( pNewPage, pTag->KeyLength, pNewPage->uiKeys ),
                 hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, uiPos ),
                 uiLen * n );
         pNewPage->uiKeys += n;
      }
      pPage->uiKeys = uiHalf;
   }
   else
   {
      memcpy( hb_nsxGetBranchKeyPtr( pNewPage, pTag->KeyLength, 0 ),
              hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, uiHalf ),
              uiLen * ( uiKeys - uiHalf ) );
      pNewPage->uiKeys = uiKeys - uiHalf;
      pPage->uiKeys = uiHalf;
      if( uiPos != uiHalf )
         hb_nsxPageKeyAdd( pTag, pPage, uiPos, pKey );
   }

   if( uiPos != uiHalf )
   {
      HB_UCHAR * ptr = hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, pPage->uiKeys - 1 );
      pKey->page = hb_nsxBranchKeyPage( ptr );
      pKey->rec = hb_nsxBranchKeyRec( ptr );
      memcpy( pKey->val, hb_nsxBranchKeyVal( ptr ), pTag->KeyLength );
      pPage->uiKeys--;
   }
   hb_nsxSetLowerPage( pNewPage, pKey->page );
   pKey->page = pNewPage->Page;

   pPage->Changed = pNewPage->Changed = HB_TRUE;
#ifdef HB_NSX_DEBUG
   hb_nsxPageCheckKeys( pNewPage, pTag, uiPos, 1 );
   hb_nsxPageCheckKeys( pPage, pTag, uiPos - pNewPage->uiKeys, 2 );
#endif
   hb_nsxPageRelease( pTag, pNewPage );

   return pKey;
}


/*
 * insert key into current stack page
 */
static HB_BOOL hb_nsxTagInsertKey( LPTAGINFO pTag, LPPAGEINFO pPage,
                                   LPKEYINFO pKey, HB_UCHAR * pKeyPrev )
{
   HB_USHORT uiOffset = NSX_LEAFKEYOFFSET, uiKeyOffset = 0,
             uiKey = pTag->stack[ pTag->stackLevel - 1 ].ikey,
             uiKeys = pPage->uiKeys, uiHalfOffset = 0, uiHalfKeys = 0, u;
   int iLen = pTag->KeyLength;
   HB_ULONG ulRecNo;
   HB_UCHAR pKeyVal[ NSX_MAXKEYLEN ], * pKeyBuff, * ptr, ucRecSize;
   LPKEYINFO pNewKey = NULL;

#ifdef HB_NSX_DEBUG_EXT
   hb_nsxPageCheckKeys( pPage, pTag, uiKey, 41 );
#endif

   ptr = pKeyBuff = ( HB_UCHAR * ) hb_xgrab( ( uiKeys + 1 ) * ( iLen + 4 ) );
   if( pKeyPrev )
      memcpy( pKeyVal, pKeyPrev, iLen );
   else
      hb_nsxTagGetPrevKey( pTag, pKeyVal, pTag->stackLevel - 1 );

   for( u = 0; u <= uiKeys; ++u )
   {
      if( u == uiKey )
      {
         uiKeyOffset = uiOffset;
         HB_PUT_LE_UINT32( ptr, pKey->rec );
         ptr += 4;
         memcpy( ptr, pKey->val, iLen );
         ptr += iLen;
      }
      else
      {
         if( uiHalfOffset == 0 && u < uiKey && uiOffset >= NSX_LEAFSPLITOFFSET )
         {
            uiHalfOffset = uiOffset;
            uiHalfKeys = u;
         }
         uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset, pKeyVal, &ulRecNo );
         if( uiOffset == 0 )
         {
            hb_xfree( pKeyBuff );
            pTag->stackLevel = 0;
            hb_nsxPageRelease( pTag, pPage );
            hb_nsxCorruptError( pTag->pIndex );
            return HB_FALSE;
         }
         HB_PUT_LE_UINT32( ptr, ulRecNo );
         ptr += 4;
         memcpy( ptr, pKeyVal, iLen );
         ptr += iLen;
      }
   }
   pPage->uiKeys++;
   ucRecSize = hb_nsxGetKeyRecSize( pPage );
   if( hb_nsxGetRecSize( pKey->rec ) > ucRecSize )
   {
      ucRecSize = hb_nsxGetRecSize( pKey->rec );
      hb_nsxSetKeyRecSize( pPage, ucRecSize );
      uiKeyOffset = NSX_LEAFKEYOFFSET;
      uiKey = uiHalfOffset = uiHalfKeys = 0;
   }
   pPage->uiOffset = uiKeyOffset;
   ptr = pKeyBuff + uiKey * ( iLen + 4 );
   if( uiKey == 0 )
   {
      if( hb_nsxTagGetPrevKey( pTag, pKeyVal, pTag->stackLevel - 1 ) )
         pKeyPrev = pKeyVal;
      else
         pKeyPrev = NULL;
   }
   else
      pKeyPrev = ptr - iLen;

   pPage->Changed = HB_TRUE;
   for(; uiKey <= uiKeys; ++uiKey )
   {
      if( uiHalfOffset == 0 && pPage->uiOffset >= NSX_LEAFSPLITOFFSET )
      {
         uiHalfOffset = pPage->uiOffset;
         uiHalfKeys = uiKey;
      }
      uiOffset = hb_nsxLeafPutKey( pTag, pPage, pPage->uiOffset, pKeyPrev,
                                   ptr + 4, HB_GET_LE_UINT32( ptr ) );
      if( uiOffset == 0 )
      {
         if( pNewKey )
            hb_errInternal( 9999, "hb_nsxTagInsertKey: multiple leaf page split", NULL, NULL );
         else if( uiHalfOffset == 0 )
            hb_errInternal( 9999, "hb_nsxTagInsertKey: split offset not set", NULL, NULL );

         pPage->uiOffset = uiHalfOffset;
         uiKey = pPage->uiKeys = uiHalfKeys;
         ptr = pKeyBuff + uiKey * ( iLen + 4 );
         hb_nsxPageType( pPage ) &= ~NSX_ROOTPAGE;
#ifdef HB_NSX_DEBUG
         hb_nsxPageCheckKeys( pPage, pTag, uiKey, 41 );
#endif
         hb_nsxPageRelease( pTag, pPage );
         pPage = hb_nsxPageNew( pTag, HB_FALSE );
         if( ! pPage )
         {
            hb_xfree( pKeyBuff );
            pTag->CurKeyOffset = 0;
            pTag->stackLevel = 0;
            return HB_FALSE;
         }
         pNewKey = hb_nsxKeyNew( iLen );
         pNewKey->page = pPage->Page;
         pNewKey->rec = HB_GET_LE_UINT32( ptr );
         memcpy( pNewKey->val, ptr + 4, iLen );
         hb_nsxSetPageType( pPage, NSX_LEAFPAGE );
         hb_nsxSetKeyRecSize( pPage, ucRecSize );
         pPage->uiKeys = uiKeys - uiKey;
         pPage->Changed = HB_TRUE;
         uiOffset = NSX_LEAFKEYOFFSET;
      }
      pPage->uiOffset = uiOffset;
      pKeyPrev = ptr + 4;
      ptr += iLen + 4;
   }
#ifdef HB_NSX_DEBUG
   hb_nsxPageCheckKeys( pPage, pTag, uiKey, 42 );
#endif
   hb_xfree( pKeyBuff );

   if( pNewKey )
   {
      int iLevel = pTag->stackLevel - 1;

      while( --iLevel >= 0 && pNewKey )
      {
         int iKey;

         hb_nsxPageRelease( pTag, pPage );
         pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
         if( ! pPage )
         {
            hb_nsxKeyFree( pNewKey );
            pTag->CurKeyOffset = 0;
            pTag->stackLevel = 0;
            return HB_FALSE;
         }
         iKey = pTag->stack[ iLevel ].ikey;
         if( pPage->uiKeys < pTag->MaxKeys )
         {
            hb_nsxPageKeyAdd( pTag, pPage, ( HB_USHORTCAST ) iKey, pNewKey );
            hb_nsxKeyFree( pNewKey );
            pNewKey = NULL;
         }
         else
         {
            pNewKey = hb_nsxPageSplit( pTag, pPage, pNewKey, ( HB_USHORTCAST ) iKey );
         }
      }

      if( pNewKey )
      {
         if( hb_nsxIsRoot( pPage ) )
         {
            hb_nsxPageType( pPage ) &= ~NSX_ROOTPAGE;
            pPage->Changed = HB_TRUE;
         }
         hb_nsxPageRelease( pTag, pPage );
         pPage = hb_nsxPageNew( pTag, HB_FALSE );
         if( ! pPage )
         {
            hb_nsxKeyFree( pNewKey );
            pTag->CurKeyOffset = 0;
            pTag->stackLevel = 0;
            return HB_FALSE;
         }
         hb_nsxSetPageType( pPage, NSX_ROOTPAGE );
         hb_nsxSetKeyRecSize( pPage, 4 );
         hb_nsxSetLowerPage( pPage, pTag->RootBlock );
         hb_nsxPageKeyAdd( pTag, pPage, 0, pNewKey );
         pTag->RootBlock = pPage->Page;
         pTag->HdrChanged = HB_TRUE;
         pTag->CurKeyOffset = 0;
         pTag->stackLevel = 0;
         hb_nsxKeyFree( pNewKey );
      }
   }
   hb_nsxPageRelease( pTag, pPage );
   pTag->CurKeyOffset = 0;
   pTag->stackLevel = 0;
   return HB_TRUE;
}

/*
 * add key to the index at the current page path
 */
static HB_BOOL hb_nsxTagKeyAdd( LPTAGINFO pTag, LPKEYINFO pKey )
{
   LPPAGEINFO pPage;
   HB_BOOL fFound, fBottom = HB_FALSE;

   if( pTag->UniqueKey )
   {
      HB_ULONG ulRecNo = pKey->rec;

      pKey->rec = NSX_IGNORE_REC_NUM;
      fFound = hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength );
      pKey->rec = ulRecNo;
      if( fFound )
         return HB_FALSE;
      fBottom = HB_TRUE;
   }
   else
   {
      pKey->page = NSX_MAX_REC_NUM;
      fFound = hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength );
      pKey->page = 0;
      if( fFound )
      {
         if( pTag->MultiKey )
            fBottom = HB_TRUE;
         else
            return HB_FALSE;
      }
   }

   if( pTag->stackLevel == 0 )
      return HB_FALSE;
   pPage = hb_nsxPageLoad( pTag, pTag->stack[ pTag->stackLevel - 1 ].page );
   if( ! pPage )
      return HB_FALSE;
   if( fBottom && ! hb_nsxIsLeaf( pPage ) )
   {
      HB_ULONG ulPage;

      ulPage = pTag->stack[ pTag->stackLevel - 1 ].ikey == 0 ?
               hb_nsxGetLowerPage( pPage ) :
               hb_nsxGetKeyPage( pPage, pTag->KeyLength, pTag->stack[ pTag->stackLevel - 1 ].ikey - 1 );
      hb_nsxPageRelease( pTag, pPage );
      pPage = hb_nsxPageBottomMove( pTag, ulPage );
      if( ! pPage )
         return HB_FALSE;
      if( pTag->stack[ pTag->stackLevel - 1 ].ikey < ( HB_SHORT ) pPage->uiKeys )
         pTag->stack[ pTag->stackLevel - 1 ].ikey++;
   }

   return hb_nsxTagInsertKey( pTag, pPage, pKey, NULL );
}

/*
 * del key from the leaf page
 */
static void hb_nsxPageLeafKeyDel( LPTAGINFO pTag, LPPAGEINFO pPage, HB_USHORT uiKey )
{
   HB_UCHAR pKeyVal[ NSX_MAXKEYLEN ], pKeyVal2[ NSX_MAXKEYLEN ];
   HB_ULONG ulRecNo;
   HB_USHORT uiOffset = NSX_LEAFKEYOFFSET, u;
   HB_BOOL fPrev;

   fPrev = hb_nsxTagGetPrevKey( pTag, pKeyVal, pTag->stackLevel - 1 );
   for( u = 0; u < uiKey; ++u )
   {
      uiOffset = hb_nsxLeafGetKey( pTag, pPage, uiOffset, pKeyVal, &ulRecNo );
      if( uiOffset == 0 )
      {
         hb_nsxCorruptError( pTag->pIndex );
         return;
      }
   }
   if( --pPage->uiKeys > uiKey )
   {
      HB_UCHAR * pPrevVal = NULL;

      /* save previous key value */
      if( fPrev || uiKey )
      {
         memcpy( pKeyVal2, pKeyVal, pTag->KeyLength );
         pPrevVal = pKeyVal2;
      }

      /* get deleted key value */
      u = hb_nsxLeafGetKey( pTag, pPage, uiOffset, pKeyVal, &ulRecNo );
      if( u != 0 )
         /* get next key value */
         u = hb_nsxLeafGetKey( pTag, pPage, u, pKeyVal, &ulRecNo );
      if( u == 0 )
      {
         hb_nsxCorruptError( pTag->pIndex );
         return;
      }
      /* store next key encoded with previous key value */
      uiOffset = hb_nsxLeafPutKey( pTag, pPage, uiOffset,
                                   pPrevVal, pKeyVal, ulRecNo );
      /* move other keys */
      memmove( hb_nsxPageBuffer( pPage ) + uiOffset,
               hb_nsxPageBuffer( pPage ) + u, pPage->uiOffset - u );
      uiOffset += pPage->uiOffset - u;
   }
   pPage->uiOffset = uiOffset;
   pPage->Changed = HB_TRUE;
}

/*
 * del key at the current page path from the index
 */
static HB_BOOL hb_nsxTagKeyDel( LPTAGINFO pTag, LPKEYINFO pKey )
{
   int iLevel, iKey;
   LPPAGEINFO pPage;
   HB_ULONG ulPage, ulRecNo;
   HB_BOOL fResult = HB_TRUE;

   pKey->page = 0;
   if( pTag->stackLevel == 0 || pTag->CurKeyInfo->rec != pKey->rec ||
       memcmp( pTag->CurKeyInfo->val, pKey->val, pTag->KeyLength ) != 0 )
   {
      if( ! hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength ) )
         return HB_FALSE;
   }

   iLevel = pTag->stackLevel - 1;
   iKey = pTag->stack[ iLevel ].ikey;
   pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
   if( ! pPage )
      return HB_FALSE;

   if( ! hb_nsxIsLeaf( pPage ) )
   {
      int iBaseLevel, iBaseKey;
      LPPAGEINFO pBasePage;

      pBasePage = pPage;
      iBaseKey = iKey - 1; /* iBaseKey >= 0 */
      iBaseLevel = iLevel;
      ulPage = hb_nsxGetKeyPage( pBasePage, pTag->KeyLength, iBaseKey );
      pPage = hb_nsxPageTopMove( pTag, ulPage );
      if( ! pPage )
      {
         hb_nsxPageRelease( pTag, pBasePage );
         return HB_FALSE;
      }
      iLevel = pTag->stackLevel - 1;
      iKey = pTag->stack[ iLevel ].ikey;  /* iKey = 0 */
      if( ! hb_nsxPageGetLeafKey( pTag, pPage, ( HB_USHORTCAST ) iKey,
                                  hb_nsxGetKeyVal( pBasePage, pTag->KeyLength, iBaseKey ),
                                  &ulRecNo ) )
      {
         hb_nsxPageRelease( pTag, pBasePage );
         hb_nsxPageRelease( pTag, pPage );
         pTag->stackLevel = 0;
         return HB_FALSE;
      }
      hb_nsxSetKeyRec( pBasePage, pTag->KeyLength, iBaseKey, ulRecNo );
      if( ! pTag->stack[ iBaseLevel ].value )
         pTag->stack[ iBaseLevel ].value = ( HB_UCHAR * ) hb_xgrab( pTag->KeyLength );
      memcpy( pTag->stack[ iBaseLevel ].value,
              hb_nsxGetKeyVal( pBasePage, pTag->KeyLength, iBaseKey ),
              pTag->KeyLength );
      pBasePage->Changed = HB_TRUE;
#ifdef HB_NSX_DEBUG
      hb_nsxPageCheckKeys( pBasePage, pTag, iBaseKey, 61 );
#endif
      hb_nsxPageRelease( pTag, pBasePage );
   }

   if( pPage->uiKeys > 1 )
   {
      hb_nsxPageLeafKeyDel( pTag, pPage, ( HB_USHORTCAST ) iKey );
#ifdef HB_NSX_DEBUG
      hb_nsxPageCheckKeys( pPage, pTag, iKey, 62 );
#endif
      hb_nsxPageRelease( pTag, pPage );
   }
   else
   {
      while( --iLevel >= 0 )
      {
         hb_nsxPageFree( pTag, pPage );
         hb_nsxPageRelease( pTag, pPage );
         pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
         if( pPage->uiKeys )
            break;
      }
      if( iLevel < 0 )
      {
         hb_nsxSetPageType( pPage, NSX_ROOTPAGE | NSX_LEAFPAGE );
         hb_nsxSetKeyRecSize( pPage, hb_nsxGetRecSize( 0 ) );
         pPage->uiOffset = NSX_LEAFKEYOFFSET;
         pPage->uiKeys = 0;
         pPage->Changed = HB_TRUE;
         hb_nsxPageRelease( pTag, pPage );
      }
      else
      {
         LPKEYINFO pKeyNew = hb_nsxKeyNew( pTag->KeyLength );
         HB_UCHAR * pKeyPtr, * pKeyPrev = NULL;

         iKey = pTag->stack[ iLevel ].ikey;
         if( iKey == 0 )
         {
            ulPage = hb_nsxGetKeyPage( pPage, pTag->KeyLength, 0 );
            hb_nsxSetLowerPage( pPage, ulPage );
            pKeyPrev = pKeyNew->val;
         }
         else
         {
            ulPage = --iKey == 0 ? hb_nsxGetLowerPage( pPage ) :
                     hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 );
         }
         pKeyPtr = hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, iKey );
         pKeyNew->rec = hb_nsxBranchKeyRec( pKeyPtr );
         memcpy( pKeyNew->val, hb_nsxBranchKeyVal( pKeyPtr ), pTag->KeyLength );
         if( --pPage->uiKeys > iKey )
         {
            memmove( pKeyPtr,
                     hb_nsxGetBranchKeyPtr( pPage, pTag->KeyLength, iKey + 1 ),
                     hb_nsxBranchKeySize( pPage, pTag->KeyLength ) *
                     ( pPage->uiKeys - iKey ) );
         }
         pPage->Changed = HB_TRUE;
         pTag->stackLevel = ( HB_USHORTCAST ) iLevel;
         hb_nsxTagSetPageStack( pTag, pPage, ( HB_USHORTCAST ) iKey );
         hb_nsxPageRelease( pTag, pPage );
         if( pKeyPrev )
         {
            pPage = hb_nsxPageTopMove( pTag, ulPage );
         }
         else
         {
            pPage = hb_nsxPageBottomMove( pTag, ulPage );
            if( pPage )
               pTag->stack[ pTag->stackLevel - 1 ].ikey++;
         }
         fResult = pPage && hb_nsxTagInsertKey( pTag, pPage, pKeyNew, pKeyPrev );
         hb_nsxKeyFree( pKeyNew );
      }
   }
   pTag->CurKeyOffset = 0;
   pTag->stackLevel = 0;
   return fResult;
}

/* ************************************************************************* */
/* end of tag update functions */
/* ************************************************************************* */


/*
 * Skip in tag respecting record filter only
 */
static void hb_nsxTagSkipFilter( LPTAGINFO pTag, HB_BOOL fForward )
{
   HB_BOOL fBack, fEof = fForward ? pTag->TagEOF : pTag->TagBOF;

   fBack = pTag->fUsrDescend ? fForward : ! fForward;

   while( ! fEof && ! hb_nsxCheckRecordScope( pTag->pIndex->pArea,
                                              pTag->CurKeyInfo->rec ) )
   {
      if( fBack )
         fEof = ! hb_nsxTagPrevKey( pTag );
      else
         fEof = ! hb_nsxTagNextKey( pTag );

      if( ! fEof && ! hb_nsxKeyInScope( pTag, pTag->CurKeyInfo ) )
      {
         fEof = HB_TRUE;
      }
   }
   if( fEof )
   {
      if( fForward )
         pTag->TagEOF = HB_TRUE;
      else
         pTag->TagBOF = HB_TRUE;
   }
}

/*
 * go to the first visible record in Tag
 */
static void hb_nsxTagGoTop( LPTAGINFO pTag )
{
   PHB_NSXSCOPE pScope = pTag->fUsrDescend ? &pTag->bottom : &pTag->top;

   if( pScope->scopeKeyLen )
      hb_nsxTagKeyFind( pTag, pScope->scopeKey, pScope->scopeKeyLen );
   else if( pTag->fUsrDescend )
      hb_nsxTagBottomKey( pTag );
   else
      hb_nsxTagTopKey( pTag );

   pTag->TagEOF = pTag->CurKeyInfo->rec == 0 ||
                  ! hb_nsxKeyInScope( pTag, pTag->CurKeyInfo );

   if( ! pTag->TagEOF && pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      hb_nsxTagSkipFilter( pTag, HB_TRUE );

   pTag->TagBOF = pTag->TagEOF;
}

/*
 * go to the last visible record in Tag
 */
static void hb_nsxTagGoBottom( LPTAGINFO pTag )
{
   PHB_NSXSCOPE pScope = pTag->fUsrDescend ? &pTag->top : &pTag->bottom;

   if( pScope->scopeKeyLen )
      hb_nsxTagKeyFind( pTag, pScope->scopeKey, pScope->scopeKeyLen );
   else if( pTag->fUsrDescend )
      hb_nsxTagTopKey( pTag );
   else
      hb_nsxTagBottomKey( pTag );

   pTag->TagBOF = pTag->CurKeyInfo->rec == 0 ||
                  ! hb_nsxKeyInScope( pTag, pTag->CurKeyInfo );

   if( ! pTag->TagBOF && pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      hb_nsxTagSkipFilter( pTag, HB_FALSE );

   pTag->TagEOF = pTag->TagBOF;
}

/*
 * skip to Next Key in the Tag
 */
static void hb_nsxTagSkipNext( LPTAGINFO pTag )
{
   pTag->TagBOF = HB_FALSE;

   if( pTag->stackLevel == 0 )
      pTag->TagEOF = HB_TRUE;
   else if( ! hb_nsxInTopScope( pTag, pTag->CurKeyInfo->val ) )
      hb_nsxTagGoTop( pTag );
   else if( pTag->fUsrDescend )
      pTag->TagEOF = ! hb_nsxTagPrevKey( pTag );
   else
      pTag->TagEOF = ! hb_nsxTagNextKey( pTag );

   if( ! pTag->TagEOF && ! hb_nsxKeyInScope( pTag, pTag->CurKeyInfo ) )
      pTag->TagEOF = HB_TRUE;

   if( ! pTag->TagEOF && pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      hb_nsxTagSkipFilter( pTag, HB_TRUE );
}

/*
 * skip to Previous Key in the Tag
 */
static void hb_nsxTagSkipPrev( LPTAGINFO pTag )
{
   pTag->TagEOF = HB_FALSE;

   if( pTag->stackLevel == 0 )
      hb_nsxTagGoBottom( pTag );
   else if( pTag->fUsrDescend )
      pTag->TagBOF = ! hb_nsxTagNextKey( pTag );
   else
      pTag->TagBOF = ! hb_nsxTagPrevKey( pTag );

   if( ! pTag->TagBOF && ! hb_nsxKeyInScope( pTag, pTag->CurKeyInfo ) )
      pTag->TagBOF = HB_TRUE;

   if( ! pTag->TagBOF && pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      hb_nsxTagSkipFilter( pTag, HB_FALSE );
}

/*
 * count keys in the given page and all subpages
 */
static HB_ULONG hb_nsxPageCountKeys( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage = hb_nsxPageLoad( pTag, ulPage );
   HB_ULONG ulKeys;

   if( ! pPage )
      return 0;

   ulKeys = pPage->uiKeys;
   if( ! hb_nsxIsLeaf( pPage ) )
   {
      HB_USHORT u;
      ulKeys += hb_nsxPageCountKeys( pTag, hb_nsxGetLowerPage( pPage ) );
      for( u = 0; u < pPage->uiKeys; u++ )
      {
         ulKeys += hb_nsxPageCountKeys( pTag,
                              hb_nsxGetKeyPage( pPage, pTag->KeyLength, u ) );
      }
   }
   hb_nsxPageRelease( pTag, pPage );

   return ulKeys;
}

/*
 * count relative position of current location in page stack
 */
static double hb_nsxTagCountRelKeyPos( LPTAGINFO pTag )
{
   int iLevel = pTag->stackLevel;
   double dPos = 1.0;

   while( --iLevel >= 0 )
   {
      LPPAGEINFO pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
      int iKeys, iKey;
      if( ! pPage )
         break;
      iKey = pTag->stack[ iLevel ].ikey;
      iKeys = pPage->uiKeys;
      if( ! hb_nsxIsLeaf( pPage ) )
      {
         if( iKey && iLevel == pTag->stackLevel - 1 )
            --iKey;
         ++iKeys;
      }
      else
         dPos = 0.5;
      if( iKeys )
         dPos = ( dPos + iKey ) / iKeys;
      hb_nsxPageRelease( pTag, pPage );
   }
   if( pTag->fUsrDescend )
      dPos = 1.0 - dPos;
   return dPos;
}

static void hb_nsxTagGoToRelKeyPos( LPTAGINFO pTag, double dPos )
{
   LPPAGEINFO pPage;
   HB_ULONG ulPage;
   int iKey, iKeys;

   if( pTag->fUsrDescend )
      dPos = 1.0 - dPos;

   pTag->stackLevel = 0;

   ulPage = hb_nsxTagRootBlock( pTag );
   if( ! ulPage )
      return;

   for( ;; )
   {
      pPage = hb_nsxPageLoad( pTag, ulPage );
      if( ! pPage )
      {
         pTag->stackLevel = 0;
         return;
      }
      iKeys = pPage->uiKeys;
      if( iKeys == 0 )
         iKey = 0;
      else
      {
         if( ! hb_nsxIsLeaf( pPage ) )
            ++iKeys;
         iKey = ( int ) ( dPos * iKeys );
         if( iKey >= iKeys )
            iKey = iKeys - 1;
         dPos = dPos * iKeys - iKey;
         if( dPos <= 0.0 )
            dPos = 0.0;
         else if( dPos >= 1.0 )
            dPos = 1.0;
      }
      hb_nsxTagSetPageStack( pTag, pPage, ( HB_USHORTCAST ) iKey );
      if( hb_nsxIsLeaf( pPage ) )
         break;
      ulPage = iKey == 0 ? hb_nsxGetLowerPage( pPage ) :
                           hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 );
      hb_nsxPageRelease( pTag, pPage );
   }

   hb_nsxTagGetCurKey( pTag, pPage, ( HB_USHORTCAST ) iKey );
   hb_nsxPageRelease( pTag, pPage );

   /* reposition for branch keys */
   if( dPos > 0.75 )
      hb_nsxTagNextKey( pTag );
   else if( dPos < 0.25 )
      hb_nsxTagPrevKey( pTag );
}

/*
 * refresh CurKey value and set proper path from RootPage to LeafPage
 */
static HB_BOOL hb_nsxCurKeyRefresh( LPTAGINFO pTag )
{
   NSXAREAP pArea = pTag->pIndex->pArea;

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( ! pArea->dbfarea.fPositioned )
   {
      pTag->stackLevel = 0;
      pTag->TagBOF = pTag->TagEOF = HB_TRUE;
      pTag->CurKeyInfo->rec = 0;
      return HB_FALSE;
   }
   else if( pTag->stackLevel == 0 || pTag->CurKeyInfo->rec != pArea->dbfarea.ulRecNo )
   {
      HB_BOOL fValidBuf = pArea->dbfarea.fValidBuffer;
      HB_BYTE buf[ NSX_MAXKEYLEN ];
      HB_BOOL fBuf = HB_FALSE;
      LPKEYINFO pKey = NULL;
      /* Try to find previous if it's key for the same record */
      if( pTag->CurKeyInfo->rec == pArea->dbfarea.ulRecNo )
      {
         fBuf = HB_TRUE;
         memcpy( buf, pTag->CurKeyInfo->val, pTag->KeyLength );
         pKey = hb_nsxKeyCopy( pKey, pTag->CurKeyInfo, pTag->KeyLength );
         hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength );
      }
      if( pTag->CurKeyInfo->rec != pArea->dbfarea.ulRecNo )
      {
         /* not found, create new key from DBF and if differs seek again */
         pKey = hb_nsxEvalKey( pKey, pTag );
         if( ! fBuf || memcmp( buf, pKey->val, pTag->KeyLength ) != 0 )
            hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength );
         /* not found, if key was generated from DBF buffer then force to
          * update it, create the new key and if differs seek again */
         if( pTag->CurKeyInfo->rec != pArea->dbfarea.ulRecNo && fValidBuf )
         {
            SELF_GOTO( &pArea->dbfarea.area, pArea->dbfarea.ulRecNo );
            memcpy( buf, pKey->val, pTag->KeyLength );
            pKey = hb_nsxEvalKey( pKey, pTag );
            if( memcmp( buf, pKey->val, pTag->KeyLength ) != 0 )
               hb_nsxTagKeyFind( pTag, pKey, pTag->KeyLength );
         }
         if( pTag->CurKeyInfo->rec != pArea->dbfarea.ulRecNo && pTag->Template )
         {
            hb_nsxTagGoTop( pTag );
            while( ! pTag->TagEOF )
            {
               if( pTag->CurKeyInfo->rec == pArea->dbfarea.ulRecNo )
                  break;
               hb_nsxTagSkipNext( pTag );
            }
         }
      }
      hb_nsxKeyFree( pKey );
      return pTag->CurKeyInfo->rec != 0 && pTag->CurKeyInfo->rec == pArea->dbfarea.ulRecNo;
   }
   pTag->TagBOF = pTag->TagEOF = HB_FALSE;
   return HB_TRUE;
}

/*
 * free pages allocated by tag
 */
static HB_BOOL hb_nsxTagPagesFree( LPTAGINFO pTag, HB_ULONG ulPage )
{
   LPPAGEINFO pPage = hb_nsxPageLoad( pTag, ulPage );
   HB_BOOL fOK = pPage != NULL;

   if( fOK )
   {
      if( ! hb_nsxIsLeaf( pPage ) )
      {
         HB_USHORT u;
         fOK = hb_nsxTagPagesFree( pTag, hb_nsxGetLowerPage( pPage ) );
         for( u = 0; fOK && u < pPage->uiKeys; u++ )
         {
            fOK = hb_nsxTagPagesFree( pTag,
                              hb_nsxGetKeyPage( pPage, pTag->KeyLength, u ) );
         }
      }
      if( fOK )
      {
         hb_nsxPageFree( pTag, pPage );
         if( ! pPage->pPrev )
            /* page is in not in hot pages list, write it now */
            fOK = hb_nsxPageSave( pTag->pIndex, pPage );
      }
      hb_nsxPageRelease( pTag, pPage );
   }

   return fOK;
}

/*
 * free space allocated by tag
 */
static HB_ERRCODE hb_nsxTagSpaceFree( LPTAGINFO pTag )
{
   if( hb_nsxTagHeaderCheck( pTag ) )
   {
      if( pTag->RootBlock )
      {
         if( ! hb_nsxTagPagesFree( pTag, pTag->RootBlock ) )
            return HB_FAILURE;
      }
      hb_nsxPageAddFree( pTag, pTag->HeadBlock );
      hb_nsxIndexTagDel( pTag->pIndex, pTag->TagName );
      pTag->pIndex->Changed = HB_TRUE;
   }
   hb_nsxTagDelete( pTag );
   return HB_SUCCESS;
}

/*
 * create index file name
 */
static void hb_nsxCreateFName( NSXAREAP pArea, const char * szBagName, HB_BOOL * fProd,
                               char * szFileName, char * szTagName )
{
   PHB_FNAME pFileName;
   PHB_ITEM pExt = NULL;
   HB_BOOL fName = szBagName && *szBagName;

   pFileName = hb_fsFNameSplit( fName ? szBagName : pArea->dbfarea.szDataFileName );

   if( szTagName )
   {
      if( pFileName->szName )
         hb_strncpyUpperTrim( szTagName, pFileName->szName, NSX_TAGNAME );
      else
         szTagName[ 0 ] = '\0';
   }

   if( ! fName || ( ! pFileName->szExtension && hb_setGetDefExtension() ) )
   {
      DBORDERINFO pExtInfo;
      memset( &pExtInfo, 0, sizeof( pExtInfo ) );
      pExt = pExtInfo.itmResult = hb_itemPutC( NULL, NULL );
      if( SELF_ORDINFO( &pArea->dbfarea.area, DBOI_BAGEXT, &pExtInfo ) == HB_SUCCESS &&
          hb_itemGetCLen( pExt ) > 0 )
      {
         pFileName->szExtension = hb_itemGetCPtr( pExt );
      }
   }
   hb_fsFNameMerge( szFileName, pFileName );

   if( fProd )
   {
      if( ! pFileName->szName )
         *fProd = HB_FALSE;
      else if( ! fName )
         *fProd = HB_TRUE;
      else
      {
         PHB_FNAME pTableFileName = hb_fsFNameSplit( pArea->dbfarea.szDataFileName );

         *fProd = pTableFileName->szName &&
                  hb_stricmp( pTableFileName->szName, pFileName->szName ) == 0;
         if( *fProd && pFileName->szExtension && ! pExt )
         {
            DBORDERINFO pExtInfo;
            memset( &pExtInfo, 0, sizeof( pExtInfo ) );
            pExt = pExtInfo.itmResult = hb_itemPutC( NULL, NULL );
            if( SELF_ORDINFO( &pArea->dbfarea.area, DBOI_BAGEXT, &pExtInfo ) == HB_SUCCESS )
            {
               *fProd = hb_stricmp( pFileName->szExtension,
                                    hb_itemGetCPtr( pExt ) ) == 0;
            }
         }
         hb_xfree( pTableFileName );
      }
   }
   hb_xfree( pFileName );
   if( pExt )
      hb_itemRelease( pExt );
}

/*
 * find order bag by its name
 */
static LPNSXINDEX hb_nsxFindBag( NSXAREAP pArea, const char * szBagName )
{
   LPNSXINDEX pIndex;
   PHB_FNAME pSeek;

   pSeek = hb_fsFNameSplit( szBagName );
   if( ! pSeek->szName )
      pSeek->szName = "";

   pIndex = pArea->lpIndexes;
   while( pIndex )
   {
      HB_BOOL fFound;
      PHB_FNAME pName = hb_fsFNameSplit( pIndex->IndexName );
      if( ! pName->szName )
         pName->szName = "";
      fFound = ! hb_stricmp( pName->szName, pSeek->szName ) &&
               ( ! pSeek->szPath || ( pName->szPath &&
                                      ! hb_stricmp( pName->szPath, pSeek->szPath ) ) ) &&
               ( ! pSeek->szExtension || ( pName->szExtension &&
                                           ! hb_stricmp( pName->szExtension, pSeek->szExtension ) ) );
      hb_xfree( pName );
      if( fFound )
         break;
      pIndex = pIndex->pNext;
   }
   hb_xfree( pSeek );
   return pIndex;
}

/*
 * Find tag by name in index bag
 */
static int hb_nsxFindTagByName( LPNSXINDEX pIndex, const char * szTag )
{
   int i;

   for( i = 0; i < pIndex->iTags; i++ )
   {
      if( ! hb_strnicmp( pIndex->lpTags[ i ]->TagName, szTag,
                         NSX_TAGNAME ) )
         return i + 1;
   }
   return 0;
}

/*
 * Find the tag by its name or number
 */
static LPTAGINFO hb_nsxFindTag( NSXAREAP pArea, PHB_ITEM pTagItem,
                                PHB_ITEM pBagItem )
{
   LPNSXINDEX pIndex;
   HB_BOOL fBag;

   if( ! pTagItem ||
       ( hb_itemType( pTagItem ) & ( HB_IT_STRING | HB_IT_NUMERIC ) ) == 0 )
      return pArea->lpCurTag;

   fBag = HB_IS_STRING( pTagItem ) && hb_itemGetCLen( pBagItem ) > 0;
   if( fBag )
   {
      pIndex = hb_nsxFindBag( pArea, hb_itemGetCPtr( pBagItem ) );
   }
   else
   {
      int iBag = hb_itemGetNI( pBagItem );

      pIndex = pArea->lpIndexes;
      if( iBag > 0 )
      {
         fBag = HB_TRUE;
         while( pIndex )
         {
            if( --iBag == 0 )
               break;
            pIndex = pIndex->pNext;
         }
      }
      else if( iBag < 0 )
      {
         pIndex = NULL;
      }
   }
   if( pIndex )
   {
      if( hb_itemType( pTagItem ) & HB_IT_STRING )
      {
         const char * szTag = hb_itemGetCPtr( pTagItem );
         int iTag;

         if( fBag )
            iTag = hb_nsxFindTagByName( pIndex, szTag );
         else
         {
            do
            {
               iTag = hb_nsxFindTagByName( pIndex, szTag );
               if( iTag )
                  break;
               pIndex = pIndex->pNext;
            }
            while( pIndex );
         }
         if( iTag )
            return pIndex->lpTags[ iTag - 1 ];
      }
      else
      {
         int i = hb_itemGetNI( pTagItem ) - 1;

         if( i >= 0 )
         {
            if( fBag )
            {
               if( i < pIndex->iTags )
                  return pIndex->lpTags[ i ];
            }
            else
            {
               do
               {
                  if( i < pIndex->iTags )
                     return pIndex->lpTags[ i ];
                  i -= pIndex->iTags;
                  pIndex = pIndex->pNext;
               }
               while( pIndex );
            }
         }
      }
   }

   return NULL;
}

/*
 * find the given tag number
 */
static int hb_nsxFindTagNum( NSXAREAP pArea, LPTAGINFO pTag )
{
   if( pArea->fSetTagNumbers )
   {
      LPNSXINDEX pIndex = pArea->lpIndexes;
      HB_USHORT uiNum = 0, i;

      pTag->uiNumber = 0;
      while( pIndex )
      {
         for( i = 0; i < pIndex->iTags; i++ )
         {
            pIndex->lpTags[ i ]->uiNumber = ++uiNum;
         }
         pIndex = pIndex->pNext;
      }
      pArea->fSetTagNumbers = HB_FALSE;
   }
   return pTag->uiNumber;
}

/*
 * count number of tags
 */
static int hb_nsxTagCount( NSXAREAP pArea )
{
   LPNSXINDEX pIndex = pArea->lpIndexes;
   int i = 0;

   while( pIndex )
   {
      i += pIndex->iTags;
      pIndex = pIndex->pNext;
   }

   return i;
}

/*
 * count number of keys in given tag
 */
static HB_ULONG hb_nsxOrdKeyCount( LPTAGINFO pTag )
{
   HB_ULONG ulKeyCount = 0;

   if( ! pTag->pIndex->fShared && pTag->keyCount &&
       ! pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      return pTag->keyCount;

   if( hb_nsxTagLockRead( pTag ) )
   {
      hb_nsxTagRefreshScope( pTag );

      if( pTag->top.scopeKeyLen || pTag->bottom.scopeKeyLen ||
          pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
      {
         hb_nsxTagGoTop( pTag );
         while( ! pTag->TagEOF )
         {
            ulKeyCount++;
            hb_nsxTagSkipNext( pTag );
         }
      }
      else
      {
         ulKeyCount = hb_nsxPageCountKeys( pTag, pTag->RootBlock );
      }
      if( ! pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
         pTag->keyCount = ulKeyCount;
      hb_nsxTagUnLockRead( pTag );
   }

   return ulKeyCount;
}

/*
 * get the logical key position in the given tag
 */
static HB_ULONG hb_nsxOrdKeyNo( LPTAGINFO pTag )
{
   HB_ULONG ulKeyNo = 0;

   if( hb_nsxTagLockRead( pTag ) )
   {
      hb_nsxTagRefreshScope( pTag );
      if( hb_nsxCurKeyRefresh( pTag ) )
      {
         if( pTag->top.scopeKeyLen || pTag->bottom.scopeKeyLen ||
             pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
         {
            if( hb_nsxKeyInScope( pTag, pTag->CurKeyInfo ) )
            {
               do
               {
                  ulKeyNo++;
                  hb_nsxTagSkipPrev( pTag );
               }
               while( ! pTag->TagBOF );
            }
         }
         else
         {
            int iLevel = pTag->stackLevel;
            HB_BOOL fBack = pTag->fUsrDescend, fFirst = HB_TRUE;

            while( --iLevel >= 0 )
            {
               LPPAGEINFO pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
               int iKey;
               if( ! pPage )
                  break;
               iKey = pTag->stack[ iLevel ].ikey;
               if( fBack )
               {
                  if( hb_nsxIsLeaf( pPage ) )
                     ulKeyNo += pPage->uiKeys - iKey;
                  else
                  {
                     if( fFirst && iKey )
                        --iKey;
                     ulKeyNo += pPage->uiKeys - iKey;
                     while( ++iKey <= pPage->uiKeys )
                     {
                        ulKeyNo += hb_nsxPageCountKeys( pTag,
                           hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 ) );
                     }
                  }
                  fFirst = HB_FALSE;
               }
               else
               {
                  if( hb_nsxIsLeaf( pPage ) )
                     ulKeyNo += iKey + 1;
                  else
                  {
                     ulKeyNo += iKey;
                     while( --iKey >= 0 )
                     {
                        ulKeyNo += hb_nsxPageCountKeys( pTag,
                           iKey == 0 ? hb_nsxGetLowerPage( pPage ) :
                           hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 ) );
                     }
                  }
               }
               hb_nsxPageRelease( pTag, pPage );
            }
         }
      }
      hb_nsxTagUnLockRead( pTag );
   }
   return ulKeyNo;
}

/*
 * set logical key position in given tag
 */
static HB_BOOL hb_nsxOrdKeyGoto( LPTAGINFO pTag, HB_ULONG ulKeyNo )
{
   NSXAREAP pArea = pTag->pIndex->pArea;

   if( ! ulKeyNo || ! hb_nsxTagLockRead( pTag ) )
      return HB_FALSE;

   hb_nsxTagRefreshScope( pTag );
   hb_nsxTagGoTop( pTag );
   if( pTag->TagEOF || pTag->top.scopeKeyLen || pTag->bottom.scopeKeyLen ||
       pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter )
   {
      while( ! pTag->TagEOF && --ulKeyNo )
         hb_nsxTagSkipNext( pTag );
   }
   else
   {
      int iLevel = pTag->stackLevel - 1;
      HB_BOOL fBack = pTag->fUsrDescend, fFirst = HB_TRUE;
      LPPAGEINFO pPage;
      HB_ULONG ulPage;

      --ulKeyNo;
      while( ulKeyNo != 0 && iLevel >= 0 )
      {
         int iKey = pTag->stack[ iLevel ].ikey;
         pPage = hb_nsxPageLoad( pTag, pTag->stack[ iLevel ].page );
         if( ! pPage )
            break;
         if( fBack )
         {
            if( hb_nsxIsLeaf( pPage ) )
            {
               if( ( HB_ULONG ) iKey < ulKeyNo )
               {
                  --iLevel;
                  ulKeyNo -= iKey;
               }
               else
               {
                  pTag->stack[ iLevel ].ikey -= ( HB_SHORT ) ulKeyNo;
                  ulKeyNo = 0;
               }
            }
            else
            {
               if( --iKey < 0 )
                  --iLevel;
               else if( fFirst || --ulKeyNo )
               {
                  pTag->stackLevel = ( HB_USHORTCAST ) iLevel;
                  hb_nsxTagSetPageStack( pTag, pPage, ( HB_USHORTCAST ) iKey );
                  ulPage = iKey == 0 ? hb_nsxGetLowerPage( pPage ) :
                           hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey - 1 );
                  hb_nsxPageRelease( pTag, pPage );
                  pPage = hb_nsxPageBottomMove( pTag, ulPage );
                  if( ! pPage )
                     break;
                  iLevel = pTag->stackLevel - 1;
                  --ulKeyNo;
               }
            }
            fFirst = HB_FALSE;
         }
         else
         {
            if( ! hb_nsxIsLeaf( pPage ) )
            {
               if( iKey >= pPage->uiKeys )
                  --iLevel;
               else
               {
                  pTag->stackLevel = ( HB_USHORTCAST ) iLevel;
                  hb_nsxTagSetPageStack( pTag, pPage, ( HB_USHORTCAST ) ( iKey + 1 ) );
                  if( --ulKeyNo )
                  {
                     ulPage = hb_nsxGetKeyPage( pPage, pTag->KeyLength, iKey );
                     hb_nsxPageRelease( pTag, pPage );
                     pPage = hb_nsxPageTopMove( pTag, ulPage );
                     if( ! pPage )
                        break;
                     iLevel = pTag->stackLevel - 1;
                     --ulKeyNo;
                  }
               }
            }
            else
            {
               iKey = pPage->uiKeys - iKey - 1;
               if( ( HB_ULONG ) iKey < ulKeyNo )
               {
                  --iLevel;
                  ulKeyNo -= iKey;
               }
               else
               {
                  pTag->stack[ iLevel ].ikey += ( HB_SHORT ) ulKeyNo;
                  ulKeyNo = 0;
               }
            }
         }
         pTag->stackLevel = ( HB_USHORTCAST ) ( iLevel + 1 );
         if( ulKeyNo == 0 )
         {
            if( ! hb_nsxTagGetCurKey( pTag, pPage, pTag->stack[ iLevel ].ikey ) )
               pTag->TagEOF = HB_TRUE;
         }
         hb_nsxPageRelease( pTag, pPage );
      }
   }

   if( ulKeyNo != 0 || pTag->TagEOF )
   {
      pTag->stackLevel = 0;
      SELF_GOTO( &pArea->dbfarea.area, 0 );
   }
   else
   {
      LPTAGINFO pSavedTag = pArea->lpCurTag;
      pArea->lpCurTag = pTag;
      if( SELF_GOTO( &pArea->dbfarea.area, pTag->CurKeyInfo->rec ) == HB_SUCCESS )
         SELF_SKIPFILTER( &pArea->dbfarea.area, 1 );
      pArea->lpCurTag = pSavedTag;
   }
   hb_nsxTagUnLockRead( pTag );
   return HB_TRUE;
}

/*
 * get the relative key position (from 0.0 to 1.0) in the given tag
 */
static double hb_nsxOrdGetRelKeyPos( LPTAGINFO pTag )
{
   double dPos = 0.0, dStart = 0.0, dStop = 1.0, dFact = 0.0000000000001;
   HB_BOOL fOK = HB_TRUE, fFilter = pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter;

   if( ! hb_nsxTagLockRead( pTag ) )
      return HB_FALSE;

   hb_nsxTagRefreshScope( pTag );

   pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter = HB_FALSE;
   if( pTag->fUsrDescend ? pTag->bottom.scopeKeyLen : pTag->top.scopeKeyLen )
   {
      hb_nsxTagGoTop( pTag );
      if( pTag->TagEOF )
         fOK = HB_FALSE;
      else
         dStart = hb_nsxTagCountRelKeyPos( pTag );
   }
   if( fOK && ( pTag->fUsrDescend ? pTag->top.scopeKeyLen : pTag->bottom.scopeKeyLen ) )
   {
      hb_nsxTagGoBottom( pTag );
      if( pTag->TagBOF )
         fOK = HB_FALSE;
      else
         dStop = hb_nsxTagCountRelKeyPos( pTag );
   }
   pTag->pIndex->pArea->dbfarea.area.dbfi.fFilter = fFilter;

   if( fOK )
   {
      if( hb_nsxCurKeyRefresh( pTag ) &&
          hb_nsxKeyInScope( pTag, pTag->CurKeyInfo ) )
      {
         if( dStart >= dStop - dFact )
            dPos = 0.5;
         else
         {
            dPos = hb_nsxTagCountRelKeyPos( pTag );
            dPos = ( dPos - dStart ) / ( dStop - dStart );
            /* fix possible differences in FL representation */
            if( dPos <= 0.0 )
               dPos = 0.0;
            else if( dPos >= 1.0 )
               dPos = 1.0;
         }
      }
   }
   hb_nsxTagUnLockRead( pTag );

   return dPos;
}

/*
 * set the relative key position (from 0.0 to 1.0) in the given tag
 */
static void hb_nsxOrdSetRelKeyPos( LPTAGINFO pTag, double dPos )
{
   if( hb_nsxTagLockRead( pTag ) )
   {
      NSXAREAP pArea = pTag->pIndex->pArea;
      double dStart = 0.0, dStop = 1.0, dFact = 0.0000000000001;
      HB_BOOL fOK = HB_TRUE, fFilter = pArea->dbfarea.area.dbfi.fFilter;
      HB_BOOL fForward = HB_TRUE, fTop = HB_FALSE;

      hb_nsxTagRefreshScope( pTag );

      if( dPos >= 1.0 )
         fForward = HB_FALSE;
      else if( dPos <= 0.0 )
         fTop = HB_TRUE;
      else
      {
         pArea->dbfarea.area.dbfi.fFilter = HB_FALSE;
         if( pTag->fUsrDescend ? pTag->bottom.scopeKeyLen : pTag->top.scopeKeyLen )
         {
            hb_nsxTagGoTop( pTag );
            if( pTag->TagEOF )
               fOK = HB_FALSE;
            else
               dStart = hb_nsxTagCountRelKeyPos( pTag );
         }
         if( fOK && ( pTag->fUsrDescend ? pTag->top.scopeKeyLen : pTag->bottom.scopeKeyLen ) )
         {
            hb_nsxTagGoBottom( pTag );
            if( pTag->TagBOF )
               fOK = HB_FALSE;
            else
               dStop = hb_nsxTagCountRelKeyPos( pTag );
         }
         pArea->dbfarea.area.dbfi.fFilter = fFilter;

         if( fOK )
         {
            if( dStart >= dStop - dFact )
            {
               fTop = HB_TRUE;
            }
            else
            {
               dPos = dPos * ( dStop - dStart ) + dStart;
               hb_nsxTagGoToRelKeyPos( pTag, dPos );
               if( pTag->CurKeyInfo->rec == 0 )
                  fForward = HB_FALSE;
               else if( ! hb_nsxInTopScope( pTag, pTag->CurKeyInfo->val ) )
                  fTop = HB_TRUE;
               else if( ! hb_nsxInBottomScope( pTag, pTag->CurKeyInfo->val ) )
                  fForward = HB_FALSE;
            }
         }
      }
      if( ! fOK )
      {
         SELF_GOTO( &pArea->dbfarea.area, 0 );
      }
      else
      {
         LPTAGINFO pSavedTag = pArea->lpCurTag;
         pArea->lpCurTag = pTag;

         pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;

         if( fForward )
         {
            if( fTop )
               hb_nsxTagGoTop( pTag );
            if( pTag->CurKeyInfo->rec != 0 )
            {
               if( SELF_GOTO( &pArea->dbfarea.area, pTag->CurKeyInfo->rec ) == HB_SUCCESS )
               {
                  SELF_SKIPFILTER( &pArea->dbfarea.area, 1 );
                  if( pArea->dbfarea.area.fEof && ! fTop )
                     fForward = HB_FALSE;
               }
            }
            else if( fTop )
               SELF_GOTO( &pArea->dbfarea.area, 0 );
            else
               fForward = HB_FALSE;
         }
         if( ! fForward )
         {
            hb_nsxTagGoBottom( pTag );
            if( SELF_GOTO( &pArea->dbfarea.area, pTag->CurKeyInfo->rec ) == HB_SUCCESS &&
                pTag->CurKeyInfo->rec != 0 )
            {
               pArea->dbfarea.area.fBottom = HB_TRUE;
               SELF_SKIPFILTER( &pArea->dbfarea.area, -1 );
            }
         }
         pArea->lpCurTag = pSavedTag;
      }
      hb_nsxTagUnLockRead( pTag );
   }
}

/*
 * skip to next/previous unique key
 */
static HB_BOOL hb_nsxOrdSkipUnique( LPTAGINFO pTag, HB_LONG lToSkip )
{
   NSXAREAP pArea = pTag->pIndex->pArea;

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;

   if( hb_nsxTagLockRead( pTag ) )
   {
      HB_BOOL fOut = HB_FALSE, fEof = HB_FALSE, fForward = ( lToSkip >= 0 );

      LPTAGINFO pSavedTag = pArea->lpCurTag;
      pArea->lpCurTag = pTag;

      hb_nsxTagRefreshScope( pTag );
      if( hb_nsxCurKeyRefresh( pTag ) )
      {
         HB_UCHAR keyVal[ NSX_MAXKEYLEN ];
         memcpy( keyVal, pTag->CurKeyInfo->val, pTag->KeyLength );

         do
         {
            if( fForward )
               hb_nsxTagSkipNext( pTag );
            else
               hb_nsxTagSkipPrev( pTag );
            fOut = pTag->TagEOF || pTag->TagBOF;
         }
         while( ! fOut && hb_nsxValCompare( pTag,
                                            pTag->CurKeyInfo->val, pTag->KeyLength,
                                            keyVal, pTag->KeyLength,
                                            NSX_CMP_EXACT ) == 0 );
      }
      else if( ! fForward && ! pArea->dbfarea.fPositioned )
      {
         hb_nsxTagGoBottom( pTag );
         fEof = pTag->TagEOF;
      }
      else
      {
         fOut = HB_TRUE;
      }
      if( fOut )
      {
         if( fForward )
            fEof = HB_TRUE;
         else
         {
            hb_nsxTagGoTop( pTag );
            fEof = pTag->TagEOF;
         }
      }
      hb_nsxTagUnLockRead( pTag );

      if( SELF_GOTO( &pArea->dbfarea.area, fEof ? 0 : pTag->CurKeyInfo->rec ) == HB_SUCCESS &&
          ! fEof )
      {
         SELF_SKIPFILTER( &pArea->dbfarea.area, ( fForward || fOut ) ? 1 : -1 );
         if( ! fForward && fOut )
            pArea->dbfarea.area.fBof = HB_TRUE;
      }

      /* Update Bof and Eof flags */
      if( fForward )
         pArea->dbfarea.area.fBof = HB_FALSE;
      else
         pArea->dbfarea.area.fEof = HB_FALSE;

      pArea->lpCurTag = pSavedTag;
      return HB_TRUE;
   }
   return HB_FALSE;
}

/*
 * skip while code block doesn't return HB_TRUE
 */
static HB_BOOL hb_nsxOrdSkipEval( LPTAGINFO pTag, HB_BOOL fForward, PHB_ITEM pEval )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   HB_BOOL fFound = HB_FALSE;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrdSkipEval(%p, %d, %p)", ( void * ) pTag, fForward, ( void * ) pEval ) );

   if( ( hb_itemType( pEval ) & HB_IT_BLOCK ) == 0 )
   {
      if( SELF_SKIP( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS )
         return HB_FALSE;
      return fForward ? ! pArea->dbfarea.area.fEof : ! pArea->dbfarea.area.fBof;
   }

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;

   if( hb_nsxTagLockRead( pTag ) )
   {
      LPTAGINFO pSavedTag = pArea->lpCurTag;
      pArea->lpCurTag = pTag;

      hb_nsxTagRefreshScope( pTag );
      if( hb_nsxCurKeyRefresh( pTag ) )
      {
         if( fForward )
            hb_nsxTagSkipNext( pTag );
         else
            hb_nsxTagSkipPrev( pTag );

         while( fForward ? ! pTag->TagEOF : ! pTag->TagBOF )
         {
            if( SELF_GOTO( &pArea->dbfarea.area, pTag->CurKeyInfo->rec ) != HB_SUCCESS )
               break;
            if( hb_nsxEvalSeekCond( pTag, pEval ) )
            {
               HB_ULONG ulRecNo = pArea->dbfarea.ulRecNo;
               if( SELF_SKIPFILTER( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS ||
                   pArea->dbfarea.ulRecNo == ulRecNo || hb_nsxEvalSeekCond( pTag, pEval ) )
               {
                  fFound = HB_TRUE;
                  break;
               }
            }
            if( fForward )
               hb_nsxTagSkipNext( pTag );
            else
               hb_nsxTagSkipPrev( pTag );
         }
         if( ! fFound )
         {
            if( fForward )
               SELF_GOTO( &pArea->dbfarea.area, 0 );
            else
            {
               SELF_GOTOP( &pArea->dbfarea.area );
               pArea->dbfarea.area.fBof = HB_TRUE;
            }
         }
      }
      pArea->lpCurTag = pSavedTag;
      hb_nsxTagUnLockRead( pTag );
   }

   /* Update Bof and Eof flags */
   if( fForward )
      pArea->dbfarea.area.fBof = HB_FALSE;
   else
      pArea->dbfarea.area.fEof = HB_FALSE;

   return fFound;
}

/*
 * skip while code block doesn't return HB_TRUE
 */
static HB_BOOL hb_nsxOrdSkipWild( LPTAGINFO pTag, HB_BOOL fForward, PHB_ITEM pWildItm )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   const char *szPattern;
   char *szFree = NULL;
   HB_BOOL fFound = HB_FALSE;
   int iFixed = 0;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrdSkipWild(%p, %d, %p)", ( void * ) pTag, fForward, ( void * ) pWildItm ) );

   szPattern = hb_itemGetCPtr( pWildItm );

   if( pTag->KeyType != 'C' || ! szPattern || ! *szPattern )
   {
      if( SELF_SKIP( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS )
         return HB_FALSE;
      return fForward ? ! pArea->dbfarea.area.fEof : ! pArea->dbfarea.area.fBof;
   }

   if( pArea->dbfarea.area.cdPage != hb_vmCDP() )
   {
      szPattern = szFree = hb_cdpDup( szPattern, hb_vmCDP(), pArea->dbfarea.area.cdPage );
   }
   while( iFixed < pTag->KeyLength && szPattern[ iFixed ] &&
          szPattern[ iFixed ] != '*' && szPattern[ iFixed ] != '?' )
   {
      ++iFixed;
   }

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;

   if( hb_nsxTagLockRead( pTag ) )
   {
      LPTAGINFO pSavedTag = pArea->lpCurTag;
      pArea->lpCurTag = pTag;

      hb_nsxTagRefreshScope( pTag );
      if( hb_nsxCurKeyRefresh( pTag ) )
      {
         int iStop = fForward ? -1 : 1;
         if( pTag->fUsrDescend )
            iStop = -iStop;
         if( iFixed && hb_nsxValCompare( pTag,
                                         ( const HB_UCHAR * ) szPattern, iFixed,
                                         pTag->CurKeyInfo->val, iFixed,
                                         NSX_CMP_PREFIX ) == -iStop )
         {
            LPKEYINFO pKey;
            pKey = hb_nsxKeyNew( pTag->KeyLength );
            memcpy( pKey->val, szPattern, iFixed );
            pKey->val[ iFixed ] = '\0';
            pKey->rec = pArea->lpCurTag->fUsrDescend ? NSX_MAX_REC_NUM :
                                                       NSX_IGNORE_REC_NUM;
            pKey->mode = NSX_CMP_PREFIX;
            if( ! hb_nsxTagKeyFind( pTag, pKey, ( HB_USHORTCAST ) iFixed ) )
            {
               if( fForward )
                  pTag->TagEOF = HB_TRUE;
               else
                  pTag->TagBOF = HB_TRUE;
            }
            hb_nsxKeyFree( pKey );
         }
         else if( fForward )
            hb_nsxTagSkipNext( pTag );
         else
            hb_nsxTagSkipPrev( pTag );

         while( fForward ? ! pTag->TagEOF : ! pTag->TagBOF )
         {
            if( hb_strMatchWild( ( const char * ) pTag->CurKeyInfo->val, szPattern ) )
            {
               HB_ULONG ulRecNo = pTag->CurKeyInfo->rec;
               if( SELF_GOTO( &pArea->dbfarea.area, ulRecNo ) != HB_SUCCESS )
                  break;
               if( SELF_SKIPFILTER( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS ||
                   pArea->dbfarea.ulRecNo == ulRecNo ||
                   hb_strMatchWild( ( const char * ) pTag->CurKeyInfo->val, szPattern ) )
               {
                  fFound = HB_TRUE;
                  break;
               }
            }
            if( iFixed && hb_nsxValCompare( pTag, ( const HB_UCHAR * ) szPattern, iFixed,
                             pTag->CurKeyInfo->val, iFixed, NSX_CMP_PREFIX ) == iStop )
            {
               break;
            }
            if( fForward )
               hb_nsxTagSkipNext( pTag );
            else
               hb_nsxTagSkipPrev( pTag );
         }
         if( ! fFound )
         {
            if( fForward )
               SELF_GOTO( &pArea->dbfarea.area, 0 );
            else
            {
               SELF_GOTOP( &pArea->dbfarea.area );
               pArea->dbfarea.area.fBof = HB_TRUE;
            }
         }
      }
      pArea->lpCurTag = pSavedTag;
      hb_nsxTagUnLockRead( pTag );
   }

   /* Update Bof and Eof flags */
   if( fForward )
      pArea->dbfarea.area.fBof = HB_FALSE;
   else
      pArea->dbfarea.area.fEof = HB_FALSE;

   if( szFree )
      hb_xfree( szFree );

   return fFound;
}

static HB_BOOL hb_nsxRegexMatch( LPTAGINFO pTag, PHB_REGEX pRegEx, const char * szKey )
{
   HB_SIZE nLen = pTag->KeyLength;
   char szBuff[ NSX_MAXKEYLEN + 1 ];

   if( pTag->pIndex->pArea->dbfarea.area.cdPage != hb_vmCDP() )
   {
      nLen = sizeof( szBuff ) - 1;
      hb_cdpnDup2( szKey, pTag->KeyLength, szBuff, &nLen,
                   pTag->pIndex->pArea->dbfarea.area.cdPage, hb_vmCDP() );
      szBuff[ nLen ] = '\0';
      szKey = szBuff;
   }
   return hb_regexMatch( pRegEx, szKey, nLen, HB_FALSE );
}

/*
 * skip while regular expression on index key val doesn't return HB_TRUE
 */
static HB_BOOL hb_nsxOrdSkipRegEx( LPTAGINFO pTag, HB_BOOL fForward, PHB_ITEM pRegExItm )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   HB_BOOL fFound = HB_FALSE;
   PHB_REGEX pRegEx;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrdSkipRegEx(%p, %d, %p)", ( void * ) pTag, fForward, ( void * ) pRegExItm ) );

   if( pTag->KeyType != 'C' || ( pRegEx = hb_regexGet( pRegExItm, 0 ) ) == NULL )
   {
      if( SELF_SKIP( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS )
         return HB_FALSE;
      return fForward ? ! pArea->dbfarea.area.fEof : ! pArea->dbfarea.area.fBof;
   }

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;

   if( hb_nsxTagLockRead( pTag ) )
   {
      LPTAGINFO pSavedTag = pArea->lpCurTag;
      pArea->lpCurTag = pTag;

      hb_nsxTagRefreshScope( pTag );
      if( hb_nsxCurKeyRefresh( pTag ) )
      {
         if( fForward )
            hb_nsxTagSkipNext( pTag );
         else
            hb_nsxTagSkipPrev( pTag );

         while( fForward ? ! pTag->TagEOF : ! pTag->TagBOF )
         {
            if( SELF_GOTO( &pArea->dbfarea.area, pTag->CurKeyInfo->rec ) != HB_SUCCESS )
               break;

            if( hb_nsxRegexMatch( pTag, pRegEx, ( const char * ) pTag->CurKeyInfo->val ) )
            {
               HB_ULONG ulRecNo = pArea->dbfarea.ulRecNo;
               if( SELF_SKIPFILTER( &pArea->dbfarea.area, fForward ? 1 : -1 ) != HB_SUCCESS ||
                   pArea->dbfarea.ulRecNo == ulRecNo ||
                   hb_nsxRegexMatch( pTag, pRegEx, ( const char * ) pTag->CurKeyInfo->val ) )
               {
                  fFound = HB_TRUE;
                  break;
               }
            }
            if( fForward )
               hb_nsxTagSkipNext( pTag );
            else
               hb_nsxTagSkipPrev( pTag );
         }
         if( ! fFound )
         {
            if( fForward )
               SELF_GOTO( &pArea->dbfarea.area, 0 );
            else
            {
               SELF_GOTOP( &pArea->dbfarea.area );
               pArea->dbfarea.area.fBof = HB_TRUE;
            }
         }
      }
      pArea->lpCurTag = pSavedTag;
      hb_nsxTagUnLockRead( pTag );
   }

   /* Update Bof and Eof flags */
   if( fForward )
      pArea->dbfarea.area.fBof = HB_FALSE;
   else
      pArea->dbfarea.area.fEof = HB_FALSE;

   hb_regexFree( pRegEx );

   return fFound;
}

/*
 * add key to custom tag (ordKeyAdd())
 * user key value is not implemented
 */
static HB_BOOL hb_nsxOrdKeyAdd( LPTAGINFO pTag, PHB_ITEM pItem )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   HB_BOOL fResult = HB_FALSE;
   LPKEYINFO pKey;

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( ! pArea->dbfarea.fPositioned )
      return HB_FALSE;

   if( pTag->pForItem && ! hb_nsxEvalCond( pArea, pTag->pForItem, HB_TRUE ) )
      return HB_FALSE;

   if( pTag->Template && pItem && hb_itemType( pItem ) != HB_IT_NIL )
   {
      pKey = hb_nsxKeyPutItem( NULL, pItem, pArea->dbfarea.ulRecNo, pTag, HB_TRUE, NULL );
   }
   else
   {
      pKey = hb_nsxEvalKey( NULL, pTag );
   }

   if( hb_nsxTagLockWrite( pTag ) )
   {
      if( hb_nsxTagKeyAdd( pTag, pKey ) )
      {
         fResult = HB_TRUE;
         if( ! pTag->pIndex->fShared && pTag->keyCount &&
             hb_nsxKeyInScope( pTag, pKey ) )
            pTag->keyCount++;
      }
      hb_nsxTagUnLockWrite( pTag );
   }
   hb_nsxKeyFree( pKey );
   return fResult;
}

/*
 * del key from custom tag (ordKeyDel())
 * user key value is not implemented
 */
static HB_BOOL hb_nsxOrdKeyDel( LPTAGINFO pTag, PHB_ITEM pItem )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   HB_BOOL fResult = HB_FALSE;
   LPKEYINFO pKey = NULL;

   if( pArea->dbfarea.lpdbPendingRel )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( ! pArea->dbfarea.fPositioned )
      return HB_FALSE;

   if( pTag->pForItem && ! hb_nsxEvalCond( pArea, pTag->pForItem, HB_TRUE ) )
      return HB_FALSE;

   if( pTag->Template && pItem && hb_itemType( pItem ) != HB_IT_NIL )
   {
      pKey = hb_nsxKeyPutItem( NULL, pItem, pArea->dbfarea.ulRecNo, pTag, HB_TRUE, NULL );
   }

   if( hb_nsxTagLockWrite( pTag ) )
   {
      if( pKey == NULL )
      {
         if( hb_nsxCurKeyRefresh( pTag ) )
            pKey = hb_nsxKeyCopy( NULL, pTag->CurKeyInfo, pTag->KeyLength );
         else
            pKey = hb_nsxEvalKey( NULL, pTag );
      }
      if( hb_nsxTagKeyDel( pTag, pKey ) )
      {
         fResult = HB_TRUE;
         if( ! pTag->pIndex->fShared && pTag->keyCount &&
             hb_nsxKeyInScope( pTag, pKey ) )
            pTag->keyCount--;
      }
      hb_nsxTagUnLockWrite( pTag );
   }
   hb_nsxKeyFree( pKey );
   return fResult;
}

/*
 * DBOI_FINDREC find a specific record in the tag - it's useful for
 * custom indexes when the same record can be stored more then once
 * or when the used index key is unknown
 */
static HB_BOOL hb_nsxOrdFindRec( LPTAGINFO pTag, HB_ULONG ulRecNo, HB_BOOL fCont )
{
   NSXAREAP pArea = pTag->pIndex->pArea;
   HB_BOOL fFound = HB_FALSE;

   if( pTag && ulRecNo )
   {
      if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
         SELF_FORCEREL( &pArea->dbfarea.area );

      if( hb_nsxTagLockRead( pTag ) )
      {
         hb_nsxTagRefreshScope( pTag );
         if( fCont )
         {
            if( ! hb_nsxCurKeyRefresh( pTag ) )
               ulRecNo = 0;
            else
               hb_nsxTagSkipNext( pTag );
         }
         else
         {
            hb_nsxTagGoTop( pTag );
         }
         if( ulRecNo )
         {
            while( ! pTag->TagEOF )
            {
               if( pTag->CurKeyInfo->rec == ulRecNo )
               {
                  fFound = HB_TRUE;
                  break;
               }
               hb_nsxTagSkipNext( pTag );
            }
         }
         hb_nsxTagUnLockRead( pTag );
      }
   }
   SELF_GOTO( &pArea->dbfarea.area, fFound ? ulRecNo : 0 );
   return fFound;
}

/*
 * evaluate given C function in given scope
 */
static HB_ULONG hb_nsxOrdScopeEval( LPTAGINFO pTag,
                                    HB_EVALSCOPE_FUNC pFunc, void * pParam,
                                    PHB_ITEM pItemLo, PHB_ITEM pItemHi )
{
   HB_ULONG ulCount = 0, ulLen = ( HB_ULONG ) pTag->KeyLength;
   PHB_ITEM pItemTop = hb_itemNew( NULL ), pItemBottom = hb_itemNew( NULL );
   HB_BOOL fDescend = pTag->fUsrDescend;

   if( fDescend )
   {
      PHB_ITEM pTemp = pItemLo;
      pItemLo = pItemHi;
      pItemHi = pTemp;
      pTag->fUsrDescend = HB_FALSE;
   }
   hb_nsxTagGetScope( pTag, 0, pItemTop );
   hb_nsxTagGetScope( pTag, 1, pItemBottom );
   hb_nsxTagSetScope( pTag, 0, pItemLo );
   hb_nsxTagSetScope( pTag, 1, pItemHi );

   if( hb_nsxTagLockRead( pTag ) )
   {
      hb_nsxTagGoTop( pTag );
      while( ! pTag->TagEOF )
      {
         pFunc( pTag->CurKeyInfo->rec, ( HB_BYTE * ) pTag->CurKeyInfo->val, ulLen, pParam );
         ulCount++;
         hb_nsxTagSkipNext( pTag );
      }
      hb_nsxTagUnLockRead( pTag );
   }

   hb_nsxTagSetScope( pTag, 0, pItemTop );
   hb_nsxTagSetScope( pTag, 1, pItemBottom );
   hb_itemRelease( pItemTop );
   hb_itemRelease( pItemBottom );

   pTag->fUsrDescend = fDescend;

   return ulCount;
}

/* ************************************************************************* */
/* create index: hb_nsxTagCreate() */
/* ************************************************************************* */

static int hb_nsxQuickSortCompare( LPNSXSORTINFO pSort, HB_UCHAR * pKey1, HB_UCHAR * pKey2 )
{
   int iLen = pSort->keyLen, i;

   i = hb_nsxValCompare( pSort->pTag, pKey1, iLen, pKey2, iLen, NSX_CMP_EXACT );
   if( i == 0 )
      i = ( HB_GET_LE_UINT32( pKey1 + iLen ) < HB_GET_LE_UINT32( pKey2 + iLen ) ) ? -1 : 1;

   return i;
}

static HB_BOOL hb_nsxQSort( LPNSXSORTINFO pSort, HB_UCHAR * pSrc, HB_UCHAR * pBuf, HB_LONG lKeys )
{
   if( lKeys > 1 )
   {
      int iLen = pSort->keyLen + 4;
      HB_LONG l1, l2;
      HB_UCHAR * pPtr1, * pPtr2, * pDst;
      HB_BOOL f1, f2;

      l1 = lKeys >> 1;
      l2 = lKeys - l1;
      pPtr1 = &pSrc[ 0 ];
      pPtr2 = &pSrc[ l1 * iLen ];

      f1 = hb_nsxQSort( pSort, pPtr1, &pBuf[ 0 ], l1 );
      f2 = hb_nsxQSort( pSort, pPtr2, &pBuf[ l1 * iLen ], l2 );
      if( f1 )
      {
         pDst = pBuf;
      }
      else
      {
         pDst = pSrc;
         pPtr1 = &pBuf[ 0 ];
      }
      if( ! f2 )
      {
         pPtr2 = &pBuf[ l1 * iLen ];
      }
      while( l1 > 0 && l2 > 0 )
      {
         if( hb_nsxQuickSortCompare( pSort, pPtr1, pPtr2 ) <= 0 )
         {
            memcpy( pDst, pPtr1, iLen );
            pPtr1 += iLen;
            l1--;
         }
         else
         {
            memcpy( pDst, pPtr2, iLen );
            pPtr2 += iLen;
            l2--;
         }
         pDst += iLen;
      }
      if( l1 > 0 )
         memcpy( pDst, pPtr1, iLen * l1 );
      else if( l2 > 0 && f1 == f2 )
         memcpy( pDst, pPtr2, iLen * l2 );
      return ! f1;
   }
   return HB_TRUE;
}

static void hb_nsxSortSortPage( LPNSXSORTINFO pSort )
{
   HB_SIZE nSize = ( HB_SIZE ) pSort->ulKeys * ( pSort->keyLen + 4 );

   if( ! hb_nsxQSort( pSort, pSort->pKeyPool, &pSort->pKeyPool[ nSize ], pSort->ulKeys ) )
      pSort->pStartKey = &pSort->pKeyPool[ nSize ];
   else
      pSort->pStartKey = pSort->pKeyPool;
}

static void hb_nsxSortBufferFlush( LPNSXSORTINFO pSort )
{
   if( pSort->ulPagesIO )
   {
      LPNSXINDEX pIndex = pSort->pTag->pIndex;
      HB_SIZE nSize = ( HB_SIZE ) pSort->ulPagesIO * NSX_PAGELEN;
      if( hb_fileWriteAt( pIndex->pFile, pSort->pBuffIO, nSize,
                    hb_nsxFileOffset( pIndex, pSort->ulFirstIO ) ) != nSize )
      {
         hb_nsxErrorRT( pIndex->pArea, EG_WRITE, EDBF_WRITE,
                        pIndex->IndexName, hb_fsError(), 0, NULL );
      }
      pSort->ulPagesIO = 0;
      pIndex->fFlush = HB_TRUE;
      if( pIndex->fShared )
         pIndex->Changed = HB_TRUE;
   }
}

static void hb_nsxSortStorePage( LPNSXSORTINFO pSort, LPPAGEINFO pPage )
{
   LPNSXINDEX pIndex = pSort->pTag->pIndex;

   if( ! pPage->Page )
   {
      pPage->Page = hb_nsxPageAlloc( pIndex );
      if( pSort->ulSizeIO )
      {
         if( pSort->ulPagesIO == pSort->ulSizeIO )
            hb_nsxSortBufferFlush( pSort );
         if( ! pSort->ulPagesIO ||
             hb_nsxFileOffset( pIndex, pSort->ulLastIO ) + NSX_PAGELEN ==
             hb_nsxFileOffset( pIndex, pPage->Page ) )
         {
            hb_nsxSetKeyCount( pPage, pPage->uiKeys );
            if( hb_nsxIsLeaf( pPage ) )
               hb_nsxLeafSetFreeOffset( pPage, pPage->uiOffset );
            memcpy( pSort->pBuffIO + pSort->ulPagesIO * NSX_PAGELEN,
                    hb_nsxPageBuffer( pPage ), NSX_PAGELEN );
            pSort->ulLastIO = pPage->Page;
            if( ! pSort->ulPagesIO++ )
               pSort->ulFirstIO = pPage->Page;
            pPage->Changed = HB_FALSE;
            return;
         }
      }
   }
   if( ! pPage->pPrev )
      /* page is in not in hot pages list, write it now */
      hb_nsxPageSave( pIndex, pPage );
}

static HB_BOOL hb_nsxSortAddNodeKey( LPNSXSORTINFO pSort, HB_UCHAR * pKeyVal, HB_ULONG ulRec )
{
   LPPAGEINFO pPage;
   HB_ULONG ulPage = 0;
   int iLevel = 0;

   for( ;; )
   {
      pPage = pSort->NodeList[ iLevel ];
      if( pPage == NULL )
      {
         pPage = pSort->NodeList[ iLevel ] = hb_nsxPageNew( pSort->pTag, HB_TRUE );
         if( ! pPage )
            return HB_FALSE;
         if( iLevel == 0 )
         {
            /* executed once for first key only */
            hb_nsxSetPageType( pPage, NSX_LEAFPAGE );
            hb_nsxSetKeyRecSize( pPage, pSort->recSize );
            pPage->uiOffset = hb_nsxLeafPutKey( pSort->pTag, pPage,
                                                NSX_LEAFKEYOFFSET, NULL,
                                                pKeyVal, ulRec );
         }
         else
         {
            hb_nsxSetKeyRecSize( pPage, 4 );
            hb_nsxSetLowerPage( pPage, ulPage );
         }
         break;
      }
      else if( iLevel == 0 )
      {
         HB_USHORT uiOffset = hb_nsxLeafPutKey( pSort->pTag, pPage,
                                                pPage->uiOffset, pSort->pLastKey,
                                                pKeyVal, ulRec );
         if( uiOffset != 0 )
         {
            pPage->uiOffset = uiOffset;
            break;
         }
#if defined( HB_NSX_CLEAR_UNUSED )
         else
            memset( hb_nsxPageBuffer( pPage ) + pPage->uiOffset, 0,
                    NSX_PAGELEN - pPage->uiOffset );
#endif
      }
      else if( pPage->uiKeys == 0 )
      {
         hb_nsxSetLowerPage( pPage, ulPage );
         break;
      }
      else
      {
         hb_nsxSetKeyPage( pPage, pSort->keyLen, pPage->uiKeys - 1, ulPage );
         if( pPage->uiKeys < pSort->pTag->MaxKeys )
            break;
      }

      hb_nsxSortStorePage( pSort, pPage );
      ulPage = pPage->Page;
      hb_nsxPageRelease( pSort->pTag, pPage );
      pPage = pSort->NodeList[ iLevel ] = hb_nsxPageNew( pSort->pTag, HB_TRUE );
      if( ! pPage )
         return HB_FALSE;
      if( iLevel == 0 )
      {
         pSort->ulLastLeaf = ulPage;
         hb_nsxSetPageType( pPage, NSX_LEAFPAGE );
         hb_nsxSetKeyRecSize( pPage, pSort->recSize );
         pPage->uiOffset = NSX_LEAFKEYOFFSET;
      }
      else
         hb_nsxSetKeyRecSize( pPage, 4 );
      iLevel++;
   }

   if( iLevel > 0 )
   {
      HB_UCHAR * pKeyPtr = hb_nsxGetBranchKeyPtr( pPage, pSort->keyLen, pPage->uiKeys );
      hb_nsxBranchKeySetRec( pKeyPtr, ulRec );
      memcpy( hb_nsxBranchKeyVal( pKeyPtr ), pKeyVal, pSort->keyLen );
   }
   pPage->uiKeys++;

   return HB_TRUE;
}

static void hb_nsxSortWritePage( LPNSXSORTINFO pSort )
{
   HB_SIZE nSize = ( HB_SIZE ) pSort->ulKeys * ( pSort->keyLen + 4 );

   hb_nsxSortSortPage( pSort );

   if( pSort->pTempFile == NULL )
   {
      char szName[ HB_PATH_MAX ];
      pSort->pTempFile = hb_fileCreateTemp( NULL, NULL, FC_NORMAL, szName );
      if( pSort->pTempFile == NULL )
         hb_nsxErrorRT( pSort->pTag->pIndex->pArea, EG_CREATE, EDBF_CREATE_TEMP,
                        szName, hb_fsError(), 0, NULL );
      else
         pSort->szTempFileName = hb_strdup( szName );
   }

   pSort->pSwapPage[ pSort->ulCurPage ].ulKeys = pSort->ulKeys;
   if( pSort->pTempFile != NULL )
   {
      pSort->pSwapPage[ pSort->ulCurPage ].nOffset = hb_fileSize( pSort->pTempFile );
      if( hb_fileWriteAt( pSort->pTempFile, pSort->pStartKey,
                          nSize, pSort->pSwapPage[ pSort->ulCurPage ].nOffset ) != nSize )
         hb_nsxErrorRT( pSort->pTag->pIndex->pArea, EG_WRITE, EDBF_WRITE_TEMP,
                        pSort->szTempFileName, hb_fsError(), 0, NULL );
   }
   else
      pSort->pSwapPage[ pSort->ulCurPage ].nOffset = 0;
   pSort->ulKeys = 0;
   pSort->ulCurPage++;
}

static void hb_nsxSortGetPageKey( LPNSXSORTINFO pSort, HB_ULONG ulPage,
                                  HB_UCHAR ** pKeyVal, HB_ULONG * pulRec )
{
   int iLen = pSort->keyLen;

   if( pSort->pSwapPage[ ulPage ].ulKeyBuf == 0 )
   {
      HB_ULONG ulKeys = HB_MIN( pSort->ulPgKeys, pSort->pSwapPage[ ulPage ].ulKeys );
      HB_SIZE nSize = ( HB_SIZE ) ulKeys * ( iLen + 4 );

      if( pSort->pTempFile != NULL &&
          hb_fileReadAt( pSort->pTempFile, pSort->pSwapPage[ ulPage ].pKeyPool,
                         nSize, pSort->pSwapPage[ ulPage ].nOffset ) != nSize )
      {
         hb_nsxErrorRT( pSort->pTag->pIndex->pArea, EG_READ, EDBF_READ_TEMP,
                        pSort->szTempFileName, hb_fsError(), 0, NULL );
      }
      pSort->pSwapPage[ ulPage ].nOffset += nSize;
      pSort->pSwapPage[ ulPage ].ulKeyBuf = ulKeys;
      pSort->pSwapPage[ ulPage ].ulCurKey = 0;
   }
   *pKeyVal = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ];
   *pulRec = HB_GET_LE_UINT32( *pKeyVal + iLen );
}

static void hb_nsxSortOrderPages( LPNSXSORTINFO pSort )
{
   pSort->ulFirst = 0;
   pSort->pSortedPages = ( HB_ULONG * ) hb_xgrab( pSort->ulPages * sizeof( HB_ULONG ) );
   pSort->pSortedPages[ 0 ] = 0;

   if( pSort->ulTotKeys > 0 )
   {
      int iLen = pSort->keyLen;
      HB_UCHAR *pKey = NULL;

      HB_ULONG n;

      for( n = 0; n < pSort->ulPages; n++ )
      {
         HB_ULONG ulRec;
         HB_LONG l, r;

         hb_nsxSortGetPageKey( pSort, n, &pKey, &ulRec );
         l = 0;
         r = n - 1;
         while( l <= r )
         {
            int i;
            HB_ULONG ulPage;
            HB_LONG m;
            HB_UCHAR * pTmp;

            m = ( l + r ) >> 1;
            ulPage = pSort->pSortedPages[ m ];
            pTmp = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ];
            i = hb_nsxValCompare( pSort->pTag, pKey, iLen, pTmp, iLen, NSX_CMP_EXACT );
            if( i == 0 )
               i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1;
            if( i >= 0 )
               l = m + 1;
            else
               r = m - 1;
         }
         for( r = n; r > l; r-- )
            pSort->pSortedPages[ r ] = pSort->pSortedPages[ r - 1 ];
         pSort->pSortedPages[ l ] = n;
      }
   }
}

static HB_BOOL hb_nsxSortKeyGet( LPNSXSORTINFO pSort, HB_UCHAR ** pKeyVal, HB_ULONG * pulRec )
{
   HB_ULONG ulPage = pSort->pSortedPages[ pSort->ulFirst ];

   /* check if first page has some keys yet */
   if( pSort->pSwapPage[ ulPage ].ulKeys > 0 )
   {
      int iLen = pSort->keyLen;
      HB_UCHAR * pKey;
      HB_ULONG ulRec;
      HB_LONG l, r;

      /*
       * last key was taken from this page - we have to resort it.
       * This is done intentionally here to be sure that the key
       * value return by this function will not be overwritten by
       * next keys in page read from temporary file in function
       * hb_nsxSortGetPageKey() - please do not move this part down
       * even it seems to be correct
       */
      hb_nsxSortGetPageKey( pSort, ulPage, &pKey, &ulRec );

      l = pSort->ulFirst + 1;
      r = pSort->ulPages - 1;
      while( l <= r )
      {
         int i;
         HB_ULONG ulPg;
         HB_LONG m;
         HB_UCHAR * pTmp;

         m = ( l + r ) >> 1;
         ulPg = pSort->pSortedPages[ m ];
         pTmp = &pSort->pSwapPage[ ulPg ].pKeyPool[ pSort->pSwapPage[ ulPg ].ulCurKey * ( iLen + 4 ) ];
         i = hb_nsxValCompare( pSort->pTag, pKey, iLen, pTmp, iLen, NSX_CMP_EXACT );
         if( i == 0 )
            i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1;
         if( i > 0 )
            l = m + 1;
         else
            r = m - 1;
      }
      if( l > ( HB_LONG ) pSort->ulFirst + 1 )
      {
         ulPage = pSort->pSortedPages[ pSort->ulFirst ];
         for( r = pSort->ulFirst + 1; r < l; r++ )
            pSort->pSortedPages[ r - 1 ] = pSort->pSortedPages[ r ];
         pSort->pSortedPages[ l - 1 ] = ulPage;
      }
   }
   else
   {
      pSort->ulFirst++;
   }
   if( pSort->ulFirst < pSort->ulPages )
   {
      ulPage = pSort->pSortedPages[ pSort->ulFirst ];
      hb_nsxSortGetPageKey( pSort, ulPage, pKeyVal, pulRec );
      pSort->pSwapPage[ ulPage ].ulCurKey++;
      pSort->pSwapPage[ ulPage ].ulKeys--;
      pSort->pSwapPage[ ulPage ].ulKeyBuf--;
      return HB_TRUE;
   }

   *pKeyVal = NULL;
   *pulRec = 0;

   return HB_FALSE;
}

static void hb_nsxSortKeyAdd( LPNSXSORTINFO pSort, HB_ULONG ulRec, const char * pKeyVal, int iKeyLen )
{
   int iLen = pSort->keyLen;
   HB_UCHAR * pDst;

   if( pSort->ulKeys >= pSort->ulPgKeys )
   {
      hb_nsxSortWritePage( pSort );
   }
   pDst = &pSort->pKeyPool[ pSort->ulKeys * ( iLen + 4 ) ];

   if( iLen > iKeyLen )
   {
      memcpy( pDst, pKeyVal, iKeyLen );
      memset( &pDst[ iKeyLen ], pSort->trailChar, iLen - iKeyLen );
   }
   else
   {
      memcpy( pDst, pKeyVal, iLen );
   }
   HB_PUT_LE_UINT32( &pDst[ iLen ], ulRec );
   pSort->ulKeys++;
   pSort->ulTotKeys++;
}

static LPNSXSORTINFO hb_nsxSortNew( LPTAGINFO pTag, HB_ULONG ulRecCount )
{
   LPNSXSORTINFO pSort;
   HB_UCHAR * pBuf;
   int iLen = pTag->KeyLength;
   HB_ULONG ulSize, ulMax, ulMin;

   if( ulRecCount == 0 )
      ulRecCount = 1;

   pSort = ( LPNSXSORTINFO ) hb_xgrabz( sizeof( NSXSORTINFO ) );

   ulMin = ( HB_ULONG ) ceil( sqrt( ( double ) ulRecCount ) );
   ulMax = ( ( HB_ULONG ) ceil( sqrt( ( double ) ulRecCount / ( iLen + 4 ) ) ) ) << 7;
   /*
    * this effectively increase allocated memory buffer for very large files
    * moving the maximum to: 267'443'712 for 4'294'967'295 records and 250
    * index key length.
    * if you want to force smaller buffer I wrote below then add here:
    * ulMax = ulMin;
    */
   ulSize = ( 1L << 20 ) / ( iLen + 4 );
   while( ulMax < ulSize )
      ulMax <<= 1;
   if( ulMax > ulRecCount )
      ulMax = ulRecCount;

   do
   {
      ulSize = ulMax * ( iLen + 4 );
      pBuf = ( HB_UCHAR * ) hb_xalloc( ulSize << 2 );
      if( pBuf )
      {
         hb_xfree( pBuf );
         pBuf = ( HB_UCHAR * ) hb_xalloc( ulSize << 1 );
      }
      else
      {
         ulMax >>= 1;
      }
   }
   while( ! pBuf && ulMax >= ulMin );

   if( ! pBuf )
   {
      /* call hb_xgrab() to force out of memory error,
       * though in multi process environment this call may return
       * with success when other process free some memory
       * (also the size of buf is reduced to absolute minimum).
       * Sorry but I'm to lazy to implement indexing with smaller
       * memory though it's possible - just simply I can even create
       * index on-line by key adding like in normal update process.
       * The memory necessary to index file is now ~
       *    ~ (keySize+4+sizeof(NSXSWAPPAGE)) * sqrt(ulRecCount) * 2
       * so the maximum is for DBF with 2^32 records and keySize 256 ~
       * ~ 2^17 * 284 ~=~ 37 MB
       * this is not a problem for current computers and I do not see
       * any way to use DBFs with four billions records and indexes with
       * such long (256 bytes) keys on the old ones - they will be simply
       * to slow. IMHO it's also better to signal out of memory here and
       * force some system upgrades then run process which will have to
       * take many hours, Druzus.
       */
      ulMax = ulMin;
      pBuf = ( HB_UCHAR * ) hb_xgrab( ( ulMax << 1 ) * ( iLen + 4 ) );
   }

   pSort->pTag = pTag;
   pSort->pTempFile = NULL;
   pSort->keyLen = iLen;
   pSort->trailChar = pTag->TrailChar;
   pSort->recSize = hb_nsxGetRecSize( ulRecCount );
   pSort->fUnique = pTag->UniqueKey;
   pSort->ulMaxKey = ulMax << 1;
   pSort->ulPgKeys = ulMax;
   pSort->ulMaxRec = ulRecCount;
   pSort->pKeyPool = pBuf;
   pSort->ulPages = ( ulRecCount + pSort->ulPgKeys - 1 ) / pSort->ulPgKeys;
   /* check for overflow on 32-bit machines when number of records is nearly 2^32 */
   if( ! pSort->ulPages )
      pSort->ulPages = ulRecCount / pSort->ulPgKeys + 1;
   pSort->pSwapPage = ( LPNSXSWAPPAGE ) hb_xgrabz( sizeof( NSXSWAPPAGE ) * pSort->ulPages );
   return pSort;
}

static void hb_nsxSortFree( LPNSXSORTINFO pSort, HB_BOOL fFull )
{
   if( pSort->pTempFile != NULL )
   {
      hb_fileClose( pSort->pTempFile );
      pSort->pTempFile = NULL;
   }
   if( pSort->szTempFileName )
   {
      hb_fileDelete( pSort->szTempFileName );
      hb_xfree( pSort->szTempFileName );
      pSort->szTempFileName = NULL;
   }
   if( pSort->pKeyPool )
   {
      hb_xfree( pSort->pKeyPool );
      pSort->pKeyPool = NULL;
   }
   if( pSort->pSwapPage )
   {
      hb_xfree( pSort->pSwapPage );
      pSort->pSwapPage = NULL;
   }
   if( pSort->pBuffIO )
   {
      hb_xfree( pSort->pBuffIO );
      pSort->pBuffIO = NULL;
   }
   if( pSort->pSortedPages )
   {
      hb_xfree( pSort->pSortedPages );
      pSort->pSortedPages = NULL;
   }
   if( fFull )
   {
      hb_xfree( pSort );
   }
}

static void hb_nsxSortOut( LPNSXSORTINFO pSort )
{
   HB_BOOL fUnique = pSort->fUnique, fNext;
   LPTAGINFO pTag = pSort->pTag;
   HB_ULONG ulLastPage = 0, ulPage, ulRec, ulKey;
   HB_UCHAR * pKeyVal = NULL;
   int iLen = pSort->keyLen, iLevel;

   pSort->ulPages = pSort->ulCurPage + 1;
   pSort->ulPgKeys = pSort->ulMaxKey / pSort->ulPages;
   if( pSort->ulPages > 1 )
   {
      HB_UCHAR * pBuf = pSort->pKeyPool;
      hb_nsxSortWritePage( pSort );
      for( ulPage = 0; ulPage < pSort->ulPages; ulPage++ )
      {
         pSort->pSwapPage[ ulPage ].ulKeyBuf = 0;
         pSort->pSwapPage[ ulPage ].ulCurKey = 0;
         pSort->pSwapPage[ ulPage ].pKeyPool = pBuf;
         pBuf += pSort->ulPgKeys * ( pSort->keyLen + 4 );
      }
   }
   else
   {
      hb_nsxSortSortPage( pSort );
      pSort->pSwapPage[ 0 ].ulKeys = pSort->ulKeys;
      pSort->pSwapPage[ 0 ].ulKeyBuf = pSort->ulKeys;
      pSort->pSwapPage[ 0 ].ulCurKey = 0;
      pSort->pSwapPage[ 0 ].pKeyPool = pSort->pStartKey;
   }

#if defined( HB_NSX_SIX_STRICT )
   if( ! pTag->HeadBlock )
   {
      pTag->HeadBlock = hb_nsxPageAlloc( pTag->pIndex );
      hb_nsxIndexTagAdd( pTag->pIndex, pTag );
   }
#endif

   hb_nsxSortOrderPages( pSort );

   if( hb_vmRequestQuery() != 0 )
      return;

   for( ulKey = 0; ulKey < pSort->ulTotKeys; ulKey++ )
   {
      if( ! hb_nsxSortKeyGet( pSort, &pKeyVal, &ulRec ) )
      {
         if( hb_vmRequestQuery() != 0 )
            return;
         hb_errInternal( 9999, "hb_nsxSortOut: memory structure corrupted.", NULL, NULL );
      }
      if( fUnique )
      {
         if( ulKey != 0 && hb_nsxValCompare( pTag, pSort->pLastKey, iLen,
                                             pKeyVal, iLen, NSX_CMP_EXACT ) == 0 )
            continue;
      }
#ifdef HB_NSX_DEBUG_EXT
      if( ulKey != 0 )
      {
         int i = hb_nsxValCompare( pTag, pSort->pLastKey, iLen, pKeyVal,
                                   iLen, NSX_CMP_EXACT );
         if( i == 0 )
            i = ( pSort->ulLastRec < ulRec ) ? -1 : 1;
         if( i > 0 )
         {
            printf( "\r\nulKey=%ld, pKeyVal=[%s][%ld], pKeyLast=[%s][%ld]\r\n",
                    ulKey, pKeyVal, ulRec, pSort->pLastKey, pSort->ulLastRec ); fflush( stdout );
            if( hb_vmRequestQuery() != 0 )
               return;
            hb_errInternal( 9999, "hb_nsxSortOut: sorting fails.", NULL, NULL );
         }
      }
#endif
      if( ! hb_nsxSortAddNodeKey( pSort, pKeyVal, ulRec ) )
         return;
      if( ulKey < pSort->ulTotKeys - 1 )
      {
         pSort->ulLastRec = ulRec;
         memcpy( pSort->pLastKey, pKeyVal, iLen );
      }
   }

#ifdef HB_NSX_DEBUG
   {
      HB_UCHAR * pTemp;
      if( hb_nsxSortKeyGet( pSort, &pTemp, &ulRec ) )
      {
         if( hb_vmRequestQuery() != 0 )
            return;
         hb_errInternal( 9999, "hb_nsxSortOut: memory structure corrupted(2).", NULL, NULL );
      }
   }
#endif

   if( pSort->NodeList[ 0 ] == NULL )
   {
      pSort->NodeList[ 0 ] = hb_nsxPageNew( pTag, HB_TRUE );
      if( pSort->NodeList[ 0 ] == NULL )
         return;
      hb_nsxSetPageType( pSort->NodeList[ 0 ], NSX_LEAFPAGE );
      hb_nsxSetKeyRecSize( pSort->NodeList[ 0 ], pSort->recSize );
      pSort->NodeList[ 0 ]->uiOffset = NSX_LEAFKEYOFFSET;
   }

   iLevel = 0;
   ulPage = 0;
   fNext = HB_TRUE;
   do
   {
      if( iLevel + 1 == NSX_STACKSIZE || pSort->NodeList[ iLevel + 1 ] == NULL )
      {
         hb_nsxPageType( pSort->NodeList[ iLevel ] ) |= NSX_ROOTPAGE;
         hb_nsxSortStorePage( pSort, pSort->NodeList[ iLevel ] );
         if( ulPage && ! ulLastPage && pSort->NodeList[ iLevel ]->uiKeys != 0 )
            ulLastPage = pSort->NodeList[ iLevel ]->Page;
         pTag->RootBlock = pSort->NodeList[ iLevel ]->Page;
         fNext = HB_FALSE;
      }
      else
      {
         hb_nsxSortStorePage( pSort, pSort->NodeList[ iLevel ] );
         if( iLevel == 0 )
         {
            if( pSort->NodeList[ iLevel ]->uiKeys == 0 )
               /* last leaf page is empty */
               ulPage = pSort->NodeList[ 0 ]->Page;
         }
         else if( ulPage && ! ulLastPage && pSort->NodeList[ iLevel ]->uiKeys != 0 )
            ulLastPage = pSort->NodeList[ iLevel ]->Page;

         if( pSort->NodeList[ iLevel + 1 ]->uiKeys == 0 )
            hb_nsxSetLowerPage( pSort->NodeList[ iLevel + 1 ],
                                pSort->NodeList[ iLevel ]->Page );
         else
            hb_nsxSetKeyPage( pSort->NodeList[ iLevel + 1 ], pSort->keyLen,
                              pSort->NodeList[ iLevel + 1 ]->uiKeys - 1,
                              pSort->NodeList[ iLevel ]->Page );
      }
      hb_nsxPageRelease( pTag, pSort->NodeList[ iLevel ] );
      iLevel++;
   }
   while( fNext );

   /* small hack to detect if we still have the value of the key before
    * last in pSort->pLastKey in unique indexes. Such information is
    * necessary to properly update the last leaf key so it will not
    * be empty. SIX3 NSX RDD does not do that so it can create corrupted
    * indexes in such case.
    */
   fUnique = fUnique && ulLastPage &&
             hb_nsxValCompare( pTag, pSort->pLastKey, iLen,
                               pKeyVal, iLen, NSX_CMP_EXACT ) == 0;

   hb_nsxSortBufferFlush( pSort );
   hb_nsxSortFree( pSort, HB_FALSE );

   if( ulLastPage && hb_vmRequestQuery() == 0 )
   {
      /* last leaf page is empty, we have to move last key
       * to this page and move the last key from the last
       * nonempty leaf page to previous last key location
       * in branch node
       * SIX3 NSX RDD does not balance the right most branch
       * pages and only move the last to keys
       */
      LPPAGEINFO pPage, pLastLeaf, pLastPage;

      if( fUnique )
      {
         /* SIXNSX does not do that - see note before above - it's a bug */
         hb_nsxTagBottomKey( pTag );
         if( ! hb_nsxTagPrevKey( pTag ) || ! hb_nsxTagPrevKey( pTag ) )
            return;
         pSort->ulLastRec = pTag->CurKeyInfo->rec;
         memcpy( pSort->pLastKey, pTag->CurKeyInfo->val, iLen );
         pTag->CurKeyOffset = 0;
         pTag->stackLevel = 0;
      }

      pPage = hb_nsxPageLoad( pTag, ulPage );
      if( pPage )
      {
         pLastLeaf = hb_nsxPageLoad( pTag, pSort->ulLastLeaf );
         if( pLastLeaf )
         {
            pLastPage = hb_nsxPageLoad( pTag, ulLastPage );
            if( pLastPage )
            {
               HB_UCHAR * pBuffer;
               HB_USHORT uiKey, uiRecSize;

               /* remove the key before last from the last nonempty leaf page */
               pBuffer = ( HB_UCHAR * ) hb_nsxPageBuffer( pLastLeaf );
               uiRecSize = hb_nsxGetKeyRecSize( pLastLeaf );
               pLastLeaf->uiOffset = NSX_LEAFKEYOFFSET;
               pLastLeaf->uiKeys--;
               for( uiKey = 0; uiKey < pLastLeaf->uiKeys; uiKey++ )
                  pLastLeaf->uiOffset += pBuffer[ pLastLeaf->uiOffset + uiRecSize ];
#if defined( HB_NSX_CLEAR_UNUSED ) && ! defined( HB_NSX_SIX_STRICT )
               memset( hb_nsxPageBuffer( pLastLeaf ) + pLastLeaf->uiOffset, 0,
                       NSX_PAGELEN - pLastLeaf->uiOffset );
#endif
               /* copy last key to last leaf page */
               pKeyVal = ( HB_UCHAR * ) hb_nsxGetKeyVal( pLastPage, pTag->KeyLength,
                                                      pLastPage->uiKeys - 1 );
               ulRec = hb_nsxGetKeyRec( pLastPage, pTag->KeyLength,
                                        pLastPage->uiKeys - 1 );
               pPage->uiOffset = hb_nsxLeafPutKey( pTag, pPage, NSX_LEAFKEYOFFSET,
                                                   pSort->pLastKey, pKeyVal, ulRec );
               pPage->uiKeys++;
               /* copy the key before last to the last branch page */
               hb_nsxSetKeyRec( pLastPage, pTag->KeyLength,
                                pLastPage->uiKeys - 1, pSort->ulLastRec );
               memcpy( pKeyVal, pSort->pLastKey, pTag->KeyLength );

               /* mark pages as modified */
               pPage->Changed = pLastPage->Changed = pLastLeaf->Changed = HB_TRUE;

               hb_nsxPageRelease( pTag, pLastPage );
            }
            hb_nsxPageRelease( pTag, pLastLeaf );
         }
         hb_nsxPageRelease( pTag, pPage );
      }
   }
}

/*
 * create tag in index file
 */
static HB_ERRCODE hb_nsxTagCreate( LPTAGINFO pTag, HB_BOOL fReindex )
{
   LPNSXAREA pArea = pTag->pIndex->pArea;
   PHB_ITEM pWhileItem = NULL, pEvalItem = NULL;
   HB_ULONG ulRecCount, ulRecNo = pArea->dbfarea.ulRecNo;
   LPNSXSORTINFO pSort;
   HB_LONG lStep = 0;
   HB_ERRCODE errCode = HB_SUCCESS;

   if( pArea->dbfarea.area.lpdbOrdCondInfo )
   {
      pWhileItem = pArea->dbfarea.area.lpdbOrdCondInfo->itmCobWhile;
      lStep = pArea->dbfarea.area.lpdbOrdCondInfo->lStep;
      pEvalItem = pArea->dbfarea.area.lpdbOrdCondInfo->itmCobEval;
   }

   if( pTag->Custom )
   {
      ulRecCount = 0;
   }
   else
   {
      errCode = SELF_RECCOUNT( &pArea->dbfarea.area, &ulRecCount );
      if( errCode != HB_SUCCESS )
         return errCode;
   }
   pArea->pSort = pSort = hb_nsxSortNew( pTag, ulRecCount );
   pSort->fReindex = fReindex;

   if( ulRecCount == 0 )
   {
      LPPAGEINFO pPage;

#if defined( HB_NSX_SIX_STRICT )
      if( ! pTag->HeadBlock )
      {
         pTag->HeadBlock = hb_nsxPageAlloc( pTag->pIndex );
         hb_nsxIndexTagAdd( pTag->pIndex, pTag );
      }
#endif

      pPage = hb_nsxPageNew( pTag, HB_FALSE );
      if( pPage )
      {
         pTag->RootBlock = pPage->Page;
         hb_nsxSetPageType( pPage, NSX_ROOTPAGE | NSX_LEAFPAGE );
         hb_nsxSetKeyRecSize( pPage, hb_nsxGetRecSize( 0 ) );
         pPage->uiOffset = NSX_LEAFKEYOFFSET;
         hb_nsxPageRelease( pTag, pPage );
      }
      else
      {
         errCode = HB_FAILURE;
      }
   }
   else
   {
      LPTAGINFO pSaveTag = pArea->lpCurTag;
      HB_ULONG ulStartRec = 0, ulNextCount = 0;
      HB_BOOL fDirectRead, fUseFilter = HB_FALSE;
      HB_BYTE * pSaveRecBuff = pArea->dbfarea.pRecord;
      char szBuffer[ NSX_MAXKEYLEN ];
      int iRecBuff = 0, iRecBufSize, iRec;
      double d;
      PHB_CODEPAGE cdpTmp = hb_cdpSelect( pArea->dbfarea.area.cdPage );
      PHB_ITEM pItem = NULL;
      PHB_ITEM pForItem;

      pForItem = pTag->pForItem;
      if( pTag->nField )
         pItem = hb_itemNew( NULL );

      if( ! pArea->dbfarea.area.lpdbOrdCondInfo || pArea->dbfarea.area.lpdbOrdCondInfo->fAll )
      {
         pArea->lpCurTag = NULL;
      }
      else
      {
         if( pArea->dbfarea.area.lpdbOrdCondInfo->itmRecID )
            ulStartRec = hb_itemGetNL( pArea->dbfarea.area.lpdbOrdCondInfo->itmRecID );
         if( ulStartRec )
         {
            ulNextCount = 1;
         }
         else if( pArea->dbfarea.area.lpdbOrdCondInfo->fRest || pArea->dbfarea.area.lpdbOrdCondInfo->lNextCount > 0 )
         {
            if( pArea->dbfarea.area.lpdbOrdCondInfo->itmStartRecID )
               ulStartRec = hb_itemGetNL( pArea->dbfarea.area.lpdbOrdCondInfo->itmStartRecID );
            if( ! ulStartRec )
               ulStartRec = ulRecNo;
            if( pArea->dbfarea.area.lpdbOrdCondInfo->lNextCount > 0 )
               ulNextCount = pArea->dbfarea.area.lpdbOrdCondInfo->lNextCount;
         }
         else if( pArea->dbfarea.area.lpdbOrdCondInfo->fUseFilter )
         {
            fUseFilter = HB_TRUE;
         }
         else if( ! pArea->dbfarea.area.lpdbOrdCondInfo->fUseCurrent )
         {
            pArea->lpCurTag = NULL;
         }
         else if( pArea->lpCurTag )
         {
            if( hb_nsxTagLockRead( pArea->lpCurTag ) )
            {
               hb_nsxTagRefreshScope( pArea->lpCurTag );
               hb_nsxTagGoTop( pArea->lpCurTag );
               if( ! pArea->lpCurTag->TagEOF )
                  ulStartRec = pArea->lpCurTag->CurKeyInfo->rec;
               hb_nsxTagUnLockRead( pArea->lpCurTag );
            }
         }
      }

      pSort->ulSizeIO = ( 1 << 16 ) / NSX_PAGELEN;
      pSort->pBuffIO = ( HB_UCHAR * ) hb_xgrab( pSort->ulSizeIO * NSX_PAGELEN );
      iRecBufSize = ( pSort->ulSizeIO * NSX_PAGELEN ) / pArea->dbfarea.uiRecordLen;
      fDirectRead = ! hb_setGetStrictRead() && iRecBufSize > 1 && /* ! pArea->dbfarea.area.lpdbRelations && */
                    ( ! pArea->dbfarea.area.lpdbOrdCondInfo || pArea->dbfarea.area.lpdbOrdCondInfo->fAll ||
                      ( pArea->lpCurTag == NULL && ! fUseFilter ) );

      if( ulStartRec == 0 && pArea->lpCurTag == NULL )
         ulStartRec = 1;

      if( ulStartRec == 0 )
      {
         errCode = SELF_GOTOP( &pArea->dbfarea.area );
      }
      else
      {
         errCode = SELF_GOTO( &pArea->dbfarea.area, ulStartRec );
         if( fUseFilter && errCode == HB_SUCCESS )
            errCode = SELF_SKIPFILTER( &pArea->dbfarea.area, 1 );
      }

      ulRecNo = pArea->dbfarea.ulRecNo;

      while( errCode == HB_SUCCESS && ! pArea->dbfarea.area.fEof )
      {
         if( hb_vmRequestQuery() != 0 )
         {
            errCode = HB_FAILURE;
            break;
         }

         if( fDirectRead )
         {
            if( ulRecNo > ulRecCount )
               break;
            if( iRecBuff == 0 || iRecBuff >= iRecBufSize )
            {
               HB_SIZE nSize;

               if( ulRecCount - ulRecNo >= ( HB_ULONG ) iRecBufSize )
                  iRec = iRecBufSize;
               else
                  iRec = ulRecCount - ulRecNo + 1;
               if( ulNextCount > 0 && ulNextCount < ( HB_ULONG ) iRec )
                  iRec = ( int ) ulNextCount;
               nSize = ( HB_SIZE ) iRec * pArea->dbfarea.uiRecordLen;
               if( hb_fileReadAt( pArea->dbfarea.pDataFile, pSort->pBuffIO, nSize,
                                  ( HB_FOFFSET ) pArea->dbfarea.uiHeaderLen +
                                  ( HB_FOFFSET ) ( ulRecNo - 1 ) *
                                  ( HB_FOFFSET ) pArea->dbfarea.uiRecordLen ) != nSize )
               {
                  hb_nsxErrorRT( pTag->pIndex->pArea, EG_READ, EDBF_READ,
                                 pTag->pIndex->IndexName, hb_fsError(), 0, NULL );
                  break;
               }
               iRecBuff = 0;
            }
            pArea->dbfarea.pRecord = pSort->pBuffIO + iRecBuff * pArea->dbfarea.uiRecordLen;
            pArea->dbfarea.ulRecNo = ulRecNo;
            if( SELF_GETREC( &pArea->dbfarea.area, NULL ) == HB_FAILURE )
               break;
            pArea->dbfarea.fValidBuffer = pArea->dbfarea.fPositioned = HB_TRUE;
            pArea->dbfarea.fDeleted = pArea->dbfarea.pRecord[ 0 ] == '*';
            /* Force relational movement in child WorkAreas */
            if( pArea->dbfarea.area.lpdbRelations )
            {
               errCode = SELF_SYNCCHILDREN( &pArea->dbfarea.area );
               if( errCode != HB_SUCCESS )
                  break;
            }
            iRecBuff++;
         }

         if( pWhileItem && ! hb_nsxEvalCond( NULL, pWhileItem, HB_FALSE ) )
            break;

         if( ulRecNo <= ulRecCount &&
             ( pForItem == NULL || hb_nsxEvalCond( pArea, pForItem, HB_FALSE ) ) )
         {
            if( pTag->nField )
               errCode = SELF_GETVALUE( &pArea->dbfarea.area, pTag->nField, pItem );
            else
               pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem );

            switch( hb_itemType( pItem ) )
            {
               case HB_IT_STRING:
               case HB_IT_MEMO:
                  hb_nsxSortKeyAdd( pSort, pArea->dbfarea.ulRecNo,
                                    hb_itemGetCPtr( pItem ),
                                    ( HB_INTCAST ) hb_itemGetCLen( pItem ) );
                  break;

               case HB_IT_INTEGER:
               case HB_IT_LONG:
               case HB_IT_DOUBLE:
                  d = hb_itemGetND( pItem );
                  HB_DBL2ORD( &d, szBuffer );
                  hb_nsxSortKeyAdd( pSort, pArea->dbfarea.ulRecNo, szBuffer, 8 );
                  break;

               case HB_IT_DATE:
               case HB_IT_TIMESTAMP:
                  if( pTag->KeyType == 'T' )
                     d = hb_itemGetTD( pItem );
                  else
                     d = ( double ) hb_itemGetDL( pItem );
                  HB_DBL2ORD( &d, szBuffer );
                  hb_nsxSortKeyAdd( pSort, pArea->dbfarea.ulRecNo, szBuffer, 8 );
                  break;

               case HB_IT_LOGICAL:
                  szBuffer[ 0 ] = hb_itemGetL( pItem ) ? 'T' : 'F';
                  hb_nsxSortKeyAdd( pSort, pArea->dbfarea.ulRecNo, szBuffer, 1 );
                  break;

               default:
                  hb_nsxErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDKEY,
                                 pTag->pIndex->IndexName, 0, 0, NULL );
                  errCode = HB_FAILURE;
                  pTag->Partial = HB_TRUE;
                  pEvalItem = NULL;
                  ulNextCount = 1;
                  break;
            }
         }

         if( ulNextCount > 0 )
         {
            if( --ulNextCount == 0 )
               break;
         }

         if( pEvalItem )
         {
            if( lStep >= pArea->dbfarea.area.lpdbOrdCondInfo->lStep )
            {
               lStep = 0;
               if( ! hb_nsxEvalCond( pArea, pEvalItem, HB_FALSE ) )
               {
                  pTag->Partial = HB_TRUE;
                  break;
               }
            }
            ++lStep;
         }

         if( fDirectRead )
            ulRecNo++;
         else if( errCode == HB_SUCCESS )
         {
            errCode = SELF_SKIPRAW( &pArea->dbfarea.area, 1 );
            if( fUseFilter && errCode == HB_SUCCESS )
               errCode = SELF_SKIPFILTER( &pArea->dbfarea.area, 1 );
            ulRecNo = pArea->dbfarea.ulRecNo;
         }
      }

      if( fDirectRead )
      {
         pArea->dbfarea.pRecord = pSaveRecBuff;
         pArea->dbfarea.fValidBuffer = HB_FALSE;
         if( errCode == HB_SUCCESS )
            errCode = SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
      }

      if( errCode == HB_SUCCESS )
         hb_nsxSortOut( pSort );

      if( pTag->nField )
         hb_itemRelease( pItem );

      pArea->lpCurTag = pSaveTag;
      hb_cdpSelect( cdpTmp );
   }

   hb_nsxSortFree( pSort, HB_TRUE );
   pArea->pSort = NULL;

   return errCode;
}

/*
 * recreate tags in index file
 */
static HB_ERRCODE hb_nsxReIndex( LPNSXINDEX pIndex )
{
   HB_ERRCODE errCode = HB_FAILURE;

   if( hb_nsxIndexLockWrite( pIndex, HB_FALSE ) )
   {
      int i;

      errCode = HB_SUCCESS;
      hb_nsxIndexTrunc( pIndex );

      for( i = 0; i < pIndex->iTags; i++ )
      {
         LPTAGINFO pTag = pIndex->lpTags[ i ];
         pTag->HeadBlock = pTag->RootBlock = pTag->keyCount = 0;
         pTag->HdrChanged = HB_TRUE;
         errCode = hb_nsxTagCreate( pTag, HB_TRUE );
         if( errCode != HB_SUCCESS )
            break;
      }
      hb_nsxIndexUnLockWrite( pIndex );
   }
   return errCode;
}


/* ************************************************************************* */

/* Implementation of RDD methods */

static HB_ERRCODE hb_nsxGoBottom( NSXAREAP pArea )
{
   HB_ERRCODE retval;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxGoBottom(%p)", ( void * ) pArea ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->lpCurTag )
      return SUPER_GOBOTTOM( &pArea->dbfarea.area );

   if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( ! hb_nsxTagLockRead( pArea->lpCurTag ) )
      return HB_FAILURE;
   hb_nsxTagRefreshScope( pArea->lpCurTag );

   hb_nsxTagGoBottom( pArea->lpCurTag );

   pArea->dbfarea.area.fTop = HB_FALSE;
   pArea->dbfarea.area.fBottom = HB_TRUE;

   if( pArea->lpCurTag->TagEOF )
      retval = SELF_GOTO( &pArea->dbfarea.area, 0 );
   else
   {
      retval = SELF_GOTO( &pArea->dbfarea.area, pArea->lpCurTag->CurKeyInfo->rec );
      if( retval != HB_FAILURE && pArea->dbfarea.fPositioned )
         retval = SELF_SKIPFILTER( &pArea->dbfarea.area, -1 );
   }
   hb_nsxTagUnLockRead( pArea->lpCurTag );

   return retval;
}

static HB_ERRCODE hb_nsxTop( NSXAREAP pArea )
{
   HB_ERRCODE retval;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxTop(%p)", ( void * ) pArea ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->lpCurTag )
      return SUPER_GOTOP( &pArea->dbfarea.area );

   if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
      SELF_FORCEREL( &pArea->dbfarea.area );

   if( ! hb_nsxTagLockRead( pArea->lpCurTag ) )
      return HB_FAILURE;
   hb_nsxTagRefreshScope( pArea->lpCurTag );

   hb_nsxTagGoTop( pArea->lpCurTag );

   pArea->dbfarea.area.fTop = HB_TRUE;
   pArea->dbfarea.area.fBottom = HB_FALSE;

   if( pArea->lpCurTag->TagEOF )
      retval = SELF_GOTO( &pArea->dbfarea.area, 0 );
   else
   {
      retval = SELF_GOTO( &pArea->dbfarea.area, pArea->lpCurTag->CurKeyInfo->rec );
      if( retval != HB_FAILURE && pArea->dbfarea.fPositioned )
         retval = SELF_SKIPFILTER( &pArea->dbfarea.area, 1 );
   }
   hb_nsxTagUnLockRead( pArea->lpCurTag );

   return retval;
}

static HB_ERRCODE hb_nsxSeek( NSXAREAP pArea, HB_BOOL fSoftSeek, PHB_ITEM pItem, HB_BOOL fFindLast )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxSeek(%p, %d, %p, %d)", ( void * ) pArea, fSoftSeek, ( void * ) pItem, fFindLast ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->lpCurTag )
   {
      hb_nsxErrorRT( pArea, EG_NOORDER, EDBF_NOTINDEXED, NULL, 0, EF_CANDEFAULT, NULL );
      return HB_FAILURE;
   }
   else
   {
      LPKEYINFO pKey;
      HB_ERRCODE retval = HB_SUCCESS;
      HB_BOOL  fEOF = HB_FALSE, fLast;
      HB_USHORT uiLen;
      HB_ULONG ulRec;

      if( pArea->dbfarea.lpdbPendingRel && pArea->dbfarea.lpdbPendingRel->isScoped )
         SELF_FORCEREL( &pArea->dbfarea.area );

      pArea->dbfarea.area.fTop = pArea->dbfarea.area.fBottom = HB_FALSE;
      pArea->dbfarea.area.fEof = HB_FALSE;

      fLast = pArea->lpCurTag->fUsrDescend ? ! fFindLast : fFindLast;

      pKey = hb_nsxKeyPutItem( NULL, pItem,
                               fLast ? NSX_MAX_REC_NUM : NSX_IGNORE_REC_NUM,
                               pArea->lpCurTag, HB_TRUE, &uiLen );

      if( ! hb_nsxTagLockRead( pArea->lpCurTag ) )
      {
         hb_nsxKeyFree( pKey );
         return HB_FAILURE;
      }
      hb_nsxTagRefreshScope( pArea->lpCurTag );

      if( hb_nsxTagKeyFind( pArea->lpCurTag, pKey, uiLen ) )
         ulRec = pArea->lpCurTag->CurKeyInfo->rec;
      else
         ulRec = 0;

      if( ( ulRec == 0 && ! fSoftSeek ) || pArea->lpCurTag->TagEOF )
         fEOF = HB_TRUE;
      else
      {
         if( ! hb_nsxInBottomScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo->val ) )
            fEOF = HB_TRUE;
         else if( ! hb_nsxInTopScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo->val ) )
         {
            hb_nsxTagGoTop( pArea->lpCurTag );
            if( pArea->lpCurTag->CurKeyInfo->rec == 0 ||
                pArea->lpCurTag->TagEOF )
               fEOF = HB_TRUE;
         }
      }
      hb_nsxTagUnLockRead( pArea->lpCurTag );
      if( ! fEOF )
      {
         retval = SELF_GOTO( &pArea->dbfarea.area, pArea->lpCurTag->CurKeyInfo->rec );
         if( retval != HB_FAILURE && pArea->dbfarea.fPositioned )
         {
            retval = SELF_SKIPFILTER( &pArea->dbfarea.area, fFindLast ? -1 : 1 );
            if( retval != HB_FAILURE && ulRec && pArea->dbfarea.fPositioned )
            {
               pArea->dbfarea.area.fFound = ( ulRec == pArea->dbfarea.ulRecNo ||
                     hb_nsxValCompare( pArea->lpCurTag, pKey->val, uiLen,
                                       pArea->lpCurTag->CurKeyInfo->val,
                                       pArea->lpCurTag->KeyLength,
                                       NSX_CMP_PREFIX ) == 0 );
               if( ! pArea->dbfarea.area.fFound && ! fSoftSeek )
                  fEOF = HB_TRUE;
            }
         }
      }
      if( retval != HB_FAILURE && ( fEOF ||
          ! hb_nsxKeyInScope( pArea->lpCurTag, pArea->lpCurTag->CurKeyInfo ) ) )
      {
         retval = SELF_GOTO( &pArea->dbfarea.area, 0 );
      }
      if( pArea->dbfarea.fPositioned || pArea->dbfarea.ulRecNo != 1 )
         pArea->dbfarea.area.fBof = HB_FALSE;
      hb_nsxKeyFree( pKey );
      return retval;
   }
}

static HB_ERRCODE hb_nsxSkipRaw( NSXAREAP pArea, HB_LONG lToSkip )
{
   HB_ERRCODE retval;
   HB_BOOL fOut = HB_FALSE, fForward;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxSkipRaw(%p, %ld)", ( void * ) pArea, lToSkip ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   if( ! pArea->lpCurTag || lToSkip == 0 )
      return SUPER_SKIPRAW( &pArea->dbfarea.area, lToSkip );

   if( ! hb_nsxTagLockRead( pArea->lpCurTag ) )
      return HB_FAILURE;
   hb_nsxTagRefreshScope( pArea->lpCurTag );

   fForward = ( lToSkip > 0 );

   if( ! hb_nsxCurKeyRefresh( pArea->lpCurTag ) )
   {
      if( fForward || pArea->dbfarea.fPositioned )
         fOut = HB_TRUE;
      else
      {
         hb_nsxTagGoBottom( pArea->lpCurTag );
         fOut = pArea->lpCurTag->TagEOF;
         lToSkip++;
      }
   }

   if( fForward )
   {
      while( ! fOut && ! pArea->lpCurTag->TagEOF && lToSkip-- > 0 )
      {
         hb_nsxTagSkipNext( pArea->lpCurTag );
      }
      retval = SELF_GOTO( &pArea->dbfarea.area,
                                    ( pArea->lpCurTag->TagEOF || fOut ) ? 0 :
                                    pArea->lpCurTag->CurKeyInfo->rec );
   }
   else /* if( lToSkip < 0 ) */
   {
      while( ! fOut && ! pArea->lpCurTag->TagBOF && lToSkip++ < 0 )
      {
         hb_nsxTagSkipPrev( pArea->lpCurTag );
      }
      if( fOut || pArea->lpCurTag->TagBOF )
      {
         hb_nsxTagGoTop( pArea->lpCurTag );
         fOut = HB_TRUE;
      }
      retval = SELF_GOTO( &pArea->dbfarea.area, pArea->lpCurTag->TagEOF ? 0 :
                                          pArea->lpCurTag->CurKeyInfo->rec );
      pArea->dbfarea.area.fBof = fOut;
   }

   hb_nsxTagUnLockRead( pArea->lpCurTag );
   /* Update Bof and Eof flags */
#if 0
   if( fForward )
      pArea->dbfarea.area.fBof = HB_FALSE;
   else
      pArea->dbfarea.area.fEof = HB_FALSE;
#endif
   return retval;
}

/*
 * Flush _system_ buffers to disk
 */
static HB_ERRCODE hb_nsxFlush( NSXAREAP pArea )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxFlush(%p)", ( void * ) pArea ) );

   errCode = SELF_GOCOLD( &pArea->dbfarea.area );
   if( errCode == HB_SUCCESS )
   {
      errCode = SUPER_FLUSH( &pArea->dbfarea.area );

      if( hb_setGetHardCommit() )
      {
         LPNSXINDEX pIndex = pArea->lpIndexes;
         while( pIndex )
         {
            if( pIndex->fFlush && ! pIndex->fDelete )
            {
               hb_fileCommit( pIndex->pFile );
               pIndex->fFlush = HB_FALSE;
            }
            pIndex = pIndex->pNext;
         }
      }
   }

   return errCode;
}

/*
 * Perform a write of WorkArea memory to the data store.
 */
static HB_ERRCODE hb_nsxGoCold( NSXAREAP pArea )
{
   HB_BOOL fRecordChanged = pArea->dbfarea.fRecordChanged;
   HB_BOOL fAppend = pArea->dbfarea.fAppend;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxGoCold(%p)", ( void * ) pArea ) );

   if( SUPER_GOCOLD( &pArea->dbfarea.area ) == HB_SUCCESS )
   {
      if( fRecordChanged || pArea->fIdxAppend )
      {
         if( fAppend && pArea->dbfarea.fShared )
         {
            if( pArea->fIdxAppend )
               hb_errInternal( 9999, "hb_nsxGoCold: multiple appending without GOCOLD.", NULL, NULL );
            pArea->fIdxAppend = HB_TRUE;
         }
         else
         {
            LPNSXINDEX pIndex = pArea->lpIndexes;
            LPTAGINFO pTag;
            LPKEYINFO pKey;
            HB_BOOL fAdd, fDel, fLck = HB_FALSE;
            int i;

            /* The pending relation may move the record pointer so we should
               disable them for KEY/FOR evaluation */
            LPDBRELINFO lpdbPendingRel = pArea->dbfarea.lpdbPendingRel;
            pArea->dbfarea.lpdbPendingRel = NULL;

            if( pArea->dbfarea.fShared )
            {
               fAppend = pArea->fIdxAppend;
               pArea->fIdxAppend = HB_FALSE;
            }

            while( pIndex )
            {
               for( i = 0; i < pIndex->iTags; i++ )
               {
                  pTag = pIndex->lpTags[ i ];
                  if( pIndex->fReadonly || pTag->Custom || ! pTag->HeadBlock ||
                      ( fAppend && pTag->ChgOnly ) )
                     continue;

                  pKey = hb_nsxEvalKey( NULL, pTag );

                  fAdd = ( pTag->pForItem == NULL ||
                           hb_nsxEvalCond( pArea, pTag->pForItem, HB_TRUE ) );
                  if( fAppend )
                  {
                     fDel = HB_FALSE;
                  }
                  else
                  {
                     if( hb_nsxValCompare( pTag, pKey->val, pTag->KeyLength,
                                           pTag->HotKeyInfo->val, pTag->KeyLength,
                                           NSX_CMP_EXACT ) == 0 )
                     {
                        if( pTag->HotFor ? fAdd : ! fAdd )
                           fAdd = fDel = HB_FALSE;
                        else
                           fDel = ! fAdd;
                     }
                     else
                     {
                        fDel = pTag->HotFor || pTag->Partial;
                     }
                  }
                  if( fDel || fAdd )
                  {
                     if( ! fLck )
                     {
                        if( ! hb_nsxIndexLockWrite( pIndex, HB_TRUE ) )
                        {
                           hb_nsxKeyFree( pKey );
                           break;
                        }
                        fLck = HB_TRUE;
                     }
                     if( ! pTag->HeadBlock || ! hb_nsxTagHeaderCheck( pTag ) )
                        fAdd = fDel = HB_FALSE;
                     if( fDel )
                     {
                        if( hb_nsxTagKeyDel( pTag, pTag->HotKeyInfo ) )
                        {
                           if( ! pIndex->fShared && pTag->keyCount &&
                               hb_nsxKeyInScope( pTag, pTag->HotKeyInfo ) )
                              pTag->keyCount--;
                        }
                        else
                        {
                           if( pTag->ChgOnly )
                              fAdd = HB_FALSE;
                           else if( ! pTag->Partial && ! pTag->UniqueKey )
                              hb_nsxCorruptError( pTag->pIndex );
                        }
                     }
                     if( fAdd )
                     {
                        if( hb_nsxTagKeyAdd( pTag, pKey ) )
                        {
                           if( ! pIndex->fShared && pTag->keyCount &&
                               hb_nsxKeyInScope( pTag, pKey ) )
                              pTag->keyCount++;
                        }
                     }
                  }
                  hb_nsxKeyFree( pKey );
               }
               if( fLck )
               {
                  hb_nsxIndexUnLockWrite( pIndex );
                  fLck = HB_FALSE;
               }
               pIndex = pIndex->pNext;
            }

            /* Restore disabled pending relation */
            pArea->dbfarea.lpdbPendingRel = lpdbPendingRel;
         }
      }
      return HB_SUCCESS;
   }
   return HB_FAILURE;
}

/*
 * Mark the WorkArea data buffer as hot.
 */
static HB_ERRCODE hb_nsxGoHot( NSXAREAP pArea )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxGoHot(%p)", ( void * ) pArea ) );

   errCode = SUPER_GOHOT( &pArea->dbfarea.area );
   if( errCode == HB_SUCCESS )
   {
      if( ! pArea->fIdxAppend )
      {
         LPNSXINDEX pIndex = pArea->lpIndexes;
         LPTAGINFO pTag;
         int i;

         while( pIndex )
         {
            if( ! pIndex->fReadonly )
            {
               for( i = 0; i < pIndex->iTags; i++ )
               {
                  pTag = pIndex->lpTags[ i ];
                  if( ! pTag->Custom )
                  {
                     pTag->HotKeyInfo = hb_nsxEvalKey( pTag->HotKeyInfo, pTag );
                     pTag->HotFor = ( pTag->pForItem == NULL ||
                                 hb_nsxEvalCond( pArea, pTag->pForItem, HB_TRUE ) );
                  }
               }
            }
            pIndex = pIndex->pNext;
         }
      }
      return HB_SUCCESS;
   }
   return errCode;
}

/*
 * Close the table in the WorkArea.
 */
static HB_ERRCODE hb_nsxClose( NSXAREAP pArea )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxClose(%p)", ( void * ) pArea ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   errCode = SUPER_CLOSE( &pArea->dbfarea.area );

   if( errCode == HB_SUCCESS )
   {
      if( pArea->pSort )
      {
         hb_nsxSortFree( pArea->pSort, HB_TRUE );
         pArea->pSort = NULL;
      }

      SELF_ORDLSTCLEAR( &pArea->dbfarea.area );

      /* close also production indexes if any */
      while( pArea->lpIndexes )
      {
         LPNSXINDEX pIndex = pArea->lpIndexes;
         pArea->lpIndexes = pIndex->pNext;
         hb_nsxIndexFree( pIndex );
      }
   }

   return errCode;
}

/*
 * Retrieve the size of the WorkArea structure.
 */
static HB_ERRCODE hb_nsxStructSize( NSXAREAP pArea, HB_USHORT * uiSize )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxStructSize(%p, %p)", ( void * ) pArea, ( void * ) uiSize ) );
   HB_SYMBOL_UNUSED( pArea );

   *uiSize = sizeof( NSXAREA );
   return HB_SUCCESS;
}

/*
 * Open a data store in the WorkArea.
 */
static HB_ERRCODE hb_nsxOpen( NSXAREAP pArea, LPDBOPENINFO pOpenInfo )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOpen(%p, %p)", ( void * ) pArea, ( void * ) pOpenInfo ) );

   if( ! pArea->dbfarea.bLockType )
   {
      PHB_ITEM pItem = hb_itemNew( NULL );
      errCode = SELF_INFO( &pArea->dbfarea.area, DBI_LOCKSCHEME, pItem );
      if( errCode != HB_SUCCESS )
      {
         hb_itemRelease( pItem );
         return errCode;
      }
      pArea->dbfarea.bLockType = ( HB_BYTE ) hb_itemGetNI( pItem );
      hb_itemRelease( pItem );
      if( pArea->dbfarea.bLockType == 0 )
         pArea->dbfarea.bLockType = DB_DBFLOCK_CLIPPER;
   }

   errCode = SUPER_OPEN( &pArea->dbfarea.area, pOpenInfo );

   if( errCode == HB_SUCCESS && ( DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct ?
                               pArea->dbfarea.fHasTags : hb_setGetAutOpen() ) )
   {
      char szFileName[ HB_PATH_MAX ];

      hb_nsxCreateFName( pArea, NULL, NULL, szFileName, NULL );
      /* CL5.2 DBFCDX and Six3 CDX/NSX RDDs looking for
         production indexes respect SET PATH but Harbour in
         core DBF* index RDDs is CL5.3/COMIX compatible and
         looks for production indexes only in the directory
         where DBF file is located and only SIXCDX Harbour
         RDD is CL5.2/Six3 compatible [druzus] */
      if( hb_fileExists( szFileName, NULL ) ||
          DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct )
      {
         DBORDERINFO pOrderInfo;

         pOrderInfo.itmResult = hb_itemPutNI( NULL, 0 );
         pOrderInfo.atomBagName = hb_itemPutC( NULL, szFileName );
         pOrderInfo.itmNewVal = NULL;
         pOrderInfo.itmOrder  = NULL;
         errCode = SELF_ORDLSTADD( &pArea->dbfarea.area, &pOrderInfo );
         if( errCode == HB_SUCCESS )
         {
            pOrderInfo.itmOrder  = hb_itemPutNI( NULL, hb_setGetAutOrder() );
            errCode = SELF_ORDLSTFOCUS( &pArea->dbfarea.area, &pOrderInfo );
            hb_itemRelease( pOrderInfo.itmOrder );
            if( errCode == HB_SUCCESS )
               errCode = SELF_GOTOP( &pArea->dbfarea.area );
         }
         hb_itemRelease( pOrderInfo.atomBagName );
         hb_itemRelease( pOrderInfo.itmResult );
      }
   }

   return errCode;
}

static HB_ERRCODE hb_nsxPack( NSXAREAP pArea )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxPack(%p)", ( void * ) pArea ) );

   errCode = SUPER_PACK( &pArea->dbfarea.area );
   if( errCode == HB_SUCCESS )
      return SELF_ORDLSTREBUILD( &pArea->dbfarea.area );

   return errCode;
}

static HB_ERRCODE hb_nsxZap( NSXAREAP pArea )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxZap(%p)", ( void * ) pArea ) );

   errCode = SUPER_ZAP( &pArea->dbfarea.area );
   if( errCode == HB_SUCCESS )
      return SELF_ORDLSTREBUILD( &pArea->dbfarea.area );

   return errCode;
}

static HB_ERRCODE hb_nsxOrderCreate( NSXAREAP pArea, LPDBORDERCREATEINFO pOrderInfo )
{
   PHB_ITEM pResult, pKeyExp, pForExp = NULL;
   int iLen, iTag;
   char szFileName[ HB_PATH_MAX ], szTagName[ NSX_TAGNAME + 1 ];
   const char * szKey, * szFor = NULL;
   LPNSXINDEX pIndex, * pIndexPtr;
   LPTAGINFO pTag = NULL;
   HB_ERRCODE errCode;
   HB_ULONG ulRecNo;
   HB_BOOL fNewFile, fProd, fLocked = HB_FALSE, fAscend = HB_TRUE, fCustom = HB_FALSE,
        fTemporary = HB_FALSE, fExclusive = HB_FALSE;
   HB_BYTE bType, bTrail;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderCreate(%p, %p)", ( void * ) pArea, ( void * ) pOrderInfo ) );

   errCode = SELF_GOCOLD( &pArea->dbfarea.area );
   if( errCode != HB_SUCCESS )
      return errCode;

   if( pArea->dbfarea.lpdbPendingRel )
   {
      errCode = SELF_FORCEREL( &pArea->dbfarea.area );
      if( errCode != HB_SUCCESS )
         return errCode;
   }

   szKey = hb_itemGetCPtr( pOrderInfo->abExpr );
   /* If we have a codeblock for the expression, use it */
   if( pOrderInfo->itmCobExpr )
      pKeyExp = hb_itemNew( pOrderInfo->itmCobExpr );
   else /* Otherwise, try compiling the key expression string */
   {
      errCode = SELF_COMPILE( &pArea->dbfarea.area, szKey );
      if( errCode != HB_SUCCESS )
         return errCode;
      pKeyExp = pArea->dbfarea.area.valResult;
      pArea->dbfarea.area.valResult = NULL;
   }

   /* Get a blank record before testing expression */
   ulRecNo = pArea->dbfarea.ulRecNo;
   errCode = SELF_GOTO( &pArea->dbfarea.area, 0 );
   if( errCode != HB_SUCCESS )
      return errCode;

   errCode = SELF_EVALBLOCK( &pArea->dbfarea.area, pKeyExp );
   if( errCode != HB_SUCCESS )
   {
      hb_vmDestroyBlockOrMacro( pKeyExp );
      SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
      return errCode;
   }
   pResult = pArea->dbfarea.area.valResult;
   pArea->dbfarea.area.valResult = NULL;

   bTrail = '\0';
   bType = hb_nsxItemType( pResult );
   switch( bType )
   {
      case 'N':
      case 'D':
      case 'T':
         iLen = 8;
         break;
      case 'L':
         iLen = 1;
         break;
      case 'C':
         iLen = ( HB_INTCAST ) hb_itemGetCLen( pResult );
         if( iLen > NSX_MAXKEYLEN )
            iLen = NSX_MAXKEYLEN;
         bTrail = ' ';
         break;
      default:
         bType = 'U';
         iLen = 0;
   }
   hb_itemRelease( pResult );

   /* Make sure KEY has proper type and iLen is not 0 */
   if( iLen == 0 )
   {
      hb_vmDestroyBlockOrMacro( pKeyExp );
      SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
      hb_nsxErrorRT( pArea, bType == 'U' ? EG_DATATYPE : EG_DATAWIDTH,
                     EDBF_INVALIDKEY, NULL, 0, 0, NULL );
      return HB_FAILURE;
   }

   if( pArea->dbfarea.area.lpdbOrdCondInfo )
   {
      fAscend = ! pArea->dbfarea.area.lpdbOrdCondInfo->fDescending;
      fCustom = pArea->dbfarea.area.lpdbOrdCondInfo->fCustom;
      fTemporary = pArea->dbfarea.area.lpdbOrdCondInfo->fTemporary;
      fExclusive = pArea->dbfarea.area.lpdbOrdCondInfo->fExclusive;
      /* Check conditional expression */
      szFor = pArea->dbfarea.area.lpdbOrdCondInfo->abFor;
      if( pArea->dbfarea.area.lpdbOrdCondInfo->itmCobFor )
         /* If we have a codeblock for the conditional expression, use it */
         pForExp = hb_itemNew( pArea->dbfarea.area.lpdbOrdCondInfo->itmCobFor );
      else if( szFor )
      {
         /* Otherwise, try compiling the conditional expression string */
         errCode = SELF_COMPILE( &pArea->dbfarea.area, szFor );
         if( errCode != HB_SUCCESS )
         {
            hb_vmDestroyBlockOrMacro( pKeyExp );
            SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
            return errCode;
         }
         pForExp = pArea->dbfarea.area.valResult;
         pArea->dbfarea.area.valResult = NULL;
      }
   }

   if( pArea->dbfarea.fTemporary )
      fTemporary = HB_TRUE;

   /* Test conditional expression */
   if( pForExp )
   {
      HB_BOOL fOK;

      errCode = SELF_EVALBLOCK( &pArea->dbfarea.area, pForExp );
      if( errCode != HB_SUCCESS )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         hb_vmDestroyBlockOrMacro( pForExp );
         SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
         return errCode;
      }
      fOK = hb_itemType( pArea->dbfarea.area.valResult ) & HB_IT_LOGICAL;
      hb_itemRelease( pArea->dbfarea.area.valResult );
      pArea->dbfarea.area.valResult = NULL;
      if( ! fOK )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         hb_vmDestroyBlockOrMacro( pForExp );
         SELF_GOTO( &pArea->dbfarea.area, ulRecNo );
         hb_nsxErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDFOR, NULL, 0, 0, NULL );
         return HB_FAILURE;
      }
   }

   SELF_GOTO( &pArea->dbfarea.area, ulRecNo );

   /*
    * abBagName -> cBag, atomBagName -> cTag
    * The following scheme implemented:
    * 1. abBagName == NULL   -> add the Tag to the structural index
    * 2. atomBagName == NULL -> overwrite any index file of abBagName
    * 3. add the Tag to index file
    */
   fNewFile = ! pOrderInfo->atomBagName || ! pOrderInfo->atomBagName[ 0 ];
   hb_nsxCreateFName( pArea, pOrderInfo->abBagName,
                      &fProd, szFileName, szTagName );
   if( ! fNewFile )
      hb_strncpyUpperTrim( szTagName, pOrderInfo->atomBagName, NSX_TAGNAME );

   pIndex = hb_nsxFindBag( pArea, szFileName );
   if( pIndex )
   {
      if( pIndex->fReadonly )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         if( pForExp != NULL )
            hb_vmDestroyBlockOrMacro( pForExp );
         hb_nsxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pIndex->IndexName, 0, 0, NULL );
         return HB_FAILURE;
      }
#if 0 /* enable this code if you want to forbid tag deleting in shared mode */
      else if( pIndex->fShared )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         if( pForExp != NULL )
            hb_vmDestroyBlockOrMacro( pForExp );
         hb_nsxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pIndex->IndexName, 0, 0, NULL );
         return HB_FAILURE;
      }
#endif
   }
   else
   {
      PHB_FILE pFile;
      HB_BOOL bRetry, fShared = pArea->dbfarea.fShared && ! fTemporary && ! fExclusive;
      PHB_ITEM pError = NULL;
      char szSpFile[ HB_PATH_MAX ];

      do
      {
         if( fTemporary )
         {
            pFile = hb_fileCreateTemp( NULL, NULL, FC_NORMAL, szSpFile );
            fNewFile = HB_TRUE;
         }
         else
         {
            pFile = hb_fileExtOpen( szFileName, NULL, FO_READWRITE |
                                    ( fShared ? FO_DENYNONE : FO_EXCLUSIVE ) |
                                    ( fNewFile ? FXO_TRUNCATE : FXO_APPEND ) |
                                    FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME |
                                    FXO_NOSEEKPOS,
                                    NULL, pError );
         }
         if( ! pFile )
            bRetry = hb_nsxErrorRT( pArea, EG_CREATE, EDBF_CREATE, szFileName,
                                    hb_fsError(), EF_CANRETRY | EF_CANDEFAULT,
                                    &pError ) == E_RETRY;
         else
         {
            bRetry = HB_FALSE;
            if( ! fNewFile )
               fNewFile = ( hb_fileSize( pFile ) == 0 );
         }
      }
      while( bRetry );

      if( pError )
         hb_errRelease( pError );

      if( ! pFile )
      {
         hb_vmDestroyBlockOrMacro( pKeyExp );
         if( pForExp != NULL )
            hb_vmDestroyBlockOrMacro( pForExp );
         return HB_FAILURE;
      }

      pIndex = hb_nsxIndexNew( pArea );
      pIndex->IndexName = hb_strdup( szFileName );
      pIndex->fReadonly = HB_FALSE;
      pIndex->fShared = fShared;
      pIndex->pFile = pFile;
      pIndex->fDelete = fTemporary;
      if( fTemporary )
         pIndex->RealName = hb_strdup( szSpFile );
      else
         pIndex->Production = fProd;

      if( ! fNewFile )
      {
         if( ! hb_nsxIndexLockWrite( pIndex, HB_TRUE ) )
            errCode = HB_FAILURE;
         else
         {
            errCode = hb_nsxIndexLoad( pIndex );
            if( errCode != HB_SUCCESS )
               hb_nsxIndexUnLockWrite( pIndex );
            else
               fLocked = HB_TRUE;
         }
         if( errCode != HB_SUCCESS )
         {
            hb_nsxIndexFree( pIndex );
            hb_vmDestroyBlockOrMacro( pKeyExp );
            if( pForExp != NULL )
               hb_vmDestroyBlockOrMacro( pForExp );
            hb_nsxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, szFileName, 0, 0, NULL );
            return errCode;
         }
      }
      else
      {
         pIndex->LargeFile = ( pIndex->pArea->dbfarea.bLockType == DB_DBFLOCK_HB64 );
      }

      pIndexPtr = &pArea->lpIndexes;
      while( *pIndexPtr )
         pIndexPtr = &( *pIndexPtr )->pNext;
      *pIndexPtr = pIndex;
   }

   if( ! fLocked && ! hb_nsxIndexLockWrite( pIndex, ! fNewFile ) )
   {
      errCode = HB_FAILURE;
   }

   iTag = hb_nsxFindTagByName( pIndex, szTagName );
   if( errCode == HB_SUCCESS && iTag == 0 && pIndex->iTags == NSX_MAXTAGS )
   {
      hb_nsxIndexUnLockWrite( pIndex );
      hb_vmDestroyBlockOrMacro( pKeyExp );
      if( pForExp != NULL )
         hb_vmDestroyBlockOrMacro( pForExp );
      hb_nsxErrorRT( pArea, EG_LIMIT, EDBF_LIMITEXCEEDED, pIndex->IndexName, 0, 0, NULL );
      errCode = HB_FAILURE;
   }

   if( errCode == HB_SUCCESS )
   {
      pTag = hb_nsxTagNew( pIndex, szTagName,
                           szKey, pKeyExp, bType, ( HB_USHORT ) iLen, bTrail,
                           szFor, pForExp,
                           fAscend, pOrderInfo->fUnique, fCustom );
      pTag->Template = hb_nsxIsTemplateFunc( pTag->KeyExpr );
      pTag->Partial = ( pArea->dbfarea.area.lpdbOrdCondInfo && ! pArea->dbfarea.area.lpdbOrdCondInfo->fAll );

      if( fNewFile )
      {
         while( pIndex->iTags )
            hb_nsxTagDelete( pIndex->lpTags[ 0 ] );
         hb_nsxIndexTrunc( pIndex );
         iTag = 0;
      }

      if( iTag )
      {
         pTag->HeadBlock = pIndex->lpTags[ iTag - 1 ]->HeadBlock;
         if( hb_nsxTagHeaderCheck( pIndex->lpTags[ iTag - 1 ] ) &&
             ! hb_nsxTagPagesFree( pIndex->lpTags[ iTag - 1 ],
                                   pIndex->lpTags[ iTag - 1 ]->RootBlock ) )
         {
            errCode = HB_FAILURE;
         }
         else
         {
            pTag->uiNumber = pIndex->lpTags[ iTag - 1 ]->uiNumber;
            hb_nsxTagFree( pIndex->lpTags[ iTag - 1 ] );
            pIndex->lpTags[ iTag - 1 ] = pTag;
         }
      }
      else
      {
         hb_nsxTagAdd( pIndex, pTag );
         hb_nsxIndexTagAdd( pIndex, pTag );
      }

      if( errCode == HB_SUCCESS )
      {
         pIndex->Update = pIndex->Changed = pTag->HdrChanged = HB_TRUE;
         errCode = hb_nsxTagCreate( pTag, HB_FALSE );
      }
      hb_nsxIndexUnLockWrite( pIndex );
   }

   pIndexPtr = &pArea->lpIndexes;
   while( *pIndexPtr && *pIndexPtr != pIndex )
      pIndexPtr = &( *pIndexPtr )->pNext;

   /* It should not happen, reentrance? */
   if( ! *pIndexPtr )
      return HB_FAILURE;

   if( errCode != HB_SUCCESS )
   {
      /* TODO: free only new indexes */
      *pIndexPtr = pIndex->pNext;
      hb_nsxIndexFree( pIndex );
      return errCode;
   }

   if( ! pArea->dbfarea.area.lpdbOrdCondInfo || ! pArea->dbfarea.area.lpdbOrdCondInfo->fAdditive )
   {
      *pIndexPtr = pIndex->pNext;
      pIndex->pNext = NULL;
      SELF_ORDLSTCLEAR( &pArea->dbfarea.area );
      pIndexPtr = &pArea->lpIndexes;
      while( *pIndexPtr )
         pIndexPtr = &( *pIndexPtr )->pNext;
      *pIndexPtr = pIndex;
      pArea->fSetTagNumbers = HB_TRUE;
   }
   if( pIndex->Production && ! pArea->dbfarea.fHasTags &&
       ( DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct || hb_setGetAutOpen() ) )
   {
      pArea->dbfarea.fHasTags = HB_TRUE;
      if( ! pArea->dbfarea.fReadonly && ( pArea->dbfarea.dbfHeader.bHasTags & 0x01 ) == 0 )
         SELF_WRITEDBHEADER( &pArea->dbfarea.area );
   }
   pArea->lpCurTag = pTag;
   SELF_ORDSETCOND( &pArea->dbfarea.area, NULL );
   return SELF_GOTOP( &pArea->dbfarea.area );
}

static HB_ERRCODE hb_nsxOrderDestroy( NSXAREAP pArea, LPDBORDERINFO pOrderInfo )
{
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderDestroy(%p, %p)", ( void * ) pArea, ( void * ) pOrderInfo ) );

   errCode = SELF_GOCOLD( &pArea->dbfarea.area );
   if( errCode != HB_SUCCESS )
      return errCode;

   if( pArea->dbfarea.lpdbPendingRel )
   {
      errCode = SELF_FORCEREL( &pArea->dbfarea.area );
      if( errCode != HB_SUCCESS )
         return errCode;
   }

   if( pOrderInfo->itmOrder )
   {
      LPTAGINFO pTag = hb_nsxFindTag( pArea, pOrderInfo->itmOrder, pOrderInfo->atomBagName );

      if( pTag )
      {
         LPNSXINDEX pIndex = pTag->pIndex;

         if( pIndex->iTags == 1 )
         {
            HB_BOOL fProd = pIndex->Production;
            LPNSXINDEX * pIndexPtr = &pArea->lpIndexes;
            while( *pIndexPtr != pIndex )
               pIndexPtr = &( *pIndexPtr )->pNext;
            *pIndexPtr      = pIndex->pNext;
            pIndex->fDelete = HB_TRUE;
            hb_nsxIndexFree( pIndex );
            if( fProd && pArea->dbfarea.fHasTags &&
                ( DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct || hb_setGetAutOpen() ) )
            {
               pArea->dbfarea.fHasTags = HB_FALSE;
               if( ! pArea->dbfarea.fReadonly && ( pArea->dbfarea.dbfHeader.bHasTags & 0x01 ) != 0 )
                  SELF_WRITEDBHEADER( &pArea->dbfarea.area );
            }
         }
         else if( pIndex->fReadonly )
         {
            hb_nsxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pIndex->IndexName, 0, 0, NULL );
            return HB_FAILURE;
         }
#if 0    /* enable this code if you want to forbid tag deleting in shared mode */
         else if( pIndex->fShared )
         {
            hb_nsxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pIndex->IndexName, 0, 0, NULL );
            return HB_FAILURE;
         }
#endif
         else if( ! hb_nsxIndexLockWrite( pIndex, HB_TRUE ) )
         {
            return HB_FAILURE;
         }
         else
         {
            errCode = hb_nsxTagSpaceFree( pTag );
            hb_nsxIndexUnLockWrite( pIndex );
         }
      }
   }

   return errCode;
}

static HB_ERRCODE hb_nsxOrderInfo( NSXAREAP pArea, HB_USHORT uiIndex, LPDBORDERINFO pInfo )
{
   LPTAGINFO pTag;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderInfo(%p, %hu, %p)", ( void * ) pArea, uiIndex, ( void * ) pInfo ) );

   switch( uiIndex )
   {
      case DBOI_STRICTREAD:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_STRICTREAD, 0, pInfo->itmResult );
      case DBOI_OPTIMIZE:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_OPTIMIZE, 0, pInfo->itmResult );
      case DBOI_AUTOOPEN:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_AUTOOPEN, 0, pInfo->itmResult );
      case DBOI_AUTOORDER:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_AUTOORDER, 0, pInfo->itmResult );
      case DBOI_AUTOSHARE:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_AUTOSHARE, 0, pInfo->itmResult );
      case DBOI_BAGEXT:
         pInfo->itmResult = hb_itemPutNil( pInfo->itmResult );
         return SELF_RDDINFO( SELF_RDDNODE( &pArea->dbfarea.area ), RDDI_ORDBAGEXT, 0, pInfo->itmResult );
      case DBOI_EVALSTEP:
         pInfo->itmResult = hb_itemPutNL( pInfo->itmResult,
                  pArea->dbfarea.area.lpdbOrdCondInfo ? pArea->dbfarea.area.lpdbOrdCondInfo->lStep : 0 );
         return HB_SUCCESS;
      case DBOI_KEYSINCLUDED:
         pInfo->itmResult = hb_itemPutNL( pInfo->itmResult,
                       pArea->pSort ? pArea->pSort->ulTotKeys : 0 );
         return HB_SUCCESS;
      case DBOI_I_TAGNAME:
         pInfo->itmResult = hb_itemPutC( pInfo->itmResult,
                      pArea->pSort ? pArea->pSort->pTag->TagName : NULL );
         return HB_SUCCESS;
      case DBOI_I_BAGNAME:
         pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pArea->pSort ?
                      pArea->pSort->pTag->pIndex->IndexName : NULL );
         return HB_SUCCESS;
      case DBOI_ISREINDEX:
         pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                      pArea->pSort ? pArea->pSort->fReindex : HB_FALSE );
         return HB_SUCCESS;
      case DBOI_LOCKOFFSET:
      case DBOI_HPLOCKING:
      {
         HB_DBFLOCKDATA lockData;

         hb_dbfLockIdxGetData( pArea->dbfarea.bLockType, &lockData );
         if( uiIndex == DBOI_LOCKOFFSET )
            pInfo->itmResult = hb_itemPutNInt( pInfo->itmResult, lockData.offset );
         else
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, lockData.size > 0 );
         return HB_SUCCESS;
      }
      case DBOI_ORDERCOUNT:
      {
         int i;

         if( hb_itemGetCLen( pInfo->atomBagName ) > 0 )
         {
            LPNSXINDEX pIndex = hb_nsxFindBag( pArea,
                                       hb_itemGetCPtr( pInfo->atomBagName ) );
            i = pIndex ? pIndex->iTags : 0;
         }
         else
            i = hb_nsxTagCount( pArea );

         pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, i );
         return HB_SUCCESS;
      }
      case DBOI_BAGCOUNT:
      {
         int i = 0;
         LPNSXINDEX pIndex = pArea->lpIndexes;
         while( pIndex )
         {
            ++i;
            pIndex = pIndex->pNext;
         }
         pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, i );
         return HB_SUCCESS;
      }
      case DBOI_BAGNUMBER:
      {
         LPNSXINDEX pIndex = pArea->lpIndexes, pIndexSeek = NULL;
         int i = 0;

         if( hb_itemGetCLen( pInfo->atomBagName ) > 0 )
            pIndexSeek = hb_nsxFindBag( pArea,
                                        hb_itemGetCPtr( pInfo->atomBagName ) );
         else if( pArea->lpCurTag )
            pIndexSeek = pArea->lpCurTag->pIndex;

         if( pIndexSeek )
         {
            do
            {
               ++i;
               if( pIndex == pIndexSeek )
                  break;
               pIndex = pIndex->pNext;
            }
            while( pIndex );
         }
         pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, pIndex ? i : 0 );
         return HB_SUCCESS;
      }
      case DBOI_BAGORDER:
      {
         LPNSXINDEX pIndex = pArea->lpIndexes, pIndexSeek = NULL;
         int i = 0;

         if( hb_itemGetCLen( pInfo->atomBagName ) > 0 )
            pIndexSeek = hb_nsxFindBag( pArea,
                                        hb_itemGetCPtr( pInfo->atomBagName ) );
         else if( pArea->lpCurTag )
            pIndexSeek = pArea->lpCurTag->pIndex;

         if( pIndexSeek )
         {
            ++i;
            do
            {
               if( pIndex == pIndexSeek )
                  break;
               i += pIndex->iTags;
               pIndex = pIndex->pNext;
            }
            while( pIndex );
         }
         pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, pIndex ? i : 0 );
         return HB_SUCCESS;
      }
      case DBOI_RESETPOS:
         return HB_SUCCESS;
   }

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   pTag = hb_nsxFindTag( pArea, pInfo->itmOrder, pInfo->atomBagName );

   if( pTag )
   {
      switch( uiIndex )
      {
         case DBOI_CONDITION:
            pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pTag->ForExpr );
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_STRING )
            {
               const char * szForExpr = hb_itemGetCPtr( pInfo->itmNewVal );
               if( pTag->ForExpr ?
                   strncmp( pTag->ForExpr, szForExpr, NSX_MAXEXPLEN ) != 0 :
                   *szForExpr )
               {
                  PHB_ITEM pForItem = NULL;
                  HB_BOOL fOK = *szForExpr == 0;
                  if( ! fOK )
                  {
                     if( SELF_COMPILE( &pArea->dbfarea.area, szForExpr ) == HB_SUCCESS )
                     {
                        pForItem = pArea->dbfarea.area.valResult;
                        pArea->dbfarea.area.valResult = NULL;
                        if( SELF_EVALBLOCK( &pArea->dbfarea.area, pForItem ) == HB_SUCCESS )
                        {
                           fOK = hb_itemType( pArea->dbfarea.area.valResult ) & HB_IT_LOGICAL;
                           hb_itemRelease( pArea->dbfarea.area.valResult );
                           pArea->dbfarea.area.valResult = NULL;
                        }
                     }
                  }
                  if( fOK && hb_nsxTagLockWrite( pTag ) )
                  {
                     if( pTag->ForExpr )
                        hb_xfree( pTag->ForExpr );
                     if( pTag->pForItem )
                        hb_vmDestroyBlockOrMacro( pTag->pForItem );
                     if( pForItem )
                     {
                        pTag->ForExpr = hb_strndup( szForExpr, NSX_MAXEXPLEN );
                        pTag->pForItem = pForItem;
                        pForItem = NULL;
                     }
                     else
                     {
                        pTag->ForExpr = NULL;
                        pTag->pForItem = NULL;
                     }
                     pTag->Partial = HB_TRUE;
                     pTag->HdrChanged = HB_TRUE;
                     pTag->pIndex->Update = HB_TRUE;
                     hb_nsxTagUnLockWrite( pTag );
                  }
                  if( pForItem )
                     hb_vmDestroyBlockOrMacro( pForItem );
               }
            }
            break;
         case DBOI_EXPRESSION:
            pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pTag->KeyExpr );
            break;
         case DBOI_BAGNAME:
         {
            PHB_FNAME pFileName = hb_fsFNameSplit( pTag->pIndex->IndexName );
            pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pFileName->szName );
            hb_xfree( pFileName );
            break;
         }
         case DBOI_NAME:
            pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pTag->TagName );
            break;
         case DBOI_NUMBER:
            pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, hb_nsxFindTagNum( pArea, pTag ) );
            break;
         case DBOI_FILEHANDLE:
            pInfo->itmResult = hb_itemPutNInt( pInfo->itmResult, ( HB_NHANDLE )
                                               hb_fileHandle( pTag->pIndex->pFile ) );
            break;
         case DBOI_FULLPATH:
            pInfo->itmResult = hb_itemPutC( pInfo->itmResult, pTag->pIndex->IndexName );
            break;
         case DBOI_KEYCOUNT:
         case DBOI_KEYCOUNTRAW:
            pInfo->itmResult = hb_itemPutNL( pInfo->itmResult, hb_nsxOrdKeyCount( pTag ) );
            break;
         case DBOI_POSITION:
         case DBOI_KEYNORAW:
         /* case DBOI_RECNO: */
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                  hb_nsxOrdKeyGoto( pTag, hb_itemGetNL( pInfo->itmNewVal ) ) );
            else
               pInfo->itmResult = hb_itemPutNL( pInfo->itmResult, hb_nsxOrdKeyNo( pTag ) );
            break;
         case DBOI_RELKEYPOS:
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
               hb_nsxOrdSetRelKeyPos( pTag, hb_itemGetND( pInfo->itmNewVal ) );
            else
               pInfo->itmResult = hb_itemPutND( pInfo->itmResult, hb_nsxOrdGetRelKeyPos( pTag ) );
            break;
         case DBOI_ISCOND:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->ForExpr != NULL );
            break;
         case DBOI_ISDESC:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->fUsrDescend );
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL )
               pTag->fUsrDescend = hb_itemGetL( pInfo->itmNewVal );
            break;
         case DBOI_UNIQUE:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->UniqueKey );
            break;
         case DBOI_CUSTOM:
            if( ! pTag->Template &&
                ( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL ) )
            {
               HB_BOOL fNewVal = hb_itemGetL( pInfo->itmNewVal );
               if( pTag->Custom ? ! fNewVal : fNewVal )
               {
                  if( hb_nsxTagLockWrite( pTag ) )
                  {
                     if( ! pTag->Template && ( pTag->Custom ? ! fNewVal : fNewVal ) )
                     {
                        pTag->Custom = fNewVal;
                        pTag->Partial = HB_TRUE;
                        pTag->ChgOnly = HB_FALSE;
                        pTag->HdrChanged = HB_TRUE;
                     }
                     hb_nsxTagUnLockWrite( pTag );
                  }
               }
            }
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->Custom );
            break;
         case DBOI_CHGONLY:
            if( ! pTag->Custom &&
                ( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL ) )
            {
               HB_BOOL fNewVal = hb_itemGetL( pInfo->itmNewVal );
               if( pTag->ChgOnly ? ! fNewVal : fNewVal )
               {
                  if( hb_nsxTagLockWrite( pTag ) )
                  {
                     if( ! pTag->Custom && ( pTag->ChgOnly ? ! fNewVal : fNewVal ) )
                     {
                        pTag->ChgOnly = fNewVal;
                        pTag->Partial = HB_TRUE;
                        pTag->HdrChanged = HB_TRUE;
                     }
                     hb_nsxTagUnLockWrite( pTag );
                  }
               }
            }
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->ChgOnly );
            break;
         case DBOI_TEMPLATE:
            if( pTag->Custom && ! pTag->Template &&
                hb_itemGetL( pInfo->itmNewVal ) )
            {
               if( hb_nsxTagLockWrite( pTag ) )
               {
                  if( pTag->Custom && ! pTag->Template )
                  {
                     pTag->Template = HB_TRUE;
                     pTag->HdrChanged = HB_TRUE;
                  }
                  hb_nsxTagUnLockWrite( pTag );
               }
            }
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->Template );
            break;
         case DBOI_MULTIKEY:
            if( pTag->Custom && ! pTag->MultiKey &&
                hb_itemGetL( pInfo->itmNewVal ) )
            {
               if( hb_nsxTagLockWrite( pTag ) )
               {
                  if( pTag->Custom && ! pTag->MultiKey )
                  {
                     pTag->MultiKey = HB_TRUE;
                     pTag->HdrChanged = HB_TRUE;
                  }
                  hb_nsxTagUnLockWrite( pTag );
               }
            }
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->MultiKey );
            break;
         case DBOI_PARTIAL:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->Partial );
            break;
         case DBOI_SCOPETOP:
            if( pInfo->itmResult )
               hb_nsxTagGetScope( pTag, 0, pInfo->itmResult );
            if( pInfo->itmNewVal )
               hb_nsxTagSetScope( pTag, 0, pInfo->itmNewVal );
            break;
         case DBOI_SCOPEBOTTOM:
            if( pInfo->itmResult )
               hb_nsxTagGetScope( pTag, 1, pInfo->itmResult );
            if( pInfo->itmNewVal )
               hb_nsxTagSetScope( pTag, 1, pInfo->itmNewVal );
            break;
         case DBOI_SCOPESET:
            if( pInfo->itmNewVal )
            {
               hb_nsxTagSetScope( pTag, 0, pInfo->itmNewVal );
               hb_nsxTagSetScope( pTag, 1, pInfo->itmNewVal );
            }
            if( pInfo->itmResult )
               hb_itemClear( pInfo->itmResult );
            break;
         case DBOI_SCOPETOPCLEAR:
            if( pInfo->itmResult )
               hb_nsxTagGetScope( pTag, 0, pInfo->itmResult );
            hb_nsxTagClearScope( pTag, 0 );
            break;
         case DBOI_SCOPEBOTTOMCLEAR:
            if( pInfo->itmResult )
               hb_nsxTagGetScope( pTag, 1, pInfo->itmResult );
            hb_nsxTagClearScope( pTag, 1 );
            break;
         case DBOI_SCOPECLEAR:
            hb_nsxTagClearScope( pTag, 0 );
            hb_nsxTagClearScope( pTag, 1 );
            if( pInfo->itmResult )
               hb_itemClear( pInfo->itmResult );
            break;
         case DBOI_KEYADD:
            if( pTag->pIndex->fReadonly )
            {
               hb_nsxErrorRT( pArea, EG_READONLY, EDBF_READONLY,
                              pTag->pIndex->IndexName, 0, 0, NULL );
               return HB_FAILURE;
            }
            if( pTag->Custom )
            {
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                            hb_nsxOrdKeyAdd( pTag, pInfo->itmNewVal ) );
            }
            else
            {
               hb_nsxErrorRT( pArea, 0, EDBF_NOTCUSTOM, NULL, 0, 0, NULL );
               return HB_FAILURE;
            }
            break;
         case DBOI_KEYDELETE:
            if( pTag->pIndex->fReadonly )
            {
               hb_nsxErrorRT( pArea, EG_READONLY, EDBF_READONLY,
                              pTag->pIndex->IndexName, 0, 0, NULL );
               return HB_FAILURE;
            }
            if( pTag->Custom )
            {
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                            hb_nsxOrdKeyDel( pTag, pInfo->itmNewVal ) );
            }
            else
            {
               hb_nsxErrorRT( pArea, 0, EDBF_NOTCUSTOM, NULL, 0, 0, NULL );
               return HB_FAILURE;
            }
            break;
         case DBOI_KEYTYPE:
            {
               char szType[ 2 ];
               szType[ 0 ] = ( char ) pTag->KeyType;
               szType[ 1 ] = 0;
               pInfo->itmResult = hb_itemPutC( pInfo->itmResult, szType );
            }
            break;
         case DBOI_KEYSIZE:
            pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, pTag->KeyLength );
            break;
         case DBOI_KEYDEC:
         /* there is no fixed number of decimal places for numeric keys
            in NSX format */
            pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, 0 );
            break;
         case DBOI_KEYVAL:
            if( hb_nsxTagLockRead( pTag ) )
            {
               if( hb_nsxCurKeyRefresh( pTag ) )
                  pInfo->itmResult = hb_nsxKeyGetItem( pInfo->itmResult,
                                       pTag->CurKeyInfo, pTag, HB_TRUE );
               else if( pInfo->itmResult )
                  hb_itemClear( pInfo->itmResult );
               hb_nsxTagUnLockRead( pTag );
            }
            break;
         case DBOI_SKIPUNIQUE:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
               hb_nsxOrdSkipUnique( pTag,
                        pInfo->itmNewVal && HB_IS_NUMERIC( pInfo->itmNewVal ) ?
                        hb_itemGetNL( pInfo->itmNewVal ) : 1 ) );
            break;
         case DBOI_SKIPEVAL:
         case DBOI_SKIPEVALBACK:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
               hb_nsxOrdSkipEval( pTag, uiIndex == DBOI_SKIPEVAL, pInfo->itmNewVal ) );
            break;
         case DBOI_SKIPWILD:
         case DBOI_SKIPWILDBACK:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
               hb_nsxOrdSkipWild( pTag, uiIndex == DBOI_SKIPWILD, pInfo->itmNewVal ) );
            break;
         case DBOI_SKIPREGEX:
         case DBOI_SKIPREGEXBACK:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
               hb_nsxOrdSkipRegEx( pTag, uiIndex == DBOI_SKIPREGEX, pInfo->itmNewVal ) );
            break;
         case DBOI_FINDREC:
         case DBOI_FINDRECCONT:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
               hb_nsxOrdFindRec( pTag, hb_itemGetNL( pInfo->itmNewVal ),
                                 uiIndex == DBOI_FINDRECCONT ) );
            break;
         case DBOI_SCOPEEVAL:
            if( ( hb_itemType( pInfo->itmNewVal ) & HB_IT_ARRAY ) &&
                hb_arrayLen( pInfo->itmNewVal ) == DBRMI_SIZE &&
                hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_FUNCTION ) != NULL )
            {
               pInfo->itmResult = hb_itemPutNL( pInfo->itmResult,
                  hb_nsxOrdScopeEval( pTag, ( HB_EVALSCOPE_FUNC )
                     hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_FUNCTION ),
                     hb_arrayGetPtr( pInfo->itmNewVal, DBRMI_PARAM ),
                     hb_arrayGetItemPtr( pInfo->itmNewVal, DBRMI_LOVAL ),
                     hb_arrayGetItemPtr( pInfo->itmNewVal, DBRMI_HIVAL ) ) );
            }
            else
               pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, 0 );
            break;
         case DBOI_UPDATECOUNTER:
            /* refresh update counter */
            if( hb_nsxIndexLockRead( pTag->pIndex ) )
               hb_nsxIndexUnLockRead( pTag->pIndex );
            pInfo->itmResult = hb_itemPutNInt( pInfo->itmResult, pTag->pIndex->Version );
            break;
         case DBOI_READLOCK:
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL )
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                            hb_itemGetL( pInfo->itmNewVal ) ?
                                 hb_nsxIndexLockRead( pTag->pIndex ) :
                                 hb_nsxIndexUnLockRead( pTag->pIndex ) );
            else
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->pIndex->lockRead > 0 );
            break;
         case DBOI_WRITELOCK:
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL )
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult,
                            hb_itemGetL( pInfo->itmNewVal ) ?
                                 hb_nsxIndexLockWrite( pTag->pIndex, HB_TRUE ) :
                                 hb_nsxIndexUnLockWrite( pTag->pIndex ) );
            else
               pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->pIndex->lockWrite > 0 );
            break;
         case DBOI_ISSORTRECNO:
         case DBOI_ISMULTITAG:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, HB_TRUE );
            break;
         case DBOI_LARGEFILE:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->pIndex->LargeFile );
            break;
         case DBOI_SHARED:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->pIndex->fShared );
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_LOGICAL )
               pTag->pIndex->fShared = hb_itemGetL( pInfo->itmNewVal );
            break;
         case DBOI_ISREADONLY:
            pInfo->itmResult = hb_itemPutL( pInfo->itmResult, pTag->pIndex->fReadonly );
            break;
         case DBOI_INDEXTYPE:
            pInfo->itmResult = hb_itemPutNI( pInfo->itmResult,
               pTag->pIndex->iTags > 1 ? DBOI_TYPE_COMPOUND : DBOI_TYPE_COMPACT );
            break;
         case DBOI_INDEXPAGESIZE:
            pInfo->itmResult = hb_itemPutNI( pInfo->itmResult, NSX_PAGELEN );
            break;
      }
   }
   else if( pInfo->itmResult )
   {
      switch( uiIndex )
      {
         case DBOI_KEYCOUNT:
         case DBOI_KEYCOUNTRAW:
         {
            HB_ULONG ulRecCount = 0;
            SELF_RECCOUNT( &pArea->dbfarea.area, &ulRecCount );
            hb_itemPutNInt( pInfo->itmResult, ulRecCount );
            break;
         }
         case DBOI_POSITION:
         case DBOI_KEYNORAW:
         /* case DBOI_RECNO: */
            if( pInfo->itmNewVal && hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
               hb_itemPutL( pInfo->itmResult, SELF_GOTO( &pArea->dbfarea.area,
                              hb_itemGetNL( pInfo->itmNewVal ) ) == HB_SUCCESS );
            else
               SELF_RECID( &pArea->dbfarea.area, pInfo->itmResult );
            break;
         case DBOI_RELKEYPOS:
            if( hb_itemType( pInfo->itmNewVal ) & HB_IT_NUMERIC )
            {
               double dPos = hb_itemGetND( pInfo->itmNewVal );
               LPTAGINFO pSavedTag = pArea->lpCurTag;
               pArea->lpCurTag = NULL;
               if( dPos >= 1.0 )
               {
                  SELF_GOBOTTOM( &pArea->dbfarea.area );
               }
               else if( dPos <= 0.0 )
               {
                  SELF_GOTOP( &pArea->dbfarea.area );
               }
               else
               {
                  HB_ULONG ulRecCount, ulRecNo;
                  SELF_RECCOUNT( &pArea->dbfarea.area, &ulRecCount );
                  ulRecNo = ( HB_ULONG ) dPos * ulRecCount + 1;
                  if( ulRecNo >= ulRecCount )
                     ulRecNo = ulRecCount;
                  if( SELF_GOTO( &pArea->dbfarea.area, ulRecNo ) == HB_SUCCESS &&
                      SELF_SKIPFILTER( &pArea->dbfarea.area, 1 ) == HB_SUCCESS &&
                      pArea->dbfarea.area.fEof )
                     SELF_GOTOP( &pArea->dbfarea.area );
               }
               pArea->lpCurTag = pSavedTag;
            }
            else
            {
               HB_ULONG ulRecNo = 0, ulRecCount = 0;
               double dPos = 0.0;
               /* resolve any pending relations */
               if( SELF_RECNO( &pArea->dbfarea.area, &ulRecNo ) == HB_SUCCESS )
               {
                  if( ! pArea->dbfarea.fPositioned )
                  {
                     if( ulRecNo > 1 )
                        dPos = 1.0;
                  }
                  else
                  {
                     SELF_RECCOUNT( &pArea->dbfarea.area, &ulRecCount );
                     if( ulRecCount != 0 )
                        dPos = ( 0.5 + ulRecNo ) / ulRecCount;
                  }
               }
               hb_itemPutND( pInfo->itmResult, dPos );
            }
            break;
         case DBOI_SKIPUNIQUE:
            hb_itemPutL( pInfo->itmResult, SELF_SKIP( &pArea->dbfarea.area,
                        pInfo->itmNewVal && HB_IS_NUMERIC( pInfo->itmNewVal ) ?
                        hb_itemGetNL( pInfo->itmNewVal ) : 1 ) == HB_SUCCESS );
            break;
         case DBOI_SKIPEVAL:
         case DBOI_SKIPEVALBACK:
         case DBOI_SKIPWILD:
         case DBOI_SKIPWILDBACK:
         case DBOI_SKIPREGEX:
         case DBOI_SKIPREGEXBACK:
         case DBOI_FINDREC:
         case DBOI_FINDRECCONT:
            SELF_GOTO( &pArea->dbfarea.area, 0 );
            hb_itemPutL( pInfo->itmResult, HB_FALSE );
            break;
         case DBOI_ISCOND:
         case DBOI_ISDESC:
         case DBOI_UNIQUE:
         case DBOI_CUSTOM:
         case DBOI_KEYADD:
         case DBOI_KEYDELETE:

         case DBOI_ISSORTRECNO:
         case DBOI_ISMULTITAG:
         case DBOI_LARGEFILE:
         case DBOI_TEMPLATE:
         case DBOI_MULTIKEY:
         case DBOI_PARTIAL:
         case DBOI_CHGONLY:
         case DBOI_SHARED:
         case DBOI_ISREADONLY:
         case DBOI_WRITELOCK:
         case DBOI_READLOCK:
            hb_itemPutL( pInfo->itmResult, HB_FALSE );
            break;
         case DBOI_KEYVAL:
         case DBOI_SCOPETOP:
         case DBOI_SCOPEBOTTOM:
         case DBOI_SCOPESET:
         case DBOI_SCOPETOPCLEAR:
         case DBOI_SCOPEBOTTOMCLEAR:
         case DBOI_SCOPECLEAR:
            hb_itemClear( pInfo->itmResult );
            break;
         case DBOI_KEYSIZE:
         case DBOI_KEYDEC:
         case DBOI_NUMBER:
         case DBOI_ORDERCOUNT:
         case DBOI_SCOPEEVAL:
         case DBOI_UPDATECOUNTER:
         case DBOI_INDEXPAGESIZE:
            hb_itemPutNI( pInfo->itmResult, 0 );
            break;
         case DBOI_FILEHANDLE:
            hb_itemPutNInt( pInfo->itmResult, ( HB_NHANDLE ) FS_ERROR );
            break;
         case DBOI_INDEXTYPE:
            hb_itemPutNI( pInfo->itmResult, DBOI_TYPE_UNDEF );
            break;
         case DBOI_BAGNAME:
         case DBOI_CONDITION:
         case DBOI_EXPRESSION:
         case DBOI_FULLPATH:
         case DBOI_NAME:
         case DBOI_KEYTYPE:
            hb_itemPutC( pInfo->itmResult, NULL );
            break;
         default:
            hb_itemClear( pInfo->itmResult );
      }
   }
   return HB_SUCCESS;
}

static HB_ERRCODE hb_nsxCountScope( NSXAREAP pArea, void * pPtr, HB_LONG * plRecNo )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxCountScope(%p, %p, %p)", ( void * ) pArea, pPtr, ( void * ) plRecNo ) );

   if( pPtr == NULL )
      return HB_SUCCESS;

   return SUPER_COUNTSCOPE( &pArea->dbfarea.area, pPtr, plRecNo );
}

static HB_ERRCODE hb_nsxOrderListAdd( NSXAREAP pArea, LPDBORDERINFO pOrderInfo )
{
   PHB_FILE pFile;
   char szFileName[ HB_PATH_MAX ];
   LPNSXINDEX pIndex;
   HB_ERRCODE errCode;
   HB_BOOL fProd;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderListAdd(%p, %p)", ( void * ) pArea, ( void * ) pOrderInfo ) );

   errCode = SELF_GOCOLD( &pArea->dbfarea.area );
   if( errCode != HB_SUCCESS )
      return errCode;

   if( hb_itemGetCLen( pOrderInfo->atomBagName ) == 0 )
      return HB_FAILURE;

   hb_nsxCreateFName( pArea, hb_itemGetCPtr( pOrderInfo->atomBagName ),
                      &fProd, szFileName, NULL );

   pIndex = hb_nsxFindBag( pArea, szFileName );

   if( ! pIndex )
   {
      PHB_ITEM pError = NULL;
      LPNSXINDEX * pIndexPtr;
      HB_BOOL fRetry, fReadonly, fShared;

      fReadonly = pArea->dbfarea.fReadonly;
      fShared = pArea->dbfarea.fShared;
      do
      {
         fRetry = HB_FALSE;
         pFile = hb_fileExtOpen( szFileName, NULL,
                                 ( fReadonly ? FO_READ : FO_READWRITE ) |
                                 ( fShared ? FO_DENYNONE : FO_EXCLUSIVE ) |
                                 FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME |
                                 FXO_NOSEEKPOS,
                                 NULL, pError );
         if( ! pFile )
         {
            fRetry = hb_nsxErrorRT( pArea, EG_OPEN, EDBF_OPEN_INDEX, szFileName,
                                    hb_fsError(), EF_CANRETRY | EF_CANDEFAULT,
                                    &pError ) == E_RETRY;
         }
      }
      while( fRetry );

      if( pError )
         hb_errRelease( pError );

      if( ! pFile )
         return HB_FAILURE;

      pIndex = hb_nsxIndexNew( pArea );
      pIndex->IndexName = hb_strdup( szFileName );
      pIndex->fReadonly = fReadonly;
      pIndex->fShared = fShared;
      pIndex->pFile = pFile;
      pIndex->Production = fProd;

      if( hb_nsxIndexLockRead( pIndex ) )
      {
         errCode = hb_nsxIndexLoad( pIndex );
         hb_nsxIndexUnLockRead( pIndex );
      }
      else
         errCode = HB_FAILURE;

      if( errCode != HB_SUCCESS )
      {
         hb_nsxIndexFree( pIndex );
         hb_nsxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, szFileName, 0, 0, NULL );
         return errCode;
      }

      pIndexPtr = &pArea->lpIndexes;
      while( *pIndexPtr )
         pIndexPtr = &( *pIndexPtr )->pNext;
      *pIndexPtr = pIndex;
   }

   if( ! pArea->lpCurTag && pIndex->iTags )
   {
      pArea->lpCurTag = pIndex->lpTags[ 0 ];
      errCode = SELF_GOTOP( &pArea->dbfarea.area );
   }
   return errCode;
}

static HB_ERRCODE hb_nsxOrderListClear( NSXAREAP pArea )
{
   LPNSXINDEX * pIndexPtr, pIndex;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderListClear(%p)", ( void * ) pArea ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   pArea->lpCurTag = NULL;
   pIndexPtr = &pArea->lpIndexes;
   while( *pIndexPtr )
   {
      pIndex = *pIndexPtr;
      if( pIndex->Production && ( DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct ?
                                  pArea->dbfarea.fHasTags : hb_setGetAutOpen() ) )
      {
         pIndexPtr = &pIndex->pNext;
      }
      else
      {
         *pIndexPtr = pIndex->pNext;
         hb_nsxIndexFree( pIndex );
      }
   }
   return HB_SUCCESS;
}

static HB_ERRCODE hb_nsxOrderListDelete( NSXAREAP pArea, LPDBORDERINFO pOrderInfo )
{
   char szTagName[ NSX_TAGNAME + 1 ];
   char szFileName[ HB_PATH_MAX ];
   LPNSXINDEX pIndex;
   HB_BOOL fProd;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderListDelete(%p, %p)", ( void * ) pArea, ( void * ) pOrderInfo ) );

   if( SELF_GOCOLD( &pArea->dbfarea.area ) == HB_FAILURE )
      return HB_FAILURE;

   hb_nsxCreateFName( pArea, hb_itemGetCPtr( pOrderInfo->atomBagName ), &fProd,
                      szFileName, szTagName );
   pIndex = hb_nsxFindBag( pArea, szFileName );

   if( pIndex && ! ( pIndex->Production &&
                     ( DBFAREA_DATA( &pArea->dbfarea )->fStrictStruct ?
                       pArea->dbfarea.fHasTags : hb_setGetAutOpen() ) ) )
   {
      LPNSXINDEX * pIndexPtr = &pArea->lpIndexes;
      while( *pIndexPtr )
      {
         if( pIndex == *pIndexPtr )
         {
            *pIndexPtr = pIndex->pNext;
            hb_nsxIndexFree( pIndex );
            break;
         }
         pIndexPtr = &( *pIndexPtr )->pNext;
      }
   }
   return HB_SUCCESS;
}

static HB_ERRCODE hb_nsxOrderListFocus( NSXAREAP pArea, LPDBORDERINFO pOrderInfo )
{
   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderListFocus(%p, %p)", ( void * ) pArea, ( void * ) pOrderInfo ) );

   pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult,
                             pArea->lpCurTag ? pArea->lpCurTag->TagName : NULL );

   if( pOrderInfo->itmOrder )
   {
      LPTAGINFO pTag = hb_nsxFindTag( pArea, pOrderInfo->itmOrder,
                                      pOrderInfo->atomBagName );
      /*
       * In Clipper 5.3 DBFCDX (COMIX) when bad name or order is given
       * tag number is set to 0 (natural record order). CL52 RDDs and
       * SIX3 drivers do not change order in such case.
       * I'd like to keep the same behavior in all native [x]Harbour
       * RDDs and I chosen DBFCDX one as default. [druzus]
       */
#ifdef HB_CLP_STRICT
      if( pTag ||
          ( HB_IS_NUMERIC( pOrderInfo->itmOrder ) &&
            hb_itemGetNI( pOrderInfo->itmOrder ) == 0 ) ||
          ( HB_IS_STRING( pOrderInfo->itmOrder ) &&
            hb_itemGetCLen( pOrderInfo->itmOrder ) == 0 ) )
#endif
         pArea->lpCurTag = pTag;
   }

   return HB_SUCCESS;
}

static HB_ERRCODE hb_nsxOrderListRebuild( NSXAREAP pArea )
{
   LPTAGINFO pCurrTag;
   LPNSXINDEX pIndex;
   HB_ERRCODE errCode;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxOrderListRebuild(%p)", ( void * ) pArea ) );

   errCode = SELF_GOCOLD( &pArea->dbfarea.area );
   if( errCode != HB_SUCCESS )
      return errCode;

   if( pArea->dbfarea.fShared )
   {
      hb_nsxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pArea->dbfarea.szDataFileName, 0, 0, NULL );
      return HB_FAILURE;
   }
   if( pArea->dbfarea.fReadonly )
   {
      hb_nsxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pArea->dbfarea.szDataFileName, 0, 0, NULL );
      return HB_FAILURE;
   }

   if( pArea->dbfarea.lpdbPendingRel )
   {
      errCode = SELF_FORCEREL( &pArea->dbfarea.area );
      if( errCode != HB_SUCCESS )
         return errCode;
   }
   pCurrTag = pArea->lpCurTag;
   pArea->lpCurTag = NULL;
   pIndex = pArea->lpIndexes;
   while( pIndex && errCode == HB_SUCCESS )
   {
      errCode = hb_nsxReIndex( pIndex );
      pIndex = pIndex->pNext;
   }
   if( errCode == HB_SUCCESS )
   {
      pArea->lpCurTag = pCurrTag;
      errCode = SELF_GOTOP( &pArea->dbfarea.area );
   }
   return errCode;
}

static HB_ERRCODE hb_nsxRddInfo( LPRDDNODE pRDD, HB_USHORT uiIndex, HB_ULONG ulConnect, PHB_ITEM pItem )
{
   LPDBFDATA pData;

   HB_TRACE( HB_TR_DEBUG, ( "hb_nsxRddInfo(%p, %hu, %lu, %p)", ( void * ) pRDD, uiIndex, ulConnect, ( void * ) pItem ) );

   pData = DBFNODE_DATA( pRDD );

   if( pData->bMemoType == 0 )
   {
      pData->bMemoType = DB_MEMO_SMT;
   }

   switch( uiIndex )
   {
      case RDDI_ORDBAGEXT:
      case RDDI_ORDEREXT:
      case RDDI_ORDSTRUCTEXT:
      {
         const char * szNew = hb_itemGetCPtr( pItem );
         char * szNewVal;

         szNewVal = szNew[ 0 ] == '.' && szNew[ 1 ] ? hb_strdup( szNew ) : NULL;
         hb_itemPutC( pItem, pData->szIndexExt[ 0 ] ? pData->szIndexExt : NSX_INDEXEXT );
         if( szNewVal )
         {
            hb_strncpy( pData->szIndexExt, szNewVal, HB_MAX_FILE_EXT );
            hb_xfree( szNewVal );
         }
         break;
      }

      case RDDI_MULTITAG:
      case RDDI_SORTRECNO:
      case RDDI_STRUCTORD:
         hb_itemPutL( pItem, HB_TRUE );
         break;

      case RDDI_STRICTSTRUCT:
      {
         HB_BOOL fStrictStruct = pData->fStrictStruct;
         if( hb_itemType( pItem ) & HB_IT_LOGICAL )
            pData->fStrictStruct = hb_itemGetL( pItem );
         hb_itemPutL( pItem, fStrictStruct );
         break;
      }

      case RDDI_MULTIKEY:
      {
         HB_BOOL fMultiKey = pData->fMultiKey;
         if( hb_itemType( pItem ) & HB_IT_LOGICAL )
            pData->fMultiKey = hb_itemGetL( pItem );
         hb_itemPutL( pItem, fMultiKey );
         break;
      }

      default:
         return SUPER_RDDINFO( pRDD, uiIndex, ulConnect, pItem );
   }

   return HB_SUCCESS;
}

static const RDDFUNCS nsxTable = {
                             NULL,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxGoBottom,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxTop,
            ( DBENTRYP_BIB ) hb_nsxSeek,
                             NULL,
                             NULL,
              ( DBENTRYP_L ) hb_nsxSkipRaw,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxFlush,
                             NULL,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxGoCold,
              ( DBENTRYP_V ) hb_nsxGoHot,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxClose,
                             NULL,
                             NULL,
                             NULL,
             ( DBENTRYP_VO ) hb_nsxOpen,
                             NULL,
             ( DBENTRYP_SP ) hb_nsxStructSize,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxPack,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
              ( DBENTRYP_V ) hb_nsxZap,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
            ( DBENTRYP_VOI ) hb_nsxOrderListAdd,
              ( DBENTRYP_V ) hb_nsxOrderListClear,
            ( DBENTRYP_VOI ) hb_nsxOrderListDelete,
            ( DBENTRYP_VOI ) hb_nsxOrderListFocus,
              ( DBENTRYP_V ) hb_nsxOrderListRebuild,
                             NULL,
            ( DBENTRYP_VOC ) hb_nsxOrderCreate,
            ( DBENTRYP_VOI ) hb_nsxOrderDestroy,
           ( DBENTRYP_SVOI ) hb_nsxOrderInfo,
                             NULL,
                             NULL,
                             NULL,
           ( DBENTRYP_VPLP ) hb_nsxCountScope,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             NULL,
                             hb_nsxRddInfo,
                             NULL
                           };

HB_FUNC_TRANSLATE( DBFNSX, _DBF )

HB_FUNC_STATIC( DBFNSX_GETFUNCTABLE )
{
   RDDFUNCS * pTable;
   HB_USHORT * puiCount, uiRddId, * puiSuperRddId;

   puiCount = ( HB_USHORT * ) hb_parptr( 1 );
   pTable = ( RDDFUNCS * ) hb_parptr( 2 );
   uiRddId = ( HB_USHORT ) hb_parni( 4 );
   puiSuperRddId = ( HB_USHORT * ) hb_parptr( 5 );

   if( pTable )
   {
      HB_ERRCODE errCode;

      if( puiCount )
         *puiCount = RDDFUNCSCOUNT;
      errCode = hb_rddInheritEx( pTable, &nsxTable, &nsxSuper, "DBFFPT", puiSuperRddId );
      if( errCode != HB_SUCCESS )
         errCode = hb_rddInheritEx( pTable, &nsxTable, &nsxSuper, "DBF", puiSuperRddId );
      if( errCode == HB_SUCCESS )
      {
         /*
          * we successfully register our RDD so now we can initialize it
          * You may think that this place is RDD init statement, Druzus
          */
         s_uiRddId = uiRddId;
      }
      hb_retni( errCode );
   }
   else
   {
      hb_retni( HB_FAILURE );
   }
}

static void hb_dbfnsxRddInit( void * cargo )
{
   HB_SYMBOL_UNUSED( cargo );

   if( hb_rddRegister( "DBF", RDT_FULL ) <= 1 )
   {
      hb_rddRegister( "DBFFPT", RDT_FULL );
      if( hb_rddRegister( "DBFNSX", RDT_FULL ) <= 1 )
         return;
   }

   hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL );
}

HB_INIT_SYMBOLS_BEGIN( dbfnsx1__InitSymbols )
{ "DBFNSX",              {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( DBFNSX )}, NULL },
{ "DBFNSX_GETFUNCTABLE", {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( DBFNSX_GETFUNCTABLE )}, NULL }
HB_INIT_SYMBOLS_END( dbfnsx1__InitSymbols )

HB_CALL_ON_STARTUP_BEGIN( _hb_dbfnsx_rdd_init_ )
   hb_vmAtInit( hb_dbfnsxRddInit, NULL );
HB_CALL_ON_STARTUP_END( _hb_dbfnsx_rdd_init_ )

#if defined( HB_PRAGMA_STARTUP )
   #pragma startup dbfnsx1__InitSymbols
   #pragma startup _hb_dbfnsx_rdd_init_
#elif defined( HB_DATASEG_STARTUP )
   #define HB_DATASEG_BODY    HB_DATASEG_FUNC( dbfnsx1__InitSymbols ) \
                              HB_DATASEG_FUNC( _hb_dbfnsx_rdd_init_ )
   #include "hbiniseg.h"
#endif
