view src/aci2/aci/db.c @ 631:597869e59805

config: introduced new CONFIG_MCSI_MODEM preprocessor symbol All MCSI functionality was previously conditionalized on CONFIG_TARGET_FCMODEM (even earlier it was CONFIG_TARGET_FCDEV3B), but having a dedicated preprocessor symbol for this purpose (defined in targets/*.h as appropriate) makes it much easier to support different modem targets with MCSI enabled.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 04 Jan 2020 19:07:02 +0000
parents 93999a60b835
children
line wrap: on
line source

/* 
+----------------------------------------------------------------------------- 
|  Project :  PHB
|  Modul   :  DBM
+----------------------------------------------------------------------------- 
|  Copyright 2005 Texas Instruments Berlin, AG 
|                 All rights reserved. 
| 
|                 This file is confidential and a trade secret of Texas 
|                 Instruments Berlin, AG 
|                 The receipt of or possession of this file does not convey 
|                 any rights to reproduce or disclose its contents or to 
|                 manufacture, use, or sell anything it may describe, in 
|                 whole, or in part, without the specific written consent of 
|                 Texas Instruments Berlin, AG. 
+----------------------------------------------------------------------------- 
|  Purpose :  Implementation of DBM functions
+----------------------------------------------------------------------------- 
*/ 

#ifdef TI_PS_FFS_PHB

#include "db_int.h"
#include <string.h>

/* For DB Testing */

/* Check if we are closing FFS files properly */
#define DB_TEST

/* A simple check to make sure that all allocated 
   memory is freed to avoid memory leak */
#define DB_MEMORY_CHECK

/* if dir is not empty during deletion, we do not
   raise the error, rather we go ahead and remove 
   all the contents and then delete the dir */
#define FORCED_DIR_REMOVAL

/**************************
  D E F I N I T I O N S & 
  D E C L A R A T I O N S
 **************************/


GLOBAL UBYTE              UsedDBs;								          /* Number of existing databases  */
GLOBAL int                LastFFS_ReturnCode;			          /* Last FFS return code          */
GLOBAL T_DBM_MASTERRECORD DbmMaster [MAX_DBs];              /* Database Master               */
GLOBAL T_DBM_STATE        DBM_State = DBM_NOT_INITIALISED;  /* DBM State                     */
GLOBAL T_DB_CHANGED       DB_History_log;                   /* History log                   */

#ifdef FFS_CLOSE_BEFORE_OPEN

  T_FFS_FD Dummy_FFSFileHandle = NULL;
  #define INVALID_FLD_CTR  0xFF
  #define NOT_OPENED       0x00
  #define OPENED_FOR_READ  0x01
  #define OPENED_FOR_WRITE 0x02

#endif

#ifdef DB_TEST

  S8 db_test_ffs_open_ctr = 0; 
  T_FFS_SIZE db_test_ffs_ret_code = EFFS_OK;

  #define DB_FFS_OPEN( ret, file, flag ) \
  { \
    ret = ffs_open( file, flag ); \
    \
    if( ret >= EFFS_OK ) \
    { \
      ++db_test_ffs_open_ctr; \
      TRACE_EVENT_P1( "DB_FFS_OPEN:db_test_ffs_open_ctr:%d", db_test_ffs_open_ctr ); \
    } else { \
      TRACE_EVENT_P1( "DB_FFS_OPEN:FFS ERROR:%d", ret ); \
    } \
  }


  #define DB_FFS_CLOSE( fd ) \
  { \
    T_FFS_SIZE db_test_ffs_ret_code = ffs_close( fd ); \
    \
    if( db_test_ffs_ret_code >= EFFS_OK ) \
    { \
      --db_test_ffs_open_ctr; \
      TRACE_EVENT_P1( "DB_FFS_CLOSE:db_test_ffs_open_ctr:%d", db_test_ffs_open_ctr ); \
    } else { \
      TRACE_EVENT_P1( "DB_FFS_CLOSE:FFS ERROR:%d", db_test_ffs_ret_code ); \
    } \
    \
    fd = NULL; \
  }

  #define DB_RETURN( ret ) \
  { \
    TRACE_EVENT_P1( "DB_RETURN:ret:%d", ret ); \
    return ret; \
  }
 
  #define DB_VALUE_RETURN( ret ) \
  { \
    TRACE_EVENT_P1( "DB_VALUE_RETURN:ret:%d", ret ); \
    return ret; \
  }
 
#else

  #define DB_FFS_OPEN( ret, file, flag ) ret = ffs_open( file, flag )
  #define DB_FFS_CLOSE( fd ) { ffs_close( fd ); fd = NULL; }
  #define DB_RETURN( ret ) return ret
  #define DB_VALUE_RETURN( ret ) return ret

#endif

#ifdef DB_MEMORY_CHECK

  UBYTE alloc_ctr = 0;

  #define DB_MALLOC( ptr, num_of_bytes ) \
  { \
    ++alloc_ctr; \
    TRACE_EVENT_P1( "DB Memory Alloc:%d", alloc_ctr ); \
    ACI_MALLOC( ptr, num_of_bytes ); \
  }

  #define DB_MFREE( ptr ) \
  { \
    --alloc_ctr; \
    TRACE_EVENT_P1( "DB Memory Free:%d", alloc_ctr ); \
    ACI_MFREE( ptr ); \
  }

#else

  #define DB_MALLOC( ptr, num_of_bytes ) ACI_MALLOC( ptr, num_of_bytes )
  #define DB_MFREE( ptr ) ACI_MFREE( ptr ) 

#endif


#ifdef _SIMULATION_
  #define DB_DIR "/dbm"
  #define DB_MASTER_FILE "/dbm/DD_master"
#else
  #define DB_DIR "/gsm/dbm" 
  #define DB_MASTER_FILE "/gsm/dbm/DD_master"
#endif

#define FILENAME_LEN 40

#define T_DB_MASTER_RECORD_SIZE 18        
/* DBM master record
   File: DB_DIR/DD_master
   Data: 
      1)	DBDirectory (16 bytes, based on MAX_LEN_DIRECTORY=16)
      2)	NumOfFiles (1 byte, based on MAX_NUM_FILES=255)
      3)	Tracked (1 byte)
 */


#define T_DB_FIELD_RECORD_SIZE 43         
/* Based on the following data written into FFS 
   File: DB_DIR/DD_<DBDirectory>
   Data: 
    1)	FieldID (2 bytes, given in Interface)
    2)	DBType (1 byte)
    3)	RecordSize (2 bytes, based on MAX_RECORD_SIZE=65kb)
    4)	NumOfRecords (1 byte, based on MAX_NUM_RECORDS=255)
    5)	SortIndexes (MAX_NUM_OF_SORT_INDEXS bytes, 1 byte for each index )
    6)	RecordBitMap (32 bytes, as of now based on MAX_NUM_RECORDS=255)
    7)	Clean (1 byte, this can be clubbed with DBType )
 */

#define RecordBitMap_OFFSET 10  /* depending on structure in DB_DIR/DD_<DBDirectory> */

#define SortIndexList_OFFSET 6  /* depending on structure in DB_DIR/DD_<DBDirectory> */

#define QuickSort_BufferStackSize 8 /* For Non recursive quick sort, 
                                         allows sorting of 2 ^ 8 = 256 elements */

#define SEARCH_FAILED -1

#define MAX_BIT_POS 8

#define BITMAP_INDEX( num ) ( ( num - 1 ) / MAX_BIT_POS )
#define BIT_POS( num ) ( ( num - 1 ) % MAX_BIT_POS )

/* For RAM strctures */
#define TRACKED_AND_CLEAN 0x03

#define TRACKED 0x02

#define CLEAN 0x01

#define NOT_CLEAN 0x00

/* For FFS strctures */
#define FFS_TRACKED 0x01

#define FFS_NOT_TRACKED 0x00

#define NOT !

#define INVALID_RECORD_NUM 0xFF

/*
    Internal function to initialize RAM structures with FFS
 */
T_DB_CODE init_RAM_with_FFS ( void );

#define	DBM_State_check \
  if ( DBM_State EQ DBM_NOT_INITIALISED ) DB_RETURN( DB_NOT_INITIALISED );

T_DB_CODE db_handle_check ( int db_handle );

UBYTE field_id_search ( int db_handle, 
                        USHORT field_id );

T_DB_CODE update_history_log ( int    db_handle,
                               USHORT field_id,
                               USHORT record_num );

/* functions swap and parition are required for sorting */
void swap ( UBYTE* sort_list,
            UBYTE  low,
            UBYTE  high );

UBYTE split_list ( UBYTE*        sort_list,
                   UBYTE         low,
                   UBYTE         high,
                   T_COMP_FUNC   compare_function, 
                   ULONG         flags,
                   int           db_handle,
                   USHORT        field_id );

void quick_sort ( UBYTE*        sort_list,
                  UBYTE         num_of_elements,
                  T_COMP_FUNC   compare_function, 
                  ULONG         flags,
                  int           db_handle,
                  USHORT        field_id ); 

UBYTE search_clear_bit_in_bitmap ( UBYTE* bitmap,
                                   UBYTE  bitmap_size );

void set_bit_in_bitmap ( UBYTE* bitmap,
                         UBYTE  bitmap_size,
                         UBYTE  bit_num );

void reset_bit_in_bitmap ( UBYTE* bitmap,
                           UBYTE  bitmap_size,
                           UBYTE  bit_num );

T_DB_CODE populate_sorted_list_from_FFS ( 

#ifdef FFS_CLOSE_BEFORE_OPEN
                                          UBYTE db_ctr,
#endif
                                          char*   sort_file,
                                          UBYTE   num_of_elements,
                                          UBYTE** sort_list_ptr );

T_DB_CODE write_sorted_list_to_FFS ( 
                                     
#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE db_ctr,
#endif
                                     char*   sort_file,
                                     UBYTE   num_of_elements,
                                     UBYTE*  sort_list );

int search ( UBYTE*            sort_list,
             UBYTE             num_of_elements,
             UBYTE*            order_num,
             T_SEARCH_FUNC     search_function, 
             ULONG             flags, 
             const UBYTE*      search_tag,
             int               db_handle,
             USHORT            field_id );

T_DB_CODE update_field_data_in_FFS ( const char* field_file,
                                     T_FFS_FD*   filehandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE       db_ctr,
#endif
                                     UBYTE       fld_ctr,
                                     UBYTE       field_data_max_size,
                                     UBYTE*      field_data,
                                     USHORT      offset,
                                     USHORT      length );

UBYTE cal_NextRecordNum ( const char* DBDirectory, 
                          USHORT	     FieldID,
                          USHORT	     RecordSize );

T_DB_CODE read_user_record_from_FFS ( const char* user_field_file,

#ifdef FFS_CLOSE_BEFORE_OPEN
                                      UBYTE       db_ctr,
                                      UBYTE       fld_ctr,
#endif
                                      T_FFS_FD*   filehandle,
                                      UBYTE       record_num,
                                      USHORT      record_size,
                                      USHORT      offset,
                                      USHORT      length,
                                      UBYTE*      record_buffer );

T_DB_CODE write_user_record_to_FFS ( const char* user_field_file,

#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE       db_ctr,
                                     UBYTE       fld_ctr,
#endif
                                     T_FFS_FD*   filehandle,
                                     UBYTE       record_num,
                                     USHORT      record_size,
                                     USHORT      offset,
                                     USHORT      length,
                                     UBYTE*      record_buffer );

T_DB_CODE delete_file_dir_from_FFS ( const char* filename );

T_DB_CODE update_dbm_data_in_FFS ( const char* filename,
                                   UBYTE       db_ctr,
                                   UBYTE       db_data_max_size,
                                   UBYTE*      db_data,
                                   USHORT      offset,
                                   USHORT      length );

T_DB_CODE remove_field_from_FFS ( UBYTE db_ctr,
                                  UBYTE fld_ctr );


#ifdef FFS_CLOSE_BEFORE_OPEN

T_FFS_FD db_open_user_field_file ( UBYTE            db_ctr, 
                                   UBYTE            fld_ctr, 
                                   const char*      user_field_file,
                                   T_FFS_OPEN_FLAGS open_option );


void db_close_user_field_files ( UBYTE db_ctr );

BOOL db_open_full_for_read ( UBYTE db_ctr );

BOOL db_open_full_for_write ( UBYTE db_ctr );

void db_close_for_write ( UBYTE db_ctr );

void db_close_for_read ( UBYTE db_ctr );

UBYTE db_status_user_field_file ( UBYTE db_ctr,
                                  UBYTE fld_ctr );


#endif /* FFS_CLOSE_BEFORE_OPEN */


#ifdef FORCED_DIR_REMOVAL

T_DB_CODE delete_dir_forced ( const char* filename );

#endif /* FORCED_DIR_REMOVAL */

#ifdef INSERTION_SORT

void get_sort_lists_from_FFS ( UBYTE db_ctr,
                               UBYTE fld_ctr );

void new_in_sort_lists ( UBYTE db_ctr,
                         UBYTE fld_ctr,
                         UBYTE record_num );

void update_in_sort_lists ( UBYTE db_ctr,
                            UBYTE fld_ctr,
                            UBYTE record_num );

void delete_in_sort_lists ( UBYTE db_ctr,
                            UBYTE fld_ctr,
                            UBYTE record_num );

void insertion_sort ( UBYTE*        sort_list,
                      UBYTE         num_of_elements,
                      T_COMP_FUNC   compare_function, 
                      ULONG         flags,
                      int           db_handle,
                      USHORT        field_id ); 

void insert_element ( UBYTE*        sort_list,
                      UBYTE         num_of_elements,
                      UBYTE         bottom,
                      UBYTE         top,
                      UBYTE         record_num,
                      T_COMP_FUNC   compare_function, 
                      ULONG         flags,
                      int           db_handle,
                      USHORT        field_id );

#endif /* INSERTION_SORT */

T_DB_CODE internal_db_sort ( int          db_handle, 
                             USHORT       field_id, 
                             UBYTE        sort_index, 
                             T_COMP_FUNC  compare_function, 
                             ULONG        flags,
                             T_DB_SORT    sort_type );

























/******************************
  I M P L E M E N T A T I O N 
 ******************************/

/**********************/
/* INTERNAL FUNCTIONS */
/**********************/

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_handle_check      |
+--------------------------------------------------------------------+

    PURPOSE : DB Handle checks
*/

T_DB_CODE db_handle_check ( int db_handle )
{
  /* Check for db_handle range */
  if( db_handle >= MAX_DBs )
    return DB_INVALID_DB;
  
  /* Check for DBState of db_handle is CLOSED or UNUSED_ENTRY
     If yes, return DB_INVALID_DB  */

  if( ( DbmMaster[db_handle].DBState EQ CLOSED       ) OR
      ( DbmMaster[db_handle].DBState EQ UNUSED_ENTRY ) )
    return DB_INVALID_DB;
  
  return DB_OK;
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: field_id_search      |
+--------------------------------------------------------------------+

    PURPOSE : checks for the existence of field id in RAM
*/
UBYTE field_id_search ( int     db_handle, 
                        USHORT  field_id )
{
  UBYTE  fld_ctr;
  
  for( fld_ctr = 0; fld_ctr < DbmMaster[db_handle].NumOfFiles; ++fld_ctr )
  {
    if( DbmMaster[db_handle].ptrFieldRecord[fld_ctr].FieldID EQ field_id )
    {
      return fld_ctr;
    }
  }
  return fld_ctr;
}


/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: update_history_log   |
+--------------------------------------------------------------------+

    PURPOSE : Updating history log
*/
T_DB_CODE update_history_log ( int    db_handle,
                               USHORT field_id,
                               USHORT record_num )
{
  UBYTE i;

  /* Check if database is tracked */
  if( NOT ( DbmMaster[db_handle].Tracked_Clean & TRACKED ) ) /* if db is NOT tracked */
    return DB_OK;

  /* Search history log if the record is present
     If present, return DB_OK */
  for( i = 0; i < DB_History_log.entries; ++i )
  {
    if( DB_History_log.field_id[i] EQ field_id )
      if( DB_History_log.record[i] EQ record_num )
        return DB_OK;
  }

  /* If the number of existing logs in history is reached limit, DB_MAX_AFFECTED, 
     return error "DB_HISTORY_FULL". */
  if( DB_History_log.entries EQ DB_MAX_AFFECTED )
    return DB_HISTORY_FULL;

  /* Add new entry */
  DB_History_log.field_id [DB_History_log.entries] = field_id;
  DB_History_log.record [DB_History_log.entries] = record_num;
  ++DB_History_log.entries;

  return DB_OK;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: swap                 |
+--------------------------------------------------------------------+

    PURPOSE : Swaps the data
*/
void swap ( UBYTE* sort_list,
            UBYTE  low,
            UBYTE  high )
{
  UBYTE tmp = sort_list[low - 1];

  sort_list[low - 1] = sort_list[high - 1];
  sort_list[high - 1] = tmp;

  return;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: split_list            |
+--------------------------------------------------------------------+

    PURPOSE : 
*/
UBYTE split_list ( UBYTE*        sort_list,
                   UBYTE         low,
                   UBYTE         high,
                   T_COMP_FUNC   compare_function, 
                   ULONG         flags,
                   int           db_handle,
                   USHORT        field_id ) 
{
  UBYTE i,
        pivot_loc;

  /* put pivot at first position */
  swap( sort_list, low, (UBYTE)((low + high)/2));

  pivot_loc = low;

  for( i = low + 1; i <= high; ++i )
  {
    if( compare_function( db_handle, 
                          field_id,
                          sort_list[i - 1],
                          sort_list[low - 1],
                          flags ) 
        < 0 )
    {
      ++pivot_loc;
      swap( sort_list, pivot_loc, i );
    }
  }

  swap( sort_list, low, pivot_loc );

  return pivot_loc;
}


/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: quick_sort           |
+--------------------------------------------------------------------+

    PURPOSE : Sorts the user data records in FFS based 
              on non-recursive qsort method
*/
void quick_sort ( UBYTE*        sort_list,
                  UBYTE         num_of_elements,
                  T_COMP_FUNC   compare_function, 
                  ULONG         flags,
                  int           db_handle,
                  USHORT        field_id )
{
  /* This is non-recursive version of QuickSort

     - It requires additional storage of (2 * QuickSort_BufferStackSize) bytes 
       compared to recursive version of quick sort
     
     - It supports sorting of 2 ^ QuickSort_BufferStackSize elements. */

  /* It is assumed that MAX_NUM_RECORDS = 254;if we want to support the 
     sorting of list bigger than 254 elements, change following UBYTE 
     to U16, U32 etc. and update QuickSort_BufferStackSize accordingly ! */

  UBYTE low,
        high,
        pivot_loc,
        buff_stack_ctr = 0,
        buffer_low[QuickSort_BufferStackSize],
        buffer_high[QuickSort_BufferStackSize];

  low = 1;
  high = num_of_elements;

  do 
  {
    if( buff_stack_ctr > 0 ) 
    {
      --buff_stack_ctr;
      low  = buffer_low[buff_stack_ctr];
      high = buffer_high[buff_stack_ctr];
    }

    while( low < high )
    {
      pivot_loc = split_list( sort_list, 
                              low, 
                              high,
                              compare_function,
                              flags,
                              db_handle, 
                              field_id );

      if( buff_stack_ctr >= QuickSort_BufferStackSize )
      {
        /* This should never happen ! */
        TRACE_ERROR( "Value set for QuickSort_BufferStackSize is small !");
        return;
      }

      /* Remember larger sublist, and process smaller */

      if( (pivot_loc - low) < (high - pivot_loc) )
      {
        /* right list is bigger */
        buffer_low[buff_stack_ctr] = pivot_loc + 1;
        buffer_high[buff_stack_ctr] = high;
        ++buff_stack_ctr;
        high = pivot_loc - 1;

      } else {
        /* left list is bigger */
        buffer_low[buff_stack_ctr] = low;
        buffer_high[buff_stack_ctr] = pivot_loc - 1;
        ++buff_stack_ctr;
        low = pivot_loc + 1;

      }
    }
  } while( buff_stack_ctr != 0 );

  return;
}

/*
+-------------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                             |
|                                       ROUINE: search_clear_bit_in_bitmap      |
+-------------------------------------------------------------------------------+

    PURPOSE : Searches for the bit not set in record bitmap which 
              indicates free record 
*/
UBYTE search_clear_bit_in_bitmap ( UBYTE* bitmap,
                                   UBYTE  bitmap_size )
{
  UBYTE* tmp_byte_ptr = bitmap + bitmap_size - 1 ;
  UBYTE  bitmap_num = 0, 
         bit_num,
         tmp_byte;

  /* we will go byte by byte */
  while( bitmap_num < ( bitmap_size * MAX_BIT_POS ) )
  {
    tmp_byte = *tmp_byte_ptr;

    for( bit_num = 0; bit_num < MAX_BIT_POS; ++bit_num )
    {
      if( NOT (tmp_byte & 0x01) )
      {
        return bitmap_num;
      }
      tmp_byte >>= 1;
      ++bitmap_num;
    }

    --tmp_byte_ptr;
  }

  return bitmap_num;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: set_bit_in_bitmap    |
+--------------------------------------------------------------------+

    PURPOSE : Sets a bit in record bitmap
*/
void set_bit_in_bitmap ( UBYTE* bitmap,
                         UBYTE  bitmap_size,
                         UBYTE  bit_num )
{
  bitmap[ bitmap_size - BITMAP_INDEX(bit_num) - 1 ] |= (0x01 << BIT_POS(bit_num) );
}

/*
+------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                      |
|                                       ROUINE: reset_bit_in_bitmap      |
+------------------------------------------------------------------------+

    PURPOSE : resets a bit in record bitmap
*/
void reset_bit_in_bitmap ( UBYTE* bitmap,
                           UBYTE  bitmap_size,
                           UBYTE  bit_num )
{
  bitmap[ bitmap_size - BITMAP_INDEX(bit_num) - 1 ] &=  ~ (0x01 << BIT_POS(bit_num));
}

/*
+----------------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                                |
|                                       ROUINE: populate_sorted_list_from_FFS      |
+----------------------------------------------------------------------------------+

    PURPOSE : Reads the sorted list from FFS
*/
T_DB_CODE populate_sorted_list_from_FFS ( 

#ifdef FFS_CLOSE_BEFORE_OPEN
                                          UBYTE db_ctr,
#endif
                                          char*   sort_file,
                                          UBYTE   num_of_elements,
                                          UBYTE** sort_list_ptr )
{
  T_FFS_FD   ffs_fd_sort_file;
  T_FFS_SIZE ffs_ret_code;
  UBYTE*     sort_list;

#ifdef FFS_CLOSE_BEFORE_OPEN
  /* READ */
  if( db_open_full_for_read( db_ctr ) )
  {
     db_close_for_read( db_ctr );  
  }
#endif

  DB_FFS_OPEN( ffs_fd_sort_file, sort_file, FFS_O_RDONLY );

  if( (ffs_fd_sort_file EQ EFFS_NAMETOOLONG ) OR
      (ffs_fd_sort_file EQ EFFS_BADNAME     ) OR
      (ffs_fd_sort_file EQ EFFS_NOTFOUND    ) OR
      (ffs_fd_sort_file EQ EFFS_INVALID     ) OR
      (ffs_fd_sort_file EQ EFFS_LOCKED      ) )

  {
	  TRACE_EVENT_P1( "populate_sorted_list_from_FFS:DB_FFS_OPEN %d", ffs_fd_sort_file );
    return DB_FAIL;
  }

  if( ffs_fd_sort_file < EFFS_OK )
  {
    LastFFS_ReturnCode = ffs_fd_sort_file;
	  TRACE_EVENT_P1( "populate_sorted_list_from_FFS:DB_FFS_OPEN %d", ffs_fd_sort_file );
    return DB_FAIL_FS;
  }

  /* This seek is not required as file pointer will be at start of file,
     but ffs guide doesn't say so ! This needs to be clarified and 
     accordingly all ffs_seek at start of file after opening the file
     can be removed ! */
  ffs_ret_code = ffs_seek( ffs_fd_sort_file, 0, FFS_SEEK_SET );

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
    DB_FFS_CLOSE( ffs_fd_sort_file );
	  TRACE_EVENT_P1( "populate_sorted_list_from_FFS:ffs_seek %d", ffs_ret_code );
    return DB_FAIL;
  }


#ifndef INSERTION_SORT

  DB_MALLOC( sort_list, num_of_elements );
  memset( sort_list, INVALID_RECORD_NUM, num_of_elements );

#else /* INSERTION_SORT */

  DB_MALLOC( sort_list, num_of_elements + 1 );
  memset( sort_list, INVALID_RECORD_NUM, num_of_elements + 1 );

#endif /* INSERTION_SORT */

  ffs_ret_code = ffs_read( ffs_fd_sort_file, 
                           sort_list, 
                           num_of_elements );

  if( (ffs_ret_code EQ EFFS_BADFD ) OR
      (ffs_ret_code EQ EFFS_BADOP ) )
  {
    DB_MFREE( sort_list );
    DB_FFS_CLOSE( ffs_fd_sort_file );
	  TRACE_EVENT_P1( "populate_sorted_list_from_FFS:ffs_read %d", ffs_ret_code );
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    LastFFS_ReturnCode = ffs_fd_sort_file;
    DB_MFREE( sort_list );
	  TRACE_EVENT_P1( "populate_sorted_list_from_FFS:ffs_read %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd_sort_file );
    return DB_FAIL_FS;
  }

  DB_FFS_CLOSE( ffs_fd_sort_file );

  /* to avoid memory leak */
  if( *sort_list_ptr NEQ NULL )
    DB_MFREE( *sort_list_ptr );

  *sort_list_ptr = sort_list;

  return DB_OK;
}

/*
+-----------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                           |  
|                                       ROUINE: write_sorted_list_to_FFS      |
+-----------------------------------------------------------------------------+

    PURPOSE : Writes the sorted list into FFS
*/
T_DB_CODE write_sorted_list_to_FFS ( 
                                     
#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE db_ctr,
#endif
                                     char*   sort_file,
                                     UBYTE   num_of_elements,
                                     UBYTE*  sort_list )
{
  T_FFS_FD   ffs_fd_sort_file;
  T_FFS_SIZE ffs_ret_code;

#ifdef FFS_CLOSE_BEFORE_OPEN
  /* WRITE */
  if( db_open_full_for_write( db_ctr ) )
  {
     db_close_for_write( db_ctr );  
  }
#endif

  /* FFS_O_TRUNC => If file already exists and it is opened for writing, its 
                    length is truncated to zero. */
                    
  DB_FFS_OPEN( ffs_fd_sort_file, sort_file, FFS_O_CREATE | FFS_O_WRONLY | FFS_O_TRUNC );

  if( (ffs_fd_sort_file EQ EFFS_NAMETOOLONG ) OR
      (ffs_fd_sort_file EQ EFFS_BADNAME     ) )
  {
	  TRACE_EVENT_P1( "write_sorted_list_to_FFS:DB_FFS_OPEN %d", ffs_fd_sort_file );
    return DB_FAIL;
  }

  if( ffs_fd_sort_file < EFFS_OK )
  {
	  TRACE_EVENT_P1( "write_sorted_list_to_FFS:DB_FFS_OPEN %d", ffs_fd_sort_file );
    LastFFS_ReturnCode = ffs_fd_sort_file;
    return DB_FAIL_FS;
  }


  ffs_ret_code = ffs_seek( ffs_fd_sort_file, 0, FFS_SEEK_SET );

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
	  TRACE_EVENT_P1( "write_sorted_list_to_FFS:ffs_seek %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd_sort_file );
    return DB_FAIL;
  }

  ffs_ret_code = ffs_write( ffs_fd_sort_file, 
                            sort_list, 
                            num_of_elements );
   
  if( ( ffs_ret_code EQ EFFS_BADFD ) OR
      ( ffs_ret_code EQ EFFS_BADOP ) )
  {
    DB_FFS_CLOSE( ffs_fd_sort_file );
    ffs_ret_code = ffs_remove( sort_file );
	  TRACE_EVENT_P1( "write_sorted_list_to_FFS:ffs_write %d", ffs_ret_code );
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd_sort_file );
    ffs_ret_code = ffs_remove( sort_file );
	  TRACE_EVENT_P1( "write_sorted_list_to_FFS:ffs_write %d", ffs_ret_code );
    return DB_FAIL_FS;
  }

  DB_FFS_CLOSE( ffs_fd_sort_file ); /* yeah, we close it here ! */

  return DB_OK;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: search               |
+--------------------------------------------------------------------+

    PURPOSE : Searches for the record; uses binary search and compare
              function is passed as a parameter.
*/
int search ( UBYTE*            sort_list,
             UBYTE             num_of_elements,
             UBYTE*            order_num,
             T_SEARCH_FUNC     search_function, 
             ULONG             flags, 
             const UBYTE*      search_tag,
             int               db_handle,
             USHORT            field_id ) 
{
  /* Binary search is used */
  UBYTE top,
        bottom,
        middle;

  S8 result;

  /* Strictly speaking, this check is not required, but 
     it would save some processing time */
  if( num_of_elements EQ 0 )
    return SEARCH_FAILED;

  if( (*order_num) EQ 0 )
  {
    /* Complete list search */
    top    = num_of_elements - 1;
    bottom = 0;

  } else {

    /* Strictly speaking, this check is not required, but 
       it would save some processing time */
    if( *order_num >= num_of_elements )
      return SEARCH_FAILED;

    /* Give the next element in sort list if matching */
    if( search_function( flags,
                         search_tag,
                         db_handle,
                         field_id,
                         sort_list[ (*order_num) ] )
        EQ 0 )
    {
      *order_num += 1;
      return *order_num;
    }

    return SEARCH_FAILED;
  }

  if( num_of_elements EQ 2 )
  { 
    /* Special case of two elements */

    if( search_function( flags,
                         search_tag,
                         db_handle,
                         field_id,
                         sort_list[ 0 ] )
       EQ 0 )
    {
      *order_num = 1;
      return 1;
    }

    if( search_function( flags,
                         search_tag,
                         db_handle,
                         field_id,
                         sort_list[ 1 ] )
        EQ 0 )
    {
      *order_num = 2;
      return 2;
    }

    return SEARCH_FAILED;
  }

  /* basic concept is here is to get first matching element in sort list and
     so we need to compare bottom_th element for returning */

  while( top >= bottom )
  {
    /* if bottom_th element match, return PASS */
    if( search_function( flags,
                         search_tag,
                         db_handle,
                         field_id,
                         sort_list[ bottom ] )
        EQ 0 )
    {
      *order_num = bottom + 1;
      return *order_num;
    }

    /* end of list */
    if( bottom EQ top )
      return SEARCH_FAILED;

    /* now binary search comes into play */

    middle = (top + bottom) / 2;

    result = search_function( flags,
                              search_tag,
                              db_handle,
                              field_id,
                              sort_list[ middle ] );

    if( result EQ 0 )
    {
      ++bottom;

      if( bottom EQ middle )
      {
        /* middle has already matched so, 
           instead of going to next loop, we return PASS here ! */
        *order_num = bottom + 1;
        return *order_num;
      }

      top = middle;

    } else {
      
      if( result > 0 )
      {
         ++bottom;         

      } else {

         bottom = middle + 1;

      }

    } /* result EQ 0 */
      
  } /* while top >= bottom */

  return SEARCH_FAILED;

}


/*
+-----------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                           |
|                                       ROUINE: update_field_data_in_FFS      |
+-----------------------------------------------------------------------------+

    PURPOSE : Updates elementary file data in FFS
*/
T_DB_CODE update_field_data_in_FFS ( const char* field_file,
                                     T_FFS_FD*   filehandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE       db_ctr,
#endif
                                     UBYTE       fld_ctr,
                                     UBYTE       field_data_max_size,
                                     UBYTE*      field_data,
                                     USHORT      offset,
                                     USHORT      length )
{
  T_FFS_FD   ffs_fd = *filehandle;
  T_FFS_SIZE ffs_ret_code;

  /* Updating field data in FFS in file "field_file" */

  if( ffs_fd EQ NULL )
  {
    DB_FFS_OPEN( ffs_fd, field_file, FFS_O_WRONLY );

    if( ( ffs_fd EQ EFFS_NOTFOUND )    OR
        ( ffs_fd EQ EFFS_NAMETOOLONG ) OR
        ( ffs_fd EQ EFFS_BADNAME )     OR
        ( ffs_fd EQ EFFS_INVALID )     )
    {
	    TRACE_EVENT_P1( "update_field_data_in_FFS:DB_FFS_OPEN %d", ffs_fd );
      return DB_FAIL;
    }

    if( ffs_fd < EFFS_OK )
    {
      LastFFS_ReturnCode = ffs_fd;
	    TRACE_EVENT_P1( "update_field_data_in_FFS:DB_FFS_OPEN %d", ffs_fd );
      return DB_FAIL_FS;
    }

    *filehandle = ffs_fd;

  } /* filehandle EQ NULL ? */

  /* Seek the file to fld_ctr position to update the entry */
  ffs_ret_code = ffs_seek( ffs_fd, 
                           ( fld_ctr * field_data_max_size ) + offset, 
                           FFS_SEEK_SET );

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
    TRACE_EVENT_P1( "update_field_data_in_FFS:ffs_seek %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "update_field_data_in_FFS:ffs_seek %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL_FS;
  }

  /* clean is reset in memset, so no processing for it 
     (in case of non-tracked database, anyway we ignore it ! */ 

  ffs_ret_code = ffs_write( ffs_fd, 
                            field_data, 
                            length );

  if( (ffs_ret_code EQ EFFS_BADFD ) OR
      (ffs_ret_code EQ EFFS_BADOP ) )
  {
    TRACE_EVENT_P1( "update_field_data_in_FFS:ffs_write %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "update_field_data_in_FFS:ffs_write %d", ffs_ret_code );
    DB_FFS_CLOSE ( ffs_fd );
    *filehandle = NULL;
    LastFFS_ReturnCode = ffs_ret_code;
    return DB_FAIL_FS;
  }

  /* DB_FFS_CLOSE( ffs_fd ); we will do it in db_flush */
  /* updation is over */

#ifdef FFS_OPEN_PROBLEM_PATCH
  DB_FFS_CLOSE( ffs_fd );
  *filehandle = NULL;
#endif

#ifndef FFS_CLOSE_BEFORE_OPEN
  /* this is db file, but we do not close it */
#endif

  return DB_OK;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: cal_NextRecordNum    |
+--------------------------------------------------------------------+

    PURPOSE : Reads the status of the file and returns 
              the next available record (position)
*/
UBYTE cal_NextRecordNum ( const char* db_directory, 
                          USHORT	    field_id,
                          USHORT	    record_size )
{
  char user_field_file[FILENAME_LEN];

  T_FFS_SIZE ffs_ret_code;
  T_FFS_STAT ffs_file_stat;

  sprintf( user_field_file, 
          "%s/UD_%d", 
          db_directory, 
          field_id );

  ffs_ret_code = ffs_stat( user_field_file, &ffs_file_stat );

  if( ffs_ret_code EQ EFFS_OK )
  {
    return ( (ffs_file_stat.size / record_size) + 1);
  }

  if( ffs_ret_code EQ EFFS_NOTFOUND )
  {
    TRACE_EVENT( "cal_NextRecordNum:ffs_stat:FILE NOT FOUND" );
    return 1;
  }

  /* this is a problem with user_field_file, raise an error and 
     return 0 */

  TRACE_ERROR( "cal_NextRecordNum: PROBLEM with user_field_file" );
  TRACE_EVENT_P1( "cal_NextRecordNum:ffs_stat %d", ffs_ret_code );
  return 1;

}


/*
+------------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                            |
|                                       ROUINE: read_user_record_from_FFS      |
+------------------------------------------------------------------------------+

    PURPOSE : Reads a user record from FFS
*/
T_DB_CODE read_user_record_from_FFS ( const char* user_field_file,

#ifdef FFS_CLOSE_BEFORE_OPEN
                                      UBYTE       db_ctr,
                                      UBYTE       fld_ctr,
#endif
                                      T_FFS_FD*   filehandle,
                                      UBYTE       record_num,
                                      USHORT      record_size,
                                      USHORT      offset,
                                      USHORT      length,
                                      UBYTE*      record_buffer )
{
  T_FFS_FD   ffs_fd = NULL;
  T_FFS_SIZE ffs_ret_code;

  /* See if file already opened, 
     if not open it and update the file handle */

#ifndef FFS_CLOSE_BEFORE_OPEN
  ffs_fd = *filehandle;

  if( ffs_fd EQ NULL )
  {
    DB_FFS_OPEN( ffs_fd, user_field_file, FFS_O_RDONLY );

    if( (ffs_fd EQ EFFS_NOTFOUND)    OR
        (ffs_fd EQ EFFS_NAMETOOLONG) OR
        (ffs_fd EQ EFFS_BADNAME)     OR
        (ffs_fd EQ EFFS_INVALID)     OR
        (ffs_fd EQ EFFS_LOCKED)      )
    {
      TRACE_EVENT_P1( "read_user_record_from_FFS:DB_FFS_OPEN %d", ffs_fd );
      return DB_FAIL;
    }

    if( ffs_fd < EFFS_OK )
    {
      TRACE_EVENT_P1( "read_user_record_from_FFS:DB_FFS_OPEN %d", ffs_fd );
      LastFFS_ReturnCode = ffs_fd;
      return DB_FAIL_FS;
    }

    *filehandle = ffs_fd;
  }

#else /* FFS_CLOSE_BEFORE_OPEN */

  ffs_fd = db_open_user_field_file( db_ctr, fld_ctr, user_field_file, FFS_O_RDONLY );

#endif /* FFS_CLOSE_BEFORE_OPEN */

  ffs_ret_code = ffs_seek( ffs_fd, 
                           ( (record_num - 1) * (record_size) + offset),
                           FFS_SEEK_SET ); 

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
    TRACE_EVENT_P1( "read_user_record_from_FFS:ffs_seek %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "read_user_record_from_FFS:ffs_seek %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL_FS;
  }

  ffs_ret_code = ffs_read( ffs_fd, 
                           record_buffer, 
                           length ); 

  if( ( ffs_ret_code EQ EFFS_BADFD ) OR
      ( ffs_ret_code EQ EFFS_BADOP ) )
  {
    TRACE_EVENT_P1( "read_user_record_from_FFS:ffs_read %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "read_user_record_from_FFS:ffs_read %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL_FS;
  }

  /* DB_FFS_CLOSE( ffs_fd ); we will do it in db_flush */

#ifdef FFS_OPEN_PROBLEM_PATCH
  DB_FFS_CLOSE( ffs_fd );
  *filehandle = NULL;
#endif

  return DB_OK;
}

/*
+-------------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                             |
|                                       ROUINE: write_user_record_to_FFS      |
+-------------------------------------------------------------------------------+

    PURPOSE : Writes user record into FFS
*/
T_DB_CODE write_user_record_to_FFS ( const char* user_field_file,

#ifdef FFS_CLOSE_BEFORE_OPEN
                                     UBYTE       db_ctr,
                                     UBYTE       fld_ctr,
#endif
                                     T_FFS_FD*   filehandle,
                                     UBYTE       record_num,
                                     USHORT      record_size,
                                     USHORT      offset,
                                     USHORT      length,
                                     UBYTE*      record_buffer )
{
  T_FFS_FD   ffs_fd = NULL;
  T_FFS_SIZE ffs_ret_code;

  /* See if file already opened, 
     if not open it and update the file handle */

#ifndef FFS_CLOSE_BEFORE_OPEN
  ffs_fd = *filehandle;

  if( ffs_fd EQ NULL )
  {
    DB_FFS_OPEN( ffs_fd, user_field_file, FFS_O_RDWR );

    if( (ffs_fd EQ EFFS_NOTFOUND)    OR
        (ffs_fd EQ EFFS_NAMETOOLONG) OR
        (ffs_fd EQ EFFS_BADNAME)     OR
        (ffs_fd EQ EFFS_INVALID)     OR
        (ffs_fd EQ EFFS_LOCKED)      )
    {
      TRACE_EVENT_P1( "write_user_record_to_FFS:DB_FFS_OPEN %d", ffs_fd );
      return DB_FAIL;
    }

    if( ffs_fd < EFFS_OK )
    {
      TRACE_EVENT_P1( "write_user_record_to_FFS:DB_FFS_OPEN %d", ffs_fd );
      LastFFS_ReturnCode = ffs_fd;
      return DB_FAIL_FS;
    }

    *filehandle = ffs_fd;
  }

#else /* FFS_CLOSE_BEFORE_OPEN */

  ffs_fd = db_open_user_field_file( db_ctr, fld_ctr, user_field_file, FFS_O_RDWR );

#endif /* FFS_CLOSE_BEFORE_OPEN */

  ffs_ret_code = ffs_seek( ffs_fd, 
                           ( (record_num - 1) * (record_size) + offset),
                           FFS_SEEK_SET ); 

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
    TRACE_EVENT_P1( "write_user_record_to_FFS:ffs_seek %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "write_user_record_to_FFS:ffs_seek %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL_FS;
  }

  ffs_ret_code = ffs_write( ffs_fd, 
                            record_buffer, 
                            length ); 

  if( ( ffs_ret_code EQ EFFS_BADFD ) OR
      ( ffs_ret_code EQ EFFS_BADOP ) )
  {
    TRACE_EVENT_P1( "write_user_record_to_FFS:ffs_write %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "write_user_record_to_FFS:ffs_write %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    *filehandle = NULL;
    return DB_FAIL_FS;
  }

  /* DB_FFS_CLOSE( ffs_fd ); we will do it in db_flush */

#ifdef FFS_OPEN_PROBLEM_PATCH
  DB_FFS_CLOSE( ffs_fd );
  *filehandle = NULL;
#endif

  return DB_OK;
}

/*
+-----------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                           |
|                                       ROUINE: delete_file_dir_from_FFS      |
+-----------------------------------------------------------------------------+

    PURPOSE : Deletes a file from FFS
*/
T_DB_CODE delete_file_dir_from_FFS ( const char* filename )
{
  T_FFS_SIZE ffs_ret_code;

  TRACE_EVENT_P1( "delete_file_dir_from_FFS %s", filename );

  ffs_ret_code = ffs_remove( filename );

#ifdef FORCED_DIR_REMOVAL

  if( ffs_ret_code EQ EFFS_DIRNOTEMPTY )
  {
    TRACE_EVENT( "Inconsistency; going for delete_dir_forced" );
    return delete_dir_forced( filename ); 
  }

#endif

  if( ffs_ret_code EQ EFFS_NOTFOUND )
  {
    TRACE_EVENT( "delete_file_dir_from_FFS:ffs_remove:FILE NOT FOUND" );
    return DB_OK;
  }

  if( ( ffs_ret_code EQ EFFS_ACCESS   ) OR
      ( ffs_ret_code EQ EFFS_LOCKED   ) )
  {
    TRACE_EVENT_P1( "delete_file_dir_from_FFS:ffs_remove %d", ffs_ret_code );
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "delete_file_dir_from_FFS:ffs_remove %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    return DB_FAIL_FS;
  }

  return DB_OK;

}

/*
+---------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                         |
|                                       ROUINE: update_dbm_data_in_FFS      |
+---------------------------------------------------------------------------+

    PURPOSE : Updates the database manager information of a 
              particular database
*/
T_DB_CODE update_dbm_data_in_FFS ( const char* filename,
                                   UBYTE       db_ctr,
                                   UBYTE       db_data_max_size,
                                   UBYTE*      db_data,
                                   USHORT      offset,
                                   USHORT      length )
{
  T_FFS_FD   ffs_fd = NULL;
  T_FFS_SIZE ffs_ret_code;

  /* See if file already opened, 
     if not open it and update the file handle */

#ifdef FFS_CLOSE_BEFORE_OPEN
  /* WRITE */
  if( db_open_full_for_write( db_ctr ) )
  {
     db_close_for_write( db_ctr );  
  }
#endif

  DB_FFS_OPEN( ffs_fd, filename, FFS_O_WRONLY );

  if( ( ffs_fd EQ EFFS_NAMETOOLONG ) OR
      ( ffs_fd EQ EFFS_BADNAME )     OR
      ( ffs_fd EQ EFFS_NOTFOUND )    OR
      ( ffs_fd EQ EFFS_INVALID )     OR
      ( ffs_fd EQ EFFS_LOCKED )      )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:DB_FFS_OPEN %d", ffs_fd );
     return DB_FAIL;
  }

  if( ffs_fd < EFFS_OK )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:DB_FFS_OPEN %d", ffs_fd );
    LastFFS_ReturnCode = ffs_fd;
    return DB_FAIL_FS;
  }

  ffs_ret_code = ffs_seek( ffs_fd, db_ctr * db_data_max_size, FFS_SEEK_SET );

  if( ( ffs_ret_code EQ EFFS_BADFD )   OR
      ( ffs_ret_code EQ EFFS_INVALID ) OR
      ( ffs_ret_code EQ EFFS_BADOP )   )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:ffs_seek %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:ffs_seek %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_FFS_CLOSE( ffs_fd );
    return DB_FAIL_FS;
  }

  ffs_ret_code = ffs_write( ffs_fd, db_data, db_data_max_size );
   
  if( ( ffs_ret_code EQ EFFS_BADFD ) OR
      ( ffs_ret_code EQ EFFS_BADOP ) )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:ffs_write %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    return DB_FAIL;
  }

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P1( "update_dbm_data_in_FFS:ffs_write %d", ffs_ret_code );
    DB_FFS_CLOSE( ffs_fd );
    LastFFS_ReturnCode = ffs_ret_code;
    return DB_FAIL_FS;
  }

  DB_FFS_CLOSE( ffs_fd );  /* yeah, DB file, so ok to close it here ! */

  /* updation of dbm data done */

  return DB_OK;
}

/*
+--------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                        |
|                                       ROUINE: remove_field_from_FFS      |
+--------------------------------------------------------------------------+

    PURPOSE : Removes the elementary file and associated Index 
              file from FFS
*/
T_DB_CODE remove_field_from_FFS ( UBYTE db_ctr,
                                  UBYTE fld_ctr )
{
  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;
  char               user_field_file[FILENAME_LEN],
                     sort_file[FILENAME_LEN];
  T_DB_CODE          db_ret_code;
  UBYTE              sort_index_ctr;

  /* Assumed that DBState is not IN_USE */

  tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord + fld_ctr;
  
  if( tmp_ptrFieldRecord -> FieldID EQ INVALID_FIELD_ID ) /* if not valid field id */
    return DB_OK;

  /* Close of user record files not required as DBState is not IN_USE */

  /* Delete file "<DBDirectory>/UD_<field_id>" */

  sprintf( user_field_file, 
           "%s/UD_%d", 
           DbmMaster[db_ctr].DBDirectory, 
           tmp_ptrFieldRecord -> FieldID );

  db_ret_code = delete_file_dir_from_FFS ( user_field_file );

  if( db_ret_code NEQ DB_OK )
    return db_ret_code;

  /* For sorted lists */

  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] NEQ INVALID_SORT_INDEX )
    {
      /* Sort file, "<DBDirectory>/UD_<field_id>_sort_<sort_index>" */

      sprintf( sort_file, 
               "%s/UD_%d_sort_%d", 
               DbmMaster[db_ctr].DBDirectory, 
               tmp_ptrFieldRecord -> FieldID,
               tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] );

      db_ret_code = delete_file_dir_from_FFS ( sort_file );

      if( db_ret_code NEQ DB_OK )
        return db_ret_code;        
    }
  }

  return DB_OK;

}



#ifdef FFS_CLOSE_BEFORE_OPEN

T_FFS_FD db_open_user_field_file ( UBYTE            db_ctr, 
                                   UBYTE            fld_ctr, 
                                   const char*      user_field_file,
                                   T_FFS_OPEN_FLAGS open_option )
{
  UBYTE i;
  FileHandleRecord* tmp_open_fields;
  UBYTE*            tmp_old;
  UBYTE             tmp_max;

  /* sanity checks not done (db_ctr >= MAX_DBs OR fld_ctr EQ INVALID_FLD_CTR) */

  /* note that we store field counters and not field ids */

  /* Search in write/read list */
  for( i = 0; i < MAX_OPEN_WRITE_PER_DB; ++i )
  {
    if( DbmMaster[db_ctr].WRITE_OPEN_FIELDS[i].fld_ctr EQ fld_ctr )
      return DbmMaster[db_ctr].WRITE_OPEN_FIELDS[i].filehandle;
  } /* for */

  /* Search in read only list */
  for( i = 0; i < MAX_OPEN_READ_PER_DB; ++i )
  {
    if( DbmMaster[db_ctr].READ_OPEN_FIELDS[i].fld_ctr EQ fld_ctr )
    {
      if( NOT ( open_option & FFS_O_WRONLY ) ) /* if not opened for writing */
      {
        return DbmMaster[db_ctr].READ_OPEN_FIELDS[i].filehandle;
      }

      /* we need to reopen it in write mode */
      DB_FFS_CLOSE( DbmMaster[db_ctr].READ_OPEN_FIELDS[i].filehandle );

      DbmMaster[db_ctr].READ_OPEN_FIELDS[i].fld_ctr = INVALID_FLD_CTR;

      break;
      
    }
  } /* for */

  /* field is not present in both lists */

  if( open_option & FFS_O_WRONLY )
  {

    /* for writing and reading both */
    tmp_open_fields = DbmMaster[db_ctr].WRITE_OPEN_FIELDS;
    tmp_old = &(DbmMaster[db_ctr].old_write);
    tmp_max = MAX_OPEN_WRITE_PER_DB;

  } else {

    /* for reading only */
    tmp_open_fields = DbmMaster[db_ctr].READ_OPEN_FIELDS;
    tmp_old = &(DbmMaster[db_ctr].old_read);
    tmp_max = MAX_OPEN_READ_PER_DB;
  }


  /* search the list */
  for( i = 0; i < tmp_max; ++i )
  {
    if( tmp_open_fields[i].fld_ctr EQ INVALID_FLD_CTR )
    {
      DB_FFS_OPEN( tmp_open_fields[i].filehandle,
                   user_field_file,
                   open_option );

      tmp_open_fields[i].fld_ctr = fld_ctr;

      return tmp_open_fields[i].filehandle;
    }
  } /* for */

  /* we need to close the some file for opening this */

  if( *tmp_old >= tmp_max )
    *tmp_old = 0;

  DB_FFS_CLOSE( tmp_open_fields[*tmp_old].filehandle );

  DB_FFS_OPEN( tmp_open_fields[*tmp_old].filehandle,
               user_field_file,
               open_option );

  if( tmp_open_fields[*tmp_old].filehandle >= EFFS_OK )
  {
    tmp_open_fields[*tmp_old].fld_ctr = fld_ctr;
    ++(*tmp_old);
    return tmp_open_fields[*tmp_old - 1].filehandle;
  } 
  
  tmp_open_fields[*tmp_old].fld_ctr = INVALID_FLD_CTR;

  return tmp_open_fields[*tmp_old].filehandle;

}

void db_close_user_field_files ( UBYTE db_ctr )
{
  UBYTE i;
  FileHandleRecord* tmp_open_fields;

  /* sanity check not done */

  /* for write list */

  tmp_open_fields = DbmMaster[db_ctr].WRITE_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_WRITE_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr NEQ INVALID_FLD_CTR )
    {
      DB_FFS_CLOSE( tmp_open_fields[i].filehandle );
      tmp_open_fields[i].fld_ctr = INVALID_FLD_CTR;
    }
  }

  /* for read list */

  tmp_open_fields = DbmMaster[db_ctr].READ_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_READ_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr NEQ INVALID_FLD_CTR )
    {
      DB_FFS_CLOSE( tmp_open_fields[i].filehandle );
      tmp_open_fields[i].fld_ctr = INVALID_FLD_CTR;
    }
  }

}

BOOL db_open_full_for_read ( UBYTE db_ctr )
{
  UBYTE i;
  FileHandleRecord* tmp_open_fields;

  /* sanity check not done */

  /* for read list */

  tmp_open_fields = DbmMaster[db_ctr].READ_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_READ_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr EQ INVALID_FLD_CTR )
      return FALSE;
  }

  return TRUE;
}

BOOL db_open_full_for_write ( UBYTE db_ctr )
{
  UBYTE i;
  FileHandleRecord* tmp_open_fields;

  /* sanity check not done */

  /* for write list */

  tmp_open_fields = DbmMaster[db_ctr].WRITE_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_WRITE_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr EQ INVALID_FLD_CTR )
      return FALSE;
  }

  return TRUE;
}

void db_close_for_read ( UBYTE db_ctr )
{
  FileHandleRecord* tmp_open_fields = DbmMaster[db_ctr].READ_OPEN_FIELDS;
  UBYTE *tmp_old = &(DbmMaster[db_ctr].old_read);

  /* sanity check not done */

  /* for read list */

  if( *tmp_old >= MAX_OPEN_READ_PER_DB )
    *tmp_old = 0;

  DB_FFS_CLOSE( tmp_open_fields[*tmp_old].filehandle );

  tmp_open_fields[*tmp_old].fld_ctr = INVALID_FLD_CTR;

  ++(*tmp_old);

  return;

}

void db_close_for_write ( UBYTE db_ctr )
{
  FileHandleRecord* tmp_open_fields = DbmMaster[db_ctr].WRITE_OPEN_FIELDS;
  UBYTE *tmp_old = &(DbmMaster[db_ctr].old_write);

  /* sanity check not done */

  /* for read list */

  if( *tmp_old >= MAX_OPEN_WRITE_PER_DB )
    *tmp_old = 0;

  DB_FFS_CLOSE( tmp_open_fields[*tmp_old].filehandle );

  tmp_open_fields[*tmp_old].fld_ctr = INVALID_FLD_CTR;

  ++(*tmp_old);

  return;

}

UBYTE db_status_user_field_file ( UBYTE db_ctr,
                                  UBYTE fld_ctr )
{
  UBYTE i;
  FileHandleRecord* tmp_open_fields;

  /* sanity check not done */

  /* for write list */

  tmp_open_fields = DbmMaster[db_ctr].WRITE_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_WRITE_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr EQ fld_ctr )
      return OPENED_FOR_WRITE;
  }

  /* for read list */

  tmp_open_fields = DbmMaster[db_ctr].READ_OPEN_FIELDS;

  for( i = 0; i < MAX_OPEN_READ_PER_DB; ++i )
  {
    if( tmp_open_fields[i].fld_ctr EQ fld_ctr )
      return OPENED_FOR_READ;
  }

  return NOT_OPENED;

}

#endif /* FFS_CLOSE_BEFORE_OPEN */


#ifdef FORCED_DIR_REMOVAL

/*
+-----------------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                           |
|                                       ROUINE: delete_dir_forced             |
+-----------------------------------------------------------------------------+

    PURPOSE : Removes contents of dir and then delete the dir
              (it uses recursion)
*/
T_DB_CODE delete_dir_forced ( const char* dir )
{
  T_FFS_SIZE ffs_ret_code;
  T_FFS_DIR  ffs_dir;
  T_FFS_STAT tmp_ffs_stat;

  char  pathname[FILENAME_LEN];
  UBYTE dir_len;

  /* Open dir, if does not exist, just return DB_OK */

  ffs_ret_code = ffs_opendir (dir, &ffs_dir);

  if( ffs_ret_code EQ EFFS_NOTFOUND )
    DB_RETURN( DB_OK );

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P2( "delete_dir_forced:ffs_remove %d, %s", ffs_ret_code, dir );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_RETURN( DB_FAIL_FS );
  }

  /* read dir recursively */
  sprintf( pathname, 
           "%s/", 
           dir );

  dir_len = strlen( pathname );

  while( 1 )
  {
    /* filename inside the directory would be copied at pathname + dirlen */

    ffs_ret_code = ffs_readdir( &ffs_dir,
                                pathname + dir_len,
                                FILENAME_LEN - dir_len - 1 );

    if( ffs_ret_code EQ 0 ) /* dir is empty now */
    {
      break;
    }

    if( ffs_ret_code < EFFS_OK )
    {
      TRACE_EVENT_P2( "delete_dir_forced:ffs_readdir:%d, %s", ffs_ret_code, dir );
      LastFFS_ReturnCode = ffs_ret_code;
      DB_RETURN( DB_FAIL_FS );
    }

    ffs_ret_code = ffs_stat( pathname, &tmp_ffs_stat );

    if( ffs_ret_code < EFFS_OK )
    {
      TRACE_EVENT_P2( "delete_dir_forced:ffs_stat:%d, %s", ffs_ret_code, pathname);
      LastFFS_ReturnCode = ffs_ret_code;
      DB_RETURN( DB_FAIL_FS );
    }

    if( tmp_ffs_stat.type EQ OT_DIR )
    {
      /* Directory !! */

      T_DB_CODE db_code;

      TRACE_EVENT_P1( "Warning: directory %s", pathname );

      /* Recursion */
      db_code = delete_dir_forced( pathname ); 

      if( db_code NEQ DB_OK )
        DB_RETURN( db_code );

    } else {

      /* normal file (OT_FILE) or symbolic link (OT_LINK) */

      ffs_ret_code = ffs_remove( pathname );

      if( ffs_ret_code < EFFS_OK )
      {
        TRACE_EVENT_P2( "delete_dir_forced:ffs_remove:%d, %s", ffs_ret_code, pathname );
        LastFFS_ReturnCode = ffs_ret_code;
        DB_RETURN( DB_FAIL_FS );
      }

    } /* if( tmp_ffs_stat.type EQ OT_DIR ) */

  } /* while( 1 ) */

  /* now delete the directory */
  ffs_ret_code = ffs_remove( dir );

  if( ffs_ret_code < EFFS_OK )
  {
    TRACE_EVENT_P2( "delete_dir_forced:ffs_remove:%d, %s", ffs_ret_code, dir );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_RETURN( DB_FAIL_FS ); 
  }

  DB_RETURN( DB_OK );
}


#endif /* FORCED_DIR_REMOVAL */


#ifdef INSERTION_SORT

void get_sort_lists_from_FFS ( UBYTE db_ctr,
                               UBYTE fld_ctr )
{
  UBYTE sort_index_ctr;

  T_DB_CODE db_ret_code;

  char sort_file[FILENAME_LEN];

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord + fld_ctr;

  /* for all existing sort indexes */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    /* valid index ? */
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] NEQ INVALID_SORT_INDEX )
    {
      /* Check if we already have sorted list in RAM structure SortedLists */

      if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] EQ NULL )
      { 
        /* If no, populate the sorted list from file, 
           "<DBDirectory>/UD_<field_id>_sort_<sort_index>". 
           Sorted lists are freed in db_flush */

        sprintf( sort_file, 
                 "%s/UD_%d_sort_%d", 
                 DbmMaster[db_ctr].DBDirectory, 
                 tmp_ptrFieldRecord -> FieldID,
                 tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] );

        db_ret_code = 
          populate_sorted_list_from_FFS (  
#ifdef FFS_CLOSE_BEFORE_OPEN
                                          db_ctr,
#endif
                                          sort_file,
                                          tmp_ptrFieldRecord -> NumOfRecords,
                                          &(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr]) );
        if( db_ret_code NEQ DB_OK )
        {
          /* Failure, still we can continue as later during actual sorting, 
             FFS would be retried */

          TRACE_EVENT( "Warning: populate_sorted_list_from_FFS failed" );
          TRACE_EVENT_P3( "db_ctr %d field_id %d sort_index %d",
                          db_ctr,
                          tmp_ptrFieldRecord -> FieldID,
                          tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] );

          continue;
        }
    
      } /* if we have sort list */

    } /* if valid index */

  } /* for all sort indexes */

  return;

}

void new_in_sort_lists ( UBYTE db_ctr,
                         UBYTE fld_ctr,
                         UBYTE record_num )
{
  UBYTE sort_index_ctr;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord + fld_ctr;

  /* make a note that this record needs to be sorted */

  /* for all existing sort indexes */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] NEQ INVALID_SORT_INDEX )
    {
      /* add it at the end like all sorted records + INVALID RECORD + .... */
      tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][tmp_ptrFieldRecord -> UsedRecords] 
        = record_num;
    }
  }

  return;

}

void update_in_sort_lists ( UBYTE db_ctr,
                            UBYTE fld_ctr,
                            UBYTE record_num )
{
  UBYTE sort_index_ctr,
        rec_ctr;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord + fld_ctr;

  /* make a note that this record needs to be sorted */

  /* for all existing sort indexes */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] NEQ INVALID_SORT_INDEX )
    {
      /* search entry in sorted records */
      rec_ctr = 0;
      while( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] NEQ INVALID_RECORD_NUM )
      {
        if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] EQ record_num )
        {
          /* found in sorted records, 
             now delete this entry */

          memmove( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr,                    /* dest */
                   (const char*)(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr + 1), /* source */
                   tmp_ptrFieldRecord -> UsedRecords - rec_ctr );                                  /* size */   

          tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][tmp_ptrFieldRecord -> UsedRecords] 
            = record_num;

          break;
        }

        ++rec_ctr;
      }

    } /* NEQ INVALID_SORT_INDEX */

  } /* for sort_index_ctr */

  return;
}

void delete_in_sort_lists ( UBYTE db_ctr,
                            UBYTE fld_ctr,
                            UBYTE record_num )
{
  UBYTE sort_index_ctr,
        rec_ctr;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord + fld_ctr;

  /* make a note that this record needs to be sorted */

  /* for all existing sort indexes */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] NEQ INVALID_SORT_INDEX )
    {
      /* search entry in sorted records */
      rec_ctr = 0;
      while( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] NEQ INVALID_RECORD_NUM )
      {
        if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] EQ record_num )
          break;

        ++rec_ctr;
      }

      if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] NEQ INVALID_RECORD_NUM )
      {
        /* found in sorted records, 
           now delete this entry */

        memmove( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr,
                 (const char*)(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr + 1),
                 tmp_ptrFieldRecord -> UsedRecords - rec_ctr );

        tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][tmp_ptrFieldRecord -> UsedRecords] 
          = INVALID_RECORD_NUM;

      } else {

        /* search in UN-sorted records */

        ++rec_ctr;
        while( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] NEQ INVALID_RECORD_NUM )
        {
          if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][rec_ctr] EQ record_num )
          {
            /* delete this entry */
            memmove( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr,
                     (const char*)(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] + rec_ctr + 1),
                     tmp_ptrFieldRecord -> UsedRecords - rec_ctr );

            tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][tmp_ptrFieldRecord -> UsedRecords] 
              = INVALID_RECORD_NUM;

            break;
          }
          ++rec_ctr;
        }

      }

    } /* NEQ INVALID_SORT_INDEX */

  } /* for sort_index_ctr */

  return;
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: insertion_sort       |
+--------------------------------------------------------------------+

    PURPOSE : Sorts the user data records in FFS based on insertion sort method
*/
void insertion_sort ( UBYTE*        sort_list,
                      UBYTE         num_of_elements,
                      T_COMP_FUNC   compare_function, 
                      ULONG         flags,
                      int           db_handle,
                      USHORT        field_id )
{
  UBYTE rec_ctr;

  /* sanity check */
  if( num_of_elements EQ 0 )
    return;

  if( sort_list[0] EQ INVALID_RECORD_NUM )
  {
    /* This is beginning of new sort list,
       Let's add first element */
    sort_list[0] = sort_list[1];
    sort_list[1] = INVALID_RECORD_NUM;
  }


  /* So now here we have list something like this
     { Sorted records } + INVALID_RECORD_NUM + { UNsorted records }
  */

  /* let's get to Unsorted records */
  rec_ctr = 1;
  while( sort_list[rec_ctr] NEQ INVALID_RECORD_NUM )
  {
    ++rec_ctr;
  }
  ++rec_ctr;

  /* rec_ctr is now pointing at UNsorted records */

  /* now add UNsorted records one by one */

  /* Not required "( sort_list[rec_ctr] NEQ INVALID_RECORD_NUM )", right ? */

  while( rec_ctr <= num_of_elements )
  {
    insert_element( sort_list,
                    rec_ctr,                /* num_of_elements */
                    0,                      /* bottom */
                    rec_ctr - 2,            /* top */
                    sort_list[rec_ctr],     /* record_num to be inserted */
                    compare_function,
                    flags,
                    db_handle,
                    field_id );

    ++rec_ctr;
  }

  /* done ! */
  return;
}

void insert_element ( UBYTE*        sort_list,
                      UBYTE         num_of_elements,
                      UBYTE         bottom,
                      UBYTE         top,
                      UBYTE         record_num,
                      T_COMP_FUNC   compare_function, 
                      ULONG         flags,
                      int           db_handle,
                      USHORT        field_id )
{
  UBYTE middle = ( top + bottom ) / 2;

  /* use binary algorithm to find the right place for insertion */
  while( middle NEQ bottom )
  {
    if( compare_function( db_handle, 
                          field_id,
                          sort_list[middle],
                          record_num,
                          flags ) 
        < 0 )
    {
      bottom = middle;
    } else {
      top = middle;
    }
    middle = ( top + bottom ) / 2;

  } /* while( middle NEQ bottom ) */

  if( compare_function( db_handle, 
                        field_id,
                        sort_list[bottom],
                        record_num,
                        flags ) 
      < 0 )
  {

    if( compare_function( db_handle, 
                          field_id,
                          sort_list[top],
                          record_num,
                          flags ) 
        < 0 )
    {
      /* insert at top + 1 */
      memmove( sort_list + top + 2,            /* dest */
               sort_list + top + 1,            /* source */
               num_of_elements - (top + 1) );  /* size */

      sort_list[top + 1] = record_num;

    } else {

      /* insert at bottom + 1 */
      memmove( sort_list + bottom + 2,            /* dest */
               sort_list + bottom + 1,            /* source */
               num_of_elements - (bottom + 1) );  /* size */

      sort_list[bottom + 1] = record_num;

    } /* if top_th < insert_th */

  } else {

    /* insert at bottom */
    memmove( sort_list + bottom + 1,       /* dest */
             sort_list + bottom,           /* source */
             num_of_elements - bottom );   /* size */

    sort_list[bottom] = record_num;

  } /* if bottom_th < insert_th */

  return;
}

#endif /* INSERTION_SORT */


/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: internal_db_sort     |
+--------------------------------------------------------------------+

    PURPOSE : (Internal) Creates or updates a sort index for
              a given (database, field)
*/
T_DB_CODE internal_db_sort ( int          db_handle, 
                             USHORT       field_id, 
                             UBYTE        sort_index, 
                             T_COMP_FUNC  compare_function, 
                             ULONG        flags,
                             T_DB_SORT    sort_type )
{
  UBYTE  fld_ctr,
         sort_index_ctr,
         remb_sort_index_ctr,
         tmp_byte,
         bit_num,
         record_num;

  UBYTE* sort_list;
  UBYTE* copy_sort_list;

  UBYTE*  tmp_byte_ptr;

  T_DB_CODE  db_ret_code;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  UBYTE* field_data;
  UBYTE  field_file[FILENAME_LEN];

  TRACE_FUNCTION("internal_db_sort()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Check if this is existing sort index */
  remb_sort_index_ctr = MAX_NUM_OF_SORT_INDEXS;

  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] EQ sort_index )
      break;

    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] EQ INVALID_SORT_INDEX )
      remb_sort_index_ctr = sort_index_ctr;

  }

  if( sort_index_ctr EQ MAX_NUM_OF_SORT_INDEXS )
  { 
    /* ok, this is new index, so
       Check if limit for sort list corresponding to field_id (for a given database) 
       has reached limit, MAX_NUM_OF_SORT_INDEXS. If so, return DB_FULL. */

    if( remb_sort_index_ctr EQ MAX_NUM_OF_SORT_INDEXS )
      DB_RETURN( DB_FULL );
    
    sort_index_ctr = remb_sort_index_ctr;

    /* update file , "~/dbm/DD_<db_handle>" for SortIndexes. */

    /* create the field data that is to be written */

    DB_MALLOC( field_data, MAX_NUM_OF_SORT_INDEXS );
    memcpy( field_data, tmp_ptrFieldRecord -> SortIndexList, MAX_NUM_OF_SORT_INDEXS );

    field_data[sort_index_ctr] = sort_index;

    sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

    db_ret_code = 
      update_field_data_in_FFS ( (const char*)field_file,
                                 &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                                 db_handle,
#endif
                                 fld_ctr,
                                 T_DB_FIELD_RECORD_SIZE,
                                 field_data,
                                 SortIndexList_OFFSET,
                                 MAX_NUM_OF_SORT_INDEXS );
    if( db_ret_code NEQ DB_OK )
    {
      DB_MFREE( field_data );
      DB_RETURN( db_ret_code );
    }

    DB_MFREE( field_data );
    /* updation of DD_<db_handle> is over */
  }

  if( ( sort_type EQ QUICK_SORT )                                    AND
      ( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] NEQ NULL ) )
  {
    /* free earlier list */
    DB_MFREE( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] );
    tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] = NULL;
  }

  /* create sort list (if not there) */
  if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] NEQ NULL )
  {
    sort_list = tmp_ptrFieldRecord -> SortedLists[sort_index_ctr];
  } else {

#ifndef INSERTION_SORT
    DB_MALLOC( sort_list, tmp_ptrFieldRecord -> NumOfRecords );
    memset( sort_list, INVALID_RECORD_NUM, tmp_ptrFieldRecord -> NumOfRecords );

    copy_sort_list = sort_list;
#else
    DB_MALLOC( sort_list, tmp_ptrFieldRecord -> NumOfRecords + 1 );
    memset( sort_list, INVALID_RECORD_NUM, tmp_ptrFieldRecord -> NumOfRecords + 1 );

    if( sort_type EQ INCREMENTAL_SORT )
    {
      /* the first element would be INVALID_RECORD_NUM (0xFF) */
      copy_sort_list = sort_list + 1;

    } else {

      copy_sort_list = sort_list;
    }
#endif /* INSERTION_SORT */

    /* The list of available records is prepared from RecordBitMap */

    tmp_byte_ptr = tmp_ptrFieldRecord -> RecordBitMap + RECORD_BITMAP_SIZE - 1;
    record_num = 0;

    while( record_num < tmp_ptrFieldRecord -> NumOfRecords )
    {
      tmp_byte = *tmp_byte_ptr;
      for( bit_num = 0; bit_num < MAX_BIT_POS; ++bit_num )
      {
        ++record_num;

        if( record_num > tmp_ptrFieldRecord -> NumOfRecords )
          break;

        if( tmp_byte & 0x01 )
        {
          *copy_sort_list = record_num;
          ++copy_sort_list;
        }


        tmp_byte >>= 1;  
      }
      --tmp_byte_ptr;

    } /* population of records */

  } /* sort_list is NULL */

  /* Sort the records with given "comparison_function" (it takes record numbers, 
     field_id and db_handle). Note that the sorted list would be kept in RAM 
     (structure SortedLists, part of field record) till db_flush is called and 
     cleared in db_flush. */

  if( sort_type EQ QUICK_SORT )
  {
    quick_sort( sort_list,
                tmp_ptrFieldRecord -> UsedRecords,   /* only used records */
                compare_function,
                flags,
                db_handle,
                field_id );
  }

#ifdef INSERTION_SORT

  if( sort_type EQ INCREMENTAL_SORT )
  {
    insertion_sort( sort_list,
                    tmp_ptrFieldRecord -> UsedRecords,   /* only used records */
                    compare_function,
                    flags,
                    db_handle,
                    field_id );
  }

#endif /* INSERTION_SORT */

  /* writing sorted list to FFS would be done in db_flush */
 
  /* Update corresponding field SortIndexList in RAM structures. */

  tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] = sort_index;
  tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] = sort_list;

  DB_RETURN( DB_OK );
}

























/***********************/
/* INTERFACE FUNCTIONS */
/***********************/


/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_init              |
+--------------------------------------------------------------------+

    PURPOSE : Initialises database manager
*/
 void db_init ( void )
{
  UBYTE db_ctr;

#ifdef FFS_CLOSE_BEFORE_OPEN
  UBYTE fld_ctr;
#endif

  TRACE_FUNCTION("db_init()");

  /* 1) Check DBM_State and if already initialized, return DB_OK */
  if ( DBM_State EQ DBM_INITIALISED )
    return;

  /* 2)	Initialize DbmMaster structure */
  for ( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
  {
    DbmMaster[db_ctr].DBState = UNUSED_ENTRY;

#ifdef FFS_CLOSE_BEFORE_OPEN 

    for( fld_ctr = 0; fld_ctr < MAX_OPEN_READ_PER_DB; ++fld_ctr )
    {
      DbmMaster[db_ctr].READ_OPEN_FIELDS[fld_ctr].fld_ctr = INVALID_FLD_CTR;
    }
    for( fld_ctr = 0; fld_ctr < MAX_OPEN_WRITE_PER_DB; ++fld_ctr )
    {
      DbmMaster[db_ctr].WRITE_OPEN_FIELDS[fld_ctr].fld_ctr = INVALID_FLD_CTR;
    }
    DbmMaster[db_ctr].old_read = 0;
    DbmMaster[db_ctr].old_write = 0;

#endif

  }

  UsedDBs = 0;

#ifdef _SIMULATION_
  {
    T_FFS_SIZE ffs_ret_code = ffs_init();

    TRACE_EVENT_P1("Initialization of FFS Simulation %d", ffs_ret_code );

    /* Delete existing database */
    init_RAM_with_FFS();

    for( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
    {
      if( DbmMaster[db_ctr].DBState NEQ UNUSED_ENTRY )
        db_remove( DbmMaster[db_ctr].DBDirectory );
    }

    db_exit();
  }
#endif

  return;
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: init_RAM_with_FFS    |
+--------------------------------------------------------------------+

    PURPOSE : Initialise DBM RAM structures with FFS data
*/
 T_DB_CODE init_RAM_with_FFS ( void )
{
  /* Initializing "DBM related data" in RAM from FFS */

  T_FFS_FD            ffs_dbm_fd;
  T_FFS_SIZE          ffs_ret_code;
  T_FFS_STAT          ffs_dir_stat;
  UBYTE*              db_buffer;
  UBYTE*              remb_db_buffer;
  UBYTE*              tmp_byte_ptr;
  UBYTE*              field_buffer;
  UBYTE*              remb_field_buffer;
  UBYTE               db_ctr,
                      fld_ctr,
                      sort_index_ctr,
                      tmp_byte,
                      tmp_num_records,
                      bit_num,
                      record_num;
  char                field_file[FILENAME_LEN];
  char*               field_file_ptr = field_file;
  char*               dbm_file = DB_MASTER_FILE;
  T_DBM_FIELDRECORD*  tmp_ptrFieldRecord;


  TRACE_FUNCTION("init_RAM_with_FFS()");

  /* Check the presence of FFS directory DB_DIR */
  ffs_ret_code = ffs_stat( DB_DIR, &ffs_dir_stat );

  TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_stat %d", ffs_ret_code );
  /* error values of ffs_ret_code are handled later */

  if( ffs_ret_code >= EFFS_OK )
  {
    /* Directory is present */
     
    /* Synchronize DB related data between FFS and RAM 
       i.e. structures DbmMasterRecord and corresponding DbmFieldRecord. */

    /* For DbmMaster */

    DB_FFS_OPEN( ffs_dbm_fd, dbm_file, FFS_O_RDONLY );

    if( (ffs_dbm_fd EQ EFFS_NAMETOOLONG ) OR
        (ffs_dbm_fd EQ EFFS_BADNAME     ) OR
        (ffs_dbm_fd EQ EFFS_NOTFOUND    ) OR
        (ffs_dbm_fd EQ EFFS_INVALID     ) OR
        (ffs_dbm_fd EQ EFFS_LOCKED      ) )

    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:DB_FFS_OPEN %d", ffs_dbm_fd );
      return DB_FAIL;
    }

    if( ffs_dbm_fd < EFFS_OK )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:DB_FFS_OPEN %d", ffs_dbm_fd );
      LastFFS_ReturnCode = ffs_dbm_fd;
      return DB_FAIL_FS;
    }

    ffs_ret_code = ffs_seek( ffs_dbm_fd, 0, FFS_SEEK_SET );

    if( ( ffs_ret_code EQ EFFS_BADFD )   OR
        ( ffs_ret_code EQ EFFS_INVALID ) OR
        ( ffs_ret_code EQ EFFS_BADOP )   )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_seek %d", ffs_ret_code );
      DB_FFS_CLOSE( ffs_dbm_fd );
      return DB_FAIL;
    }

    /* buffer for master records */
    DB_MALLOC( db_buffer, T_DB_MASTER_RECORD_SIZE ); 
    remb_db_buffer = db_buffer;

    for( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
    {
      db_buffer = remb_db_buffer;
      ffs_ret_code = ffs_read( ffs_dbm_fd, db_buffer, T_DB_MASTER_RECORD_SIZE );

      if( ( ffs_ret_code EQ EFFS_BADFD ) OR
          ( ffs_ret_code EQ EFFS_BADOP ) )
      {
        TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_read %d", ffs_ret_code );
        DB_FFS_CLOSE( ffs_dbm_fd );
        DB_MFREE( db_buffer );
        return DB_FAIL;
      }

      if( ffs_ret_code EQ 0 ) /* number of bytes read EQ 0 */
        break;

      if( *db_buffer EQ 0xFF ) /* if invalid database */
        continue;

      memcpy( DbmMaster[db_ctr].DBDirectory, db_buffer, MAX_LEN_DIRECTORY );
      db_buffer += MAX_LEN_DIRECTORY;

      DbmMaster[db_ctr].NumOfFiles = *db_buffer;
      ++db_buffer;

      DbmMaster[db_ctr].Tracked_Clean = ( *db_buffer ) ? TRACKED_AND_CLEAN : CLEAN;
      ++db_buffer;

      DbmMaster[db_ctr].UsedFiles = 0;

      DbmMaster[db_ctr].DBState = CLOSED;

      ++UsedDBs;

      /* we need not "seek" here file pointer would have moved to next */
    }
    
    DB_FFS_CLOSE( ffs_dbm_fd ); 
    DB_MFREE( remb_db_buffer ); /* freeing db_buffer */

    /* For FieldRecords */

    /* buffer for field records */
    DB_MALLOC( field_buffer, T_DB_FIELD_RECORD_SIZE ); 
    remb_field_buffer = field_buffer;

    for( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
    {

      if( DbmMaster[db_ctr].DBState EQ UNUSED_ENTRY )
        continue;

      /* Field file = DD_<pos. in DD_master> */

      sprintf( field_file, "%s/DD_%d", DB_DIR, db_ctr);  

      DB_FFS_OPEN( ffs_dbm_fd, field_file_ptr, FFS_O_RDONLY );

      if(  ( ffs_dbm_fd EQ EFFS_NOTFOUND ) OR   /* Field file not found */
           ( ffs_dbm_fd EQ EFFS_NUMFD )    OR   /* FFS specific errors */
           ( ffs_dbm_fd EQ EFFS_LOCKED)    )
      {
        TRACE_EVENT_P1( "init_RAM_with_FFS:DB_FFS_OPEN %d", ffs_dbm_fd );
        DB_MFREE( field_buffer );
        return DB_FAIL;
      }

      ffs_ret_code = ffs_seek( ffs_dbm_fd, 0, FFS_SEEK_SET );

      if( ( ffs_ret_code EQ EFFS_BADFD )   OR
          ( ffs_ret_code EQ EFFS_INVALID ) OR
          ( ffs_ret_code EQ EFFS_BADOP )   )
      {
        TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_seek %d", ffs_ret_code );
        DB_FFS_CLOSE( ffs_dbm_fd );
        DB_MFREE( field_buffer );
        return DB_FAIL;
      }

      /* Allocate memory for field records */
      DB_MALLOC( DbmMaster[db_ctr].ptrFieldRecord, 
                  DbmMaster[db_ctr].NumOfFiles * sizeof(T_DBM_FIELDRECORD) ); 

      tmp_ptrFieldRecord = DbmMaster[db_ctr].ptrFieldRecord;

      for( fld_ctr = 0; fld_ctr < DbmMaster[db_ctr].NumOfFiles; ++fld_ctr )
      {
        field_buffer = remb_field_buffer;

        ffs_ret_code = ffs_read( ffs_dbm_fd, field_buffer, T_DB_FIELD_RECORD_SIZE );

        if( (ffs_ret_code EQ EFFS_BADFD ) OR
            (ffs_ret_code EQ EFFS_BADOP ) )
        {
          TRACE_EVENT_P2( "init_RAM_with_FFS:ffs_seek %d, fld_ctr %d", ffs_ret_code, fld_ctr );
          DB_FFS_CLOSE ( ffs_dbm_fd );
          DB_MFREE( field_buffer );
          DB_MFREE( DbmMaster[db_ctr].ptrFieldRecord );
          return DB_FAIL; 
        }

        if( ffs_ret_code EQ 0 ) /* number of bytes read  EQ 0 */
        {
          /* we need not go further, just make rest of field ids as invalid (not existing) */
          for( ; fld_ctr < DbmMaster[db_ctr].NumOfFiles; ++fld_ctr )
          {
            tmp_ptrFieldRecord -> FieldID	= INVALID_FIELD_ID;
            ++tmp_ptrFieldRecord;
          }
          break;
        }

        /* Syncing of field records */
        
        tmp_ptrFieldRecord -> FieldID	  = *field_buffer;
        tmp_ptrFieldRecord -> FieldID <<= MAX_BIT_POS;
        ++field_buffer;
        tmp_ptrFieldRecord -> FieldID	 |= *field_buffer;
        ++field_buffer;

        if ( tmp_ptrFieldRecord -> FieldID NEQ INVALID_FIELD_ID )
        {
          tmp_ptrFieldRecord -> DBType = *field_buffer;
          ++field_buffer;

          tmp_ptrFieldRecord -> RecordSize = *field_buffer;
          tmp_ptrFieldRecord -> RecordSize <<= MAX_BIT_POS;
          ++field_buffer;
          tmp_ptrFieldRecord -> RecordSize |= *field_buffer;
          ++field_buffer;

          tmp_ptrFieldRecord -> NumOfRecords = *field_buffer;
          ++field_buffer;

          for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
          {
            tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] = *field_buffer;
            tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] = NULL;
            ++field_buffer;
          }

          memcpy( tmp_ptrFieldRecord -> RecordBitMap, field_buffer, RECORD_BITMAP_SIZE );
          field_buffer += RECORD_BITMAP_SIZE;

          tmp_ptrFieldRecord -> Clean = *field_buffer;

#ifndef FFS_CLOSE_BEFORE_OPEN
          tmp_ptrFieldRecord -> FFSFileHandle = NULL; 
#endif
          /* we need to get size of user file <DBDirectory>/UD_<field_id> 
             NextRecordNum = (size of user file) / record size */
          tmp_ptrFieldRecord -> NextRecordNum = 
                  cal_NextRecordNum ( (const char*)DbmMaster[db_ctr].DBDirectory, 
                                      tmp_ptrFieldRecord -> FieldID,
                                      tmp_ptrFieldRecord -> RecordSize );
                                            
          /* Update database records for UsedFiles, Clean, and UsedRecords */

          DbmMaster[db_ctr].UsedFiles += 1;

          if( DbmMaster[db_ctr].Tracked_Clean & TRACKED ) /* if db is tracked */
            if( NOT tmp_ptrFieldRecord -> Clean )          /* if field is not clean */
              DbmMaster[db_ctr].Tracked_Clean &= ~ CLEAN ;    /* make db as not clean */


          /* For calculating UsedRecords */

          tmp_byte_ptr = tmp_ptrFieldRecord -> RecordBitMap + RECORD_BITMAP_SIZE - 1;
          tmp_num_records = 0;
          record_num = 0;

          while( record_num < tmp_ptrFieldRecord -> NumOfRecords )
          {
            tmp_byte = *tmp_byte_ptr;

            for( bit_num = 0; bit_num < MAX_BIT_POS; ++bit_num )
            {
              ++record_num;

              if( record_num > tmp_ptrFieldRecord -> NumOfRecords )
                break;

              if( tmp_byte & 0x01 )
                 ++tmp_num_records;

              tmp_byte >>= 1;  
            }

            --tmp_byte_ptr;
          }

          tmp_ptrFieldRecord -> UsedRecords = tmp_num_records;

        } /* FieldID NEQ INVALID_FIELD_ID */

        /* go to next field record */

        /* seek is not required here ! */

        ++tmp_ptrFieldRecord;   /* next field */ 

      } /* for( fld_ctr ) ... all fields */

      /* close the DD_<db_handle> file */
      DB_FFS_CLOSE( ffs_dbm_fd );

    } /* ffs_ret_code >= EFFS_OK */

    DB_MFREE( remb_field_buffer );  /* freeing field buffer */

    DBM_State = DBM_INITIALISED;

    return DB_OK;

  } /* if( ffs_ret_code >= EFFS_OK ) */

  /* Directory not present */
  if( ffs_ret_code EQ EFFS_NOTFOUND )
  {
    /* Create DBM directory */
    ffs_ret_code = ffs_mkdir( DB_DIR ); 
    
    if( (ffs_ret_code EQ EFFS_NAMETOOLONG ) OR
        (ffs_ret_code EQ EFFS_BADNAME     ) )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_mkdir %d", ffs_ret_code );
      return DB_FAIL;
    }

    if( ffs_ret_code < EFFS_OK )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:ffs_mkdir %d", ffs_ret_code );
      LastFFS_ReturnCode = ffs_ret_code;
      return DB_FAIL_FS;
    }

    /* Create empty file DD_master
       FFS_O_EXCL => Generate error if FFS_O_CREATE is also specified and 
                     the file already exists. */

    DB_FFS_OPEN( ffs_dbm_fd, dbm_file, FFS_O_CREATE | FFS_O_RDWR | FFS_O_EXCL );

    if( (ffs_dbm_fd EQ EFFS_NAMETOOLONG ) OR
        (ffs_dbm_fd EQ EFFS_BADNAME     ) )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:DB_FFS_OPEN %d", ffs_dbm_fd );
      ffs_ret_code = ffs_remove( DB_DIR ); /* undo creating of DBM directory */
      return DB_FAIL;
    }

    if( ffs_dbm_fd < EFFS_OK )
    {
      TRACE_EVENT_P1( "init_RAM_with_FFS:DB_FFS_OPEN %d", ffs_dbm_fd );
      LastFFS_ReturnCode = ffs_dbm_fd;
      ffs_ret_code = ffs_remove( DB_DIR ); /* undo creating of DBM directory */
      return DB_FAIL_FS;
    }

    DB_FFS_CLOSE( ffs_dbm_fd );

    DBM_State = DBM_INITIALISED;

    return DB_OK;
  }

  /* Unexpected error: Not a directory */
  return DB_FAIL;

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_create            |
+--------------------------------------------------------------------+

    PURPOSE : Create a new database in the given directory of the FFS. 
              The directory must not to exist yet, it will be created.
*/
int db_create ( const char* directory,
                UBYTE       num_of_fields,
                BOOL        tracked )
{
  int         db_handle;
  T_DB_CODE   db_ret_code;
  UBYTE       fld_ctr;
  S8          db_ctr;
  T_FFS_SIZE  ffs_ret_code;
  T_FFS_FD    ffs_fd;
  char        field_file[FILENAME_LEN];
  char*       field_file_ptr = field_file;
  UBYTE*      dbm_data;
  UBYTE*      remb_dbm_data;

  TRACE_FUNCTION("db_create()");

  /* If DBM is not in initialized state, call init_RAM_with_FFS (). */
  if( DBM_State NEQ DBM_INITIALISED )
  {
    T_DB_CODE db_ret_code;

    db_ret_code = init_RAM_with_FFS();

    if( db_ret_code NEQ DB_OK )
      DB_RETURN( db_ret_code );
  }

  /* See if you have reached the maximum number of DBs, ? 
     (UsedDBs+1) > MAX_DBs */

  if( UsedDBs EQ MAX_DBs )
    DB_RETURN( DB_FULL );


  /* Check DbmMaster if already database for "directory" exists. 
     If yes, return error "DB_EXISTS" */

  for( db_ctr = (MAX_DBs - 1); db_ctr >= 0; --db_ctr )
  {
    if ( DbmMaster[db_ctr].DBState NEQ UNUSED_ENTRY )
    {
        if( memcmp( DbmMaster[db_ctr].DBDirectory, 
                    directory, 
                    strlen( directory )          ) EQ 0 )
          DB_RETURN( DB_EXISTS );
    } else {
      db_handle = db_ctr;
    }
  }

  /* sanity check */
  if( num_of_fields <= 0 )
    DB_RETURN( DB_FAIL );

  /* Create a directory, "<DBDirectory>" (DBDirectory = directory) */
  ffs_ret_code = ffs_mkdir( directory ); 

  if( ffs_ret_code < EFFS_OK ) 
  {
    TRACE_EVENT_P1( "db_create:ffs_mkdir %d", ffs_ret_code );
    LastFFS_ReturnCode = ffs_ret_code;
    DB_RETURN( DB_FAIL_FS );
  }


  /* Create a empty file, "~/dbm/DD_<position in DD_master = db_handle>"; 
     if this FFS operation fails, we need to delete the directory 
     before returning error. 
  */
  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle);  

  TRACE_EVENT_P1( "field_file %s", field_file );

  /* FFS_O_EXCL => Generate error if FFS_O_CREATE is also specified and 
                   the file already exists. */
  DB_FFS_OPEN( ffs_fd, field_file_ptr, FFS_O_CREATE | FFS_O_RDWR | FFS_O_EXCL ); 

  if( ( ffs_fd EQ EFFS_EXISTS )      OR
      ( ffs_fd EQ EFFS_NAMETOOLONG ) OR
      ( ffs_fd EQ EFFS_BADNAME )     OR
      ( ffs_fd EQ EFFS_INVALID )     )
  {
    TRACE_EVENT_P1( "db_create:DB_FFS_OPEN %d", ffs_fd );
    ffs_fd = ffs_remove( directory ); 
    DB_RETURN( DB_FAIL );
  }

  if( ffs_fd < EFFS_OK )
  {
    TRACE_EVENT_P1( "db_create:DB_FFS_OPEN %d", ffs_fd );
    LastFFS_ReturnCode = ffs_fd;
    ffs_ret_code = ffs_remove( directory ); 
    DB_RETURN( DB_FAIL_FS );
  }

  /* DB_FFS_CLOSE( ffs_fd ); we will close it in db_flush */

#ifdef FFS_OPEN_PROBLEM_PATCH
  DB_FFS_CLOSE( ffs_fd );
#endif

#ifdef FFS_CLOSE_BEFORE_OPEN
  /* no we do not close it */
#endif

  /* Update FFS data i.e. file, "~/dbm/DD_master". */

  /* prepare the dbm data and write */
  DB_MALLOC( dbm_data, T_DB_MASTER_RECORD_SIZE );
  memset( dbm_data, 0, T_DB_MASTER_RECORD_SIZE );
  remb_dbm_data = dbm_data;

  memcpy( dbm_data, directory, MAX_LEN_DIRECTORY );
  dbm_data += MAX_LEN_DIRECTORY;
  
  *dbm_data = num_of_fields;
  ++dbm_data;

  *dbm_data = ( tracked ) ? FFS_TRACKED : FFS_NOT_TRACKED;

  dbm_data = remb_dbm_data;

  db_ret_code = 
    update_dbm_data_in_FFS ( DB_MASTER_FILE,
                             (UBYTE) db_handle,   /* to supress warning */
                             T_DB_MASTER_RECORD_SIZE,
                             dbm_data,
                             0,
                             T_DB_MASTER_RECORD_SIZE );

  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( dbm_data );
    ffs_ret_code = ffs_remove( directory );
    DB_FFS_CLOSE( ffs_fd );
    ffs_ret_code = ffs_remove( field_file );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( dbm_data );
  /* updation of DD_master done 
     now update RAM structures  */

  memcpy( DbmMaster[db_handle].DBDirectory, 
          directory, 
          strlen(directory) );

  DbmMaster[db_handle].NumOfFiles    = num_of_fields;
  DbmMaster[db_handle].UsedFiles     = 0;  
  DbmMaster[db_handle].Tracked_Clean = ( tracked ) ? TRACKED_AND_CLEAN : CLEAN;
  DbmMaster[db_handle].DBState       = OPEN;
  DbmMaster[db_handle].FFSFileHandle = ffs_fd;

#ifdef FFS_CLOSE_BEFORE_OPEN

  /* No files corresponding to fields have been opened 
     This initialization is already done in db_init */

#endif
  
  /* Allocate memory for storing field information 
     and initialize the memory with "FF" */

  DB_MALLOC( DbmMaster[db_handle].ptrFieldRecord, 
              num_of_fields * sizeof(T_DBM_FIELDRECORD) );

  for( fld_ctr = 0; fld_ctr < num_of_fields; ++fld_ctr )
  {
    DbmMaster[db_handle].ptrFieldRecord[fld_ctr].FieldID = INVALID_FIELD_ID; 
  }

  UsedDBs++;

  DB_RETURN( db_handle );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_open              |
+--------------------------------------------------------------------+

    PURPOSE : Open an existing database
*/
int db_open ( const char *directory )
{
  T_DB_CODE  db_ret_code;
  UBYTE      db_ctr;

  TRACE_FUNCTION("db_open()");

  /* If DBM is not in initialized state, call init_RAM_with_FFS (). */
  if( DBM_State NEQ DBM_INITIALISED )
  {
    db_ret_code = init_RAM_with_FFS();

    if( db_ret_code NEQ DB_OK )
      DB_RETURN( db_ret_code );
  }

  /* Search for "directory" in DbmMaster */
  for( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
  {
    if( DbmMaster[db_ctr].DBState NEQ UNUSED_ENTRY )
    {
        if( memcmp( DbmMaster[db_ctr].DBDirectory, 
                    directory, 
                    strlen(directory)                     ) EQ 0 )
          break;
    } 
  }

  /* If "directory" not found, return DB_FAIL. */
  if( db_ctr EQ MAX_DBs )
    DB_RETURN( DB_INVALID_DB );

  /* Update DBState as "OPEN". */
  DbmMaster[db_ctr].DBState = OPEN;

  /* Return corresponding DBHandle. */
  DB_RETURN( db_ctr );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_create_field      |
+--------------------------------------------------------------------+

    PURPOSE : Create a new elementary file of a given type with a given 
              numeric identifier and a given record size.
*/
T_DB_CODE db_create_field ( int         db_handle, 
                            T_DB_TYPE   db_type, 
                            USHORT      field_id, 
                            USHORT      record_size, 
                            USHORT      num_of_records )
{
  T_DB_CODE          db_ret_code;
  char               user_field_file[FILENAME_LEN],
                     field_file[FILENAME_LEN];
  char*              user_field_file_ptr = user_field_file;
  char*              field_file_ptr = field_file;
  UBYTE              fld_ctr,
                     sort_index_ctr;
  T_FFS_SIZE         ffs_ret_code;
  T_FFS_FD           ffs_fd_user_field_file;
  UBYTE*             field_data;
  UBYTE*             remb_field_data;
  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;  


  TRACE_FUNCTION("db_create_field()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field id found, return error DB_FIELD_EXISTS */
  if( field_id_search( db_handle, field_id ) NEQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_FIELD_EXISTS );

  /* If limit for number of fields for this DB has been reached, return error DB_FULL. */
  if( DbmMaster[db_handle].UsedFiles  EQ 
      DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_FULL );

  /* Search for free field entry in field records corresponding to the database. */
  for( fld_ctr = 0; fld_ctr < DbmMaster[db_handle].NumOfFiles; ++fld_ctr )
  {
    if( DbmMaster[db_handle].ptrFieldRecord[fld_ctr].FieldID EQ INVALID_FIELD_ID )
      break;
  }

  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
  { /* this is "inconsistency"; should never happen */
    DB_RETURN( DB_FAIL );
  }

  /* sanity check */
  if( ( num_of_records <= 0 ) OR
      ( record_size <= 0 )    )
  {
    DB_RETURN( DB_FAIL );    
  }

  /* Create file "<DBDirectory>/UD_<field_id>" */
  sprintf( user_field_file, "%s/UD_%d", DbmMaster[db_handle].DBDirectory, field_id );

  /* FFS_O_EXCL => Generate error if FFS_O_CREATE is also specified and 
                   the file already exists. */
#ifndef FFS_CLOSE_BEFORE_OPEN

  DB_FFS_OPEN( ffs_fd_user_field_file, user_field_file_ptr, FFS_O_CREATE | FFS_O_RDWR | FFS_O_EXCL ); 

#else

  ffs_fd_user_field_file = 
    db_open_user_field_file( db_handle, 
                             fld_ctr, 
                             user_field_file_ptr, 
                             FFS_O_CREATE | FFS_O_RDWR | FFS_O_EXCL );
#endif

  if( ( ffs_fd_user_field_file EQ EFFS_EXISTS )      OR
      ( ffs_fd_user_field_file EQ EFFS_NAMETOOLONG ) OR
      ( ffs_fd_user_field_file EQ EFFS_BADNAME )     OR
      ( ffs_fd_user_field_file EQ EFFS_INVALID )     )
  {
    TRACE_EVENT_P1( "db_create_field:DB_FFS_OPEN %d", ffs_fd_user_field_file );
    DB_RETURN( DB_FAIL );
  }

  if( ffs_fd_user_field_file < EFFS_OK )
  {
    TRACE_EVENT_P1( "db_create_field:DB_FFS_OPEN %d", ffs_fd_user_field_file );
    LastFFS_ReturnCode = ffs_fd_user_field_file;
    DB_RETURN( DB_FAIL_FS );
  }

  /* We will close this file in db_flush */

#ifdef FFS_OPEN_PROBLEM_PATCH
  DB_FFS_CLOSE( ffs_fd_user_field_file );
#endif

  
  /* file "<DBDirectory>/UD_<field_id>" has been created 
     Now, Add new field entry in "~/dbm/DD_<db_handle>" */

  /* create the field data that needs to be written */

  DB_MALLOC( field_data, T_DB_FIELD_RECORD_SIZE );
  memset( field_data, 0, T_DB_FIELD_RECORD_SIZE );
  remb_field_data = field_data;

  *field_data = (UBYTE) ( (field_id & 0xFF00) >> MAX_BIT_POS ); 
  ++field_data;
  *field_data = (UBYTE) (field_id & 0x00FF);
  ++field_data;

  *field_data = db_type; 
  ++field_data;

  *field_data = (UBYTE) ( (record_size & 0xFF00) >> MAX_BIT_POS );
  ++field_data;
  *field_data = (UBYTE) (record_size & 0x00FF);
  ++field_data;

  *field_data = (UBYTE)num_of_records;
  field_data++;
  
  memset( field_data, 0xFF, MAX_NUM_OF_SORT_INDEXS ); 
  field_data += MAX_NUM_OF_SORT_INDEXS;

  memset( field_data, 0, RECORD_BITMAP_SIZE );
  field_data += RECORD_BITMAP_SIZE;

  *field_data = CLEAN; /* Clean is true */
  
  field_data = remb_field_data;

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  db_ret_code = 
    update_field_data_in_FFS ( (const char*)field_file,
                               &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                               db_handle,
#endif
                               fld_ctr,
                               T_DB_FIELD_RECORD_SIZE,
                               field_data,
                               0,
                               T_DB_FIELD_RECORD_SIZE );

  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( field_data );
    ffs_ret_code = ffs_remove( user_field_file );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( field_data );
  /* updation of DD_<db_handle> is over */

  /* Add new field entry in field records in RAM. */
  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  tmp_ptrFieldRecord -> FieldID       = field_id;
  tmp_ptrFieldRecord -> DBType        = db_type;
  tmp_ptrFieldRecord -> RecordSize    = record_size;
  tmp_ptrFieldRecord -> NumOfRecords  = (UBYTE)num_of_records;
  tmp_ptrFieldRecord -> UsedRecords   = 0; 
  tmp_ptrFieldRecord -> Clean         = CLEAN;

#ifndef FFS_CLOSE_BEFORE_OPEN
  tmp_ptrFieldRecord -> FFSFileHandle = ffs_fd_user_field_file;
#endif

  tmp_ptrFieldRecord -> NextRecordNum = 1;

  memset( tmp_ptrFieldRecord -> RecordBitMap, 0, RECORD_BITMAP_SIZE );

  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr]  = INVALID_SORT_INDEX;
    tmp_ptrFieldRecord -> SortedLists[sort_index_ctr]    = NULL;
  }

  /* Increment UsedFiles for the particular database in RAM */
  DbmMaster[db_handle].UsedFiles += 1;

  /* everyting ok */
  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_write_record      |
+--------------------------------------------------------------------+

    PURPOSE : Write length bytes at offset into the given record in 
              (database, field)
*/
int db_write_record ( int          db_handle,	 
                      USHORT       field_id, 
                      USHORT       record_num, 
                      USHORT       offset, 
                      USHORT       length, 
                      const UBYTE* buffer )
{
  T_DB_CODE  db_ret_code;

  UBYTE fld_ctr,
        tmp_byte,
        rec_ctr;

  char  user_field_file[FILENAME_LEN],
        field_file[FILENAME_LEN];              

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  UBYTE*  record_buffer;
  UBYTE*  field_data;

  BOOL this_is_new_record = TRUE;

  TRACE_FUNCTION("db_write_record()");

  TRACE_EVENT_P1("field_id %d", field_id);

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Check if record exceeds the record size (i.e. offset + length > record size); 
     if it exceeds, return error DB_INVALID_SIZE */
  if( (offset + length) > 
      tmp_ptrFieldRecord -> RecordSize )
    DB_RETURN( DB_INVALID_SIZE );

  /* Check if FFS file is already opened using FFSFileHandle, part of field record. 
     If it is not yet opened, open the FFS file and update FFSFileHandle. 
     This is taken care in read_user_record_from_FFS */

  /* User data file name is "<DBDirectory>/UD_<field_id>" 
     This would be used below */

  sprintf( user_field_file, 
           "%s/UD_%d", 
           DbmMaster[db_handle].DBDirectory, 
           tmp_ptrFieldRecord -> FieldID );

  if( record_num EQ 0 )
  {
    /* If given record is equal to zero, 
        o	Find a free record using RecordBitMap.
        o	If free record not found, return DB_FULL */

    record_num = 1 + search_clear_bit_in_bitmap( tmp_ptrFieldRecord -> RecordBitMap,
                                                 RECORD_BITMAP_SIZE );

    if( record_num > tmp_ptrFieldRecord -> NumOfRecords )
      DB_RETURN( DB_FULL );
                                             

  } else {
    /* If given record is not equal to zero, 
        o	Check if record exists using RecordBitMap; 
          if not found, proceed to writing..
        o	Read the record from "<DBDirectory>/UD_<field_id>" and 
          compare it with given record (partially if offset > 0). Memcmp would be used.
        o	If record match, return DB_OK. */

    /* record_num = bit_pos + (8 * bitmap_idx) + 1; 
       bitmap_idx  = (record_num - 1) / MAX_BIT_POS;
       bit_pos     = (record_num - 1) % MAX_BIT_POS;  */
    
    tmp_byte = 
      (tmp_ptrFieldRecord -> RecordBitMap)[ RECORD_BITMAP_SIZE - BITMAP_INDEX(record_num) - 1];

    if( tmp_byte & ( 0x01 << BIT_POS(record_num) )  )  /* record exists */
    {
      this_is_new_record = FALSE;

      DB_MALLOC( record_buffer, length); 

      db_ret_code = 
        read_user_record_from_FFS ( user_field_file,

#ifndef FFS_CLOSE_BEFORE_OPEN
                                    &(tmp_ptrFieldRecord -> FFSFileHandle),
#else
                                    db_handle,
                                    fld_ctr,
                                    &Dummy_FFSFileHandle,
#endif
                                    (UBYTE)record_num,
                                    tmp_ptrFieldRecord -> RecordSize,
                                    offset,
                                    length,
                                    record_buffer );
      if( db_ret_code NEQ DB_OK )
      {
        DB_MFREE( record_buffer );
        DB_RETURN( db_ret_code );
      }

      /* See if there is same data, if so, we need not overwrite */
      if( memcmp( record_buffer, buffer, length) EQ 0 )
      { /* matching data */
        DB_MFREE( record_buffer );
        DB_RETURN( DB_OK );
      }

      DB_MFREE( record_buffer );

    } /* record exists */

  } /* record_num EQ 0 ? */


  /* As per the request, write record in FFS in "<DBDirectory>/UD_<field_id>" */

  /* in case of new record, we need to create fill unfilled-data with FF */

  if( this_is_new_record )
  { /* new record */

    DB_MALLOC( record_buffer, tmp_ptrFieldRecord -> RecordSize ); 
    memset( record_buffer, 0xFF, tmp_ptrFieldRecord -> RecordSize ) ;

    /* To take care of non-sequential write 
       For example, initially when file is empty, and say user writes
       3rd record, 1st and 2nd record should be addded as dummy and then
       3rd record */

    rec_ctr = tmp_ptrFieldRecord -> NextRecordNum;

    while( rec_ctr < record_num   )
    {
      db_ret_code = 
        write_user_record_to_FFS ( user_field_file,

#ifndef FFS_CLOSE_BEFORE_OPEN
                                   &(tmp_ptrFieldRecord -> FFSFileHandle),
#else
                                   db_handle,
                                   fld_ctr,
                                   &Dummy_FFSFileHandle,
#endif
                                   rec_ctr,
                                   tmp_ptrFieldRecord -> RecordSize,
                                   0,
                                   tmp_ptrFieldRecord -> RecordSize,
                                   record_buffer );
      if( db_ret_code NEQ DB_OK )
      {
        DB_MFREE( record_buffer );
        DB_RETURN( db_ret_code );
      }

      ++rec_ctr;
    }

    /* Add the new record */

    memcpy( record_buffer + offset, buffer, length );

    db_ret_code = 
      write_user_record_to_FFS ( user_field_file,

#ifndef FFS_CLOSE_BEFORE_OPEN
                                 &(tmp_ptrFieldRecord -> FFSFileHandle),
#else
                                 db_handle,
                                 fld_ctr,
                                 &Dummy_FFSFileHandle,
#endif
                                 (UBYTE)record_num,
                                 tmp_ptrFieldRecord -> RecordSize,
                                 0,
                                 tmp_ptrFieldRecord -> RecordSize,
                                 record_buffer );
    if( db_ret_code NEQ DB_OK )
    {
      DB_MFREE( record_buffer );
      DB_RETURN( db_ret_code );
    }

    DB_MFREE( record_buffer );

    /* writing of record is over, update DBState (as IN_USE). */
    DbmMaster[db_handle].DBState = IN_USE;

  } else {
    /* overwritten record */ 

    db_ret_code = 
      write_user_record_to_FFS ( user_field_file,

#ifndef FFS_CLOSE_BEFORE_OPEN
                                 &(tmp_ptrFieldRecord -> FFSFileHandle),
#else
                                 db_handle,
                                 fld_ctr,
                                 &Dummy_FFSFileHandle,
#endif
                                 (UBYTE)record_num,
                                 tmp_ptrFieldRecord -> RecordSize,
                                 offset,
                                 length,
                                 (UBYTE*)buffer );  /* type casting to suppress warning */
    if( db_ret_code NEQ DB_OK )
    {
      DB_RETURN( db_ret_code );
    }

    /* writing of record is over, update DBState (as IN_USE). */
    DbmMaster[db_handle].DBState = IN_USE;

#ifdef INSERTION_SORT

    /* get the sort lists in RAM from FFS */
    get_sort_lists_from_FFS( (UBYTE)db_handle, fld_ctr );

    /* this is updation of record, so need to be re-sorted */
    update_in_sort_lists( (UBYTE)db_handle, fld_ctr, (UBYTE)record_num );

#endif

    /* if database is not tracked, no need to go further for history log writing */
    if( NOT ( DbmMaster[db_handle].Tracked_Clean & TRACKED ) )
      DB_VALUE_RETURN( record_num );

    /* if already Clean has been reset, only history updation */
    if( NOT ( DbmMaster[db_handle].Tracked_Clean & CLEAN ) )
    {
      db_ret_code = update_history_log( db_handle, field_id, record_num );

      if( db_ret_code NEQ DB_OK )
        DB_RETURN( db_ret_code );

      DB_VALUE_RETURN( record_num );
    }

  } /* this_is_new_record */

  /* Updating RecordBitMap and Clean in FFS, ~/dbm/DD_<db_handle> */

  /* create the field data that is to be written */

  DB_MALLOC( field_data, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );
  memset( field_data, 0, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );
  memcpy( field_data, tmp_ptrFieldRecord -> RecordBitMap, RECORD_BITMAP_SIZE );

  if( this_is_new_record )
  { /* RecordBitMap to be updated only for new records */
    set_bit_in_bitmap( field_data,
                       RECORD_BITMAP_SIZE,
                       (UBYTE)record_num );
  } 

  /* clean is reset in memset, so no processing for it 
     (in case of non-tracked database, anyway we ignore it ! */ 

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  db_ret_code = 
    update_field_data_in_FFS ( (const char*)field_file,
                               &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                               db_handle,
#endif
                               fld_ctr,
                               T_DB_FIELD_RECORD_SIZE,
                               field_data,
                               RecordBitMap_OFFSET,
                               ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );


  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( field_data );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( field_data );
  /* updation of DD_<db_handle> is over */

  /* Updating RAM (both DbmMaster and Field records) strctures */
  tmp_ptrFieldRecord -> Clean = NOT_CLEAN;

  if( this_is_new_record )
  { /* RecordBitMap to be updated only for new records */
    set_bit_in_bitmap( tmp_ptrFieldRecord -> RecordBitMap,
                       RECORD_BITMAP_SIZE,
                       (UBYTE)record_num );

    ++tmp_ptrFieldRecord -> UsedRecords;

    tmp_ptrFieldRecord -> NextRecordNum =  
        ( (record_num + 1) > tmp_ptrFieldRecord -> NextRecordNum ) ?
          (record_num + 1) : tmp_ptrFieldRecord -> NextRecordNum ;

#ifdef INSERTION_SORT

    /* get the sort lists in RAM from FFS */
    get_sort_lists_from_FFS( (UBYTE)db_handle, fld_ctr );

    /* note the record number for sorting later 
       yeah, we call it *after* updating UsedRecords */
    new_in_sort_lists( (UBYTE)db_handle, fld_ctr, (UBYTE)record_num );

#endif

  }

  DbmMaster[db_handle].Tracked_Clean &= ~(CLEAN);

  /* history log updation */

  db_ret_code = update_history_log( db_handle, field_id, record_num );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  DB_VALUE_RETURN( record_num );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_create_index      |
+--------------------------------------------------------------------+

    PURPOSE : Creates or updates a sort index for a given (database, field)
*/
T_DB_CODE db_create_index ( int          db_handle, 
                            USHORT       field_id, 
                            UBYTE        sort_index, 
                            T_COMP_FUNC  compare_function, 
                            ULONG        flags )
{

  return internal_db_sort( db_handle,
                           field_id,
                           sort_index,
                           compare_function,
                           flags,
                           QUICK_SORT );
}


#ifdef INSERTION_SORT
/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_update_index      |
+--------------------------------------------------------------------+

    PURPOSE : Sort the list for newly added/deleted records (uses incremental
              sort i.e. add the element at the right place)
*/
T_DB_CODE db_update_index ( int          db_handle, 
                            USHORT       field_id, 
                            UBYTE        sort_index, 
                            T_COMP_FUNC  compare_function, 
                            ULONG        flags )
{

  return internal_db_sort( db_handle,
                           field_id,
                           sort_index,
                           compare_function,
                           flags,
                           INCREMENTAL_SORT );
}
#endif /* INSERTION_SORT */

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_get_phy_from_idx  |
+--------------------------------------------------------------------+

    PURPOSE : Translates a logical entry within a given sort index to the 
              physical record number
*/
T_DB_CODE db_get_phy_from_idx ( int    db_handle, 
                                USHORT field_id, 
                                UBYTE  sort_index, 
                                USHORT order_num )
{
  T_DB_CODE          db_ret_code;
  UBYTE              fld_ctr,
                     sort_index_ctr;
  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;
  char              sort_file[FILENAME_LEN];


  TRACE_FUNCTION("db_get_phy_from_idx()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* sanity check on order_num */
  if( ( order_num > tmp_ptrFieldRecord -> NumOfRecords ) OR
      ( order_num EQ 0 ) )
    DB_RETURN( DB_FAIL );  /* may be we can return 0xFF => no record for it ! */

  /* Search given sort_index in SortIndexList for above field_id; 
     if not found, return DB_INVALID_INDEX. */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] EQ sort_index )
      break;
  }

  if( sort_index_ctr EQ MAX_NUM_OF_SORT_INDEXS )
    DB_RETURN( DB_INVALID_INDEX );

  /* Check if we already have sorted list in RAM structure SortedLists */

  if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] EQ NULL )
  { 
    /* If no, populate the sorted list from file, 
       "<DBDirectory>/UD_<field_id>_sort_<sort_index>". 
       Sorted lists are freed in db_flush */

    sprintf( sort_file, 
             "%s/UD_%d_sort_%d", 
             DbmMaster[db_handle].DBDirectory, 
             tmp_ptrFieldRecord -> FieldID,
             sort_index );

    db_ret_code = 
      populate_sorted_list_from_FFS (  
#ifdef FFS_CLOSE_BEFORE_OPEN
                                      db_handle,
#endif
                                      sort_file,
                                      tmp_ptrFieldRecord -> NumOfRecords,
                                      &(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr]) );
    if( db_ret_code NEQ DB_OK )
      DB_RETURN( db_ret_code );
    
  } /* if we have sort list */

  /* Get (physical) record number for given order_num from sorted list. */
  DB_VALUE_RETURN( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][order_num - 1] );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_flush             |
+--------------------------------------------------------------------+

    PURPOSE : Flush all internal data structures of the database described by 
              db_handle
*/
T_DB_CODE db_flush ( int db_handle)
{
  UBYTE db_ret_code,
        clean_byte,
        fld_ctr,
        i;

  char field_file[FILENAME_LEN];

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  char sort_file[FILENAME_LEN];

  T_FFS_STAT ffs_file_stat;
  T_FFS_SIZE ffs_ret_code;

  TRACE_FUNCTION("db_flush()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /* Clear "clean" in "~/dbm/DD_<db_handle>" for fields. 
     after ffs updation, update RAM data */

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  clean_byte = CLEAN;

  /* Update the affected field records */

#ifdef FFS_CLOSE_BEFORE_OPEN
  db_close_user_field_files( db_handle );
#endif;

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord;

  for( fld_ctr = 0; fld_ctr < DbmMaster[db_handle].NumOfFiles; ++fld_ctr )
  {
    if( tmp_ptrFieldRecord -> FieldID EQ INVALID_FIELD_ID ) /* if not valid field id */
    {
      ++tmp_ptrFieldRecord;   /* next field */
      continue;
    }

    /* Clear file handles */
#ifndef FFS_CLOSE_BEFORE_OPEN

    if( tmp_ptrFieldRecord -> FFSFileHandle NEQ NULL )
    {
      DB_FFS_CLOSE( tmp_ptrFieldRecord -> FFSFileHandle );
      tmp_ptrFieldRecord -> FFSFileHandle = NULL;
    }

#else
    /* Done it closing before for loop */
#endif

    /* Write sort lists to FFS and clear (free memory) sort lists */
    for( i = 0; i < MAX_NUM_OF_SORT_INDEXS; ++i )
    {
      if( tmp_ptrFieldRecord -> SortedLists[i] NEQ NULL )
      {
        /* currently I am not removing unused entries in sort list */
        /* Write sorted lists to "<DBDirectory>/UD_<field_id>_sort_<sort_index>". */

        sprintf( sort_file, 
                 "%s/UD_%d_sort_%d", 
                 DbmMaster[db_handle].DBDirectory, 
                 tmp_ptrFieldRecord -> FieldID,
                 tmp_ptrFieldRecord -> SortIndexList[i] );

        /* writing sorted lists to FFS s required in two cases
           1) the field is not clean and/or
           2) sort file does not exist, but we have sort list (happen during 
               bootup time with zero records)
         */
        
        ffs_ret_code = ffs_stat( sort_file, &ffs_file_stat );

        if( ( ffs_ret_code NEQ EFFS_OK )      AND
            ( ffs_ret_code NEQ EFFS_NOTFOUND ) )
        {
          LastFFS_ReturnCode = ffs_ret_code;
          DB_MFREE( tmp_ptrFieldRecord -> SortedLists[i] );
          tmp_ptrFieldRecord -> SortedLists[i] = NULL;
          DB_RETURN( DB_FAIL_FS );
        }

        if( ( NOT tmp_ptrFieldRecord -> Clean )  OR /* not clean (consistent) */
            ( ffs_ret_code EQ EFFS_NOTFOUND )    )  /* file does not exist */
        {
          db_ret_code =
            write_sorted_list_to_FFS ( 
#ifdef FFS_CLOSE_BEFORE_OPEN
                                       db_handle,
#endif
                                       sort_file,
                                       tmp_ptrFieldRecord -> NumOfRecords,
                                       tmp_ptrFieldRecord -> SortedLists[i] );

          if( db_ret_code NEQ DB_OK )
          {
            DB_MFREE( tmp_ptrFieldRecord -> SortedLists[i] );
            tmp_ptrFieldRecord -> SortedLists[i] = NULL;
            DB_RETURN( db_ret_code );
          }
        }

        /* writing in sort file is over */
        DB_MFREE( tmp_ptrFieldRecord -> SortedLists[i] );
        tmp_ptrFieldRecord -> SortedLists[i] = NULL;
      }
    }

    if( tmp_ptrFieldRecord -> Clean ) /* is it clean (consistent) ? */
    {
      ++tmp_ptrFieldRecord;   /* next field */
      continue;
    }

    /* Clear "clean" in "~/dbm/DD_<db_handle>" if not clean  */

    db_ret_code = 
      update_field_data_in_FFS ( (const char*)field_file,
                                 &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                                 db_handle,
#endif
                                 fld_ctr,
                                 T_DB_FIELD_RECORD_SIZE,
                                 &clean_byte,
                                 ( T_DB_FIELD_RECORD_SIZE - 1 ) ,
                                 1 );       /* "Clean" contained in last one byte */

    if( db_ret_code NEQ DB_OK )
    {
      /* this leaves out FFS and RAM in consistent state, 
         this is ok since FFS is in correct state for some of fields ! */
      DB_RETURN( db_ret_code );
    }

    tmp_ptrFieldRecord -> Clean = CLEAN;

    ++tmp_ptrFieldRecord;   /* next field */

  } /* for all field records */

  if( DbmMaster[db_handle].FFSFileHandle NEQ NULL )
  {
    DB_FFS_CLOSE( DbmMaster[db_handle].FFSFileHandle );
  }

  /* updation of FFS is over */

  /* Update corresponding "clean" field for database in RAM data structures, 
     also DBState = OPEN */
  DbmMaster[db_handle].Tracked_Clean |= CLEAN;
  DbmMaster[db_handle].DBState = OPEN;

  /* done */
  DB_RETURN( DB_OK );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_search            |
+--------------------------------------------------------------------+

    PURPOSE : Searches for an entry within a given database and a given file
*/
int db_search ( int               db_handle, 
                USHORT            field_id, 
                UBYTE             sort_index, 
                UBYTE*            order_num,
                T_SEARCH_FUNC     search_function, 
                ULONG             flags, 
                const UBYTE*      search_tag )
{
  T_DB_CODE  db_ret_code;

  UBYTE fld_ctr,
        tmp_byte,
        sort_index_ctr,
        bit_num;

  UBYTE* tmp_byte_ptr;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  char sort_file[FILENAME_LEN];              

  int record_num = 0,
      index_num ;

  TRACE_FUNCTION("db_search()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  if( sort_index EQ 0 )
  {
    /* If sort_index is zero, 
        o	Do linear search using RecordBitMap, search/comparison function is given by caller.
        o	If search is successful, return the record number.
        o	Otherwise return DB_RECORD_NOT_FOUND. */

    /* Sanity check */
    if( (*order_num) > tmp_ptrFieldRecord -> NumOfRecords )
      DB_RETURN( DB_FAIL );

    /* search from next record,
       search_node->top would be zero for fresh search */

    record_num = *order_num;

    /* get the correct byte */
    tmp_byte_ptr = 
      tmp_ptrFieldRecord -> RecordBitMap + RECORD_BITMAP_SIZE - BITMAP_INDEX(record_num) - 1;
    tmp_byte = *tmp_byte_ptr;

    /* get the correct bit position */
    bit_num = BIT_POS(record_num);
    tmp_byte >>= bit_num;

    /* for bytes in bitmap */
    while( record_num < tmp_ptrFieldRecord -> NumOfRecords )
    {
      /* for bits in byte */
      while( bit_num < MAX_BIT_POS )
      {
        if( record_num > tmp_ptrFieldRecord -> NumOfRecords )
          break;

        if( NOT (tmp_byte & 0x01 ) ) /* if record does not exist, move on to next */
        {
          tmp_byte >>= 1;
          ++bit_num;
          ++record_num;
          continue;
        }

        /* ok, this is a valid record */

        if( search_function( flags,
                             search_tag, 
                             db_handle, 
                             field_id,
                             (UBYTE)record_num ) 
            EQ 0 )  /* yeah, "EQ 0" is success */
        {
          /* found it ! */
          *order_num = record_num; /* for next search */
          DB_VALUE_RETURN( record_num );
        }

        tmp_byte >>= 1;
        ++bit_num;
        ++record_num;
                             
      } /* for bits in byte */

      --tmp_byte_ptr;
      tmp_byte = *tmp_byte_ptr;
      bit_num = 0;

    } /* for bytes in bitmap */

    /* record not found */
    DB_RETURN( DB_RECORD_NOT_FOUND );

  } else {
    /* If sort_index is not zero, search using sorted list */

    /* Search given sort_index in SortIndexList for above field_id; 
       if not found, return DB_INVALID_INDEX. */
    for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
    {
      if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] EQ sort_index )
        break;
    }

    if( sort_index_ctr EQ MAX_NUM_OF_SORT_INDEXS )
      DB_RETURN( DB_INVALID_INDEX );

    if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] EQ NULL )
    { 
      /* If no, populate the sorted list from file, 
         "<DBDirectory>/UD_<field_id>_sort_<sort_index>". 
         Sorted lists are freed in db_flush */

      sprintf( sort_file, 
               "%s/UD_%d_sort_%d", 
               DbmMaster[db_handle].DBDirectory, 
               tmp_ptrFieldRecord -> FieldID,
               sort_index );

      db_ret_code = 
        populate_sorted_list_from_FFS (  

#ifdef FFS_CLOSE_BEFORE_OPEN
                                        db_handle,
#endif
                                        sort_file,
                                        tmp_ptrFieldRecord -> NumOfRecords,
                                        &(tmp_ptrFieldRecord -> SortedLists[sort_index_ctr]) );

      if( db_ret_code NEQ DB_OK )
        DB_RETURN( db_ret_code );

    }

    /* (Binary) search the sorted list, search/compare function is given by caller. 
       o	If search is successful, return the record number.
       o	Otherwise return DB_RECORD_NOT_FOUND */


    index_num = 
           search( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr],
                   tmp_ptrFieldRecord -> UsedRecords,
                   order_num,
                   search_function, 
                   flags, 
                   search_tag,
                   db_handle, 
                   field_id );

    if( index_num EQ SEARCH_FAILED )
      DB_RETURN( DB_RECORD_NOT_FOUND );

    DB_VALUE_RETURN( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr][index_num - 1] );
                   
  }
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_delete_record     |
+--------------------------------------------------------------------+

    PURPOSE : Delete a record in the given field of the given database
*/
T_DB_CODE db_delete_record ( int    db_handle,
                             USHORT field_id, 
                             USHORT record_num )
{
  T_DB_CODE  db_ret_code;

  UBYTE fld_ctr,
        tmp_byte;

  char  field_file[FILENAME_LEN];              

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  UBYTE*  field_data;

  TRACE_FUNCTION("db_delete_record()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Using RecordBitMap, check whether record_num exists; 
     if it does not exist, return DB_OK. */

  tmp_byte = 
    (tmp_ptrFieldRecord -> RecordBitMap)[ RECORD_BITMAP_SIZE - BITMAP_INDEX(record_num) - 1];

  if( NOT ( tmp_byte & ( 0x01 << BIT_POS(record_num) ) ) )  /* if record does *not* exist */
    DB_RETURN( DB_OK );

  /* Update "Clean" and "RecordBitMap" fields in "~/dbm/DD_<db_handle>" */

  /* create the field data that is to be written */

  DB_MALLOC( field_data, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );
  memset( field_data, 0, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );
  memcpy( field_data, tmp_ptrFieldRecord -> RecordBitMap, RECORD_BITMAP_SIZE );

  /* updating RecordBitMap */
  reset_bit_in_bitmap( field_data,
                       RECORD_BITMAP_SIZE,
                       (UBYTE)record_num );

  /* clean is reset in memset, so no processing for it 
     (in case of non-tracked database, anyway we ignore it ! */ 

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  db_ret_code = 
    update_field_data_in_FFS ( (const char*)field_file,
                               &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                               db_handle,
#endif
                               fld_ctr,
                               T_DB_FIELD_RECORD_SIZE,
                               field_data,
                               RecordBitMap_OFFSET,
                               ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );

  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( field_data );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( field_data );
  /* updation of DD_<db_handle> is over */

  /* Update corresponding "Clean" and "RecordBitMap" fields in RAM data structures, 
     also update DBState (as IN_USE) and decrement UsedRecords. */

  tmp_ptrFieldRecord -> Clean = NOT_CLEAN;

  /* updating RecordBitMap */
  reset_bit_in_bitmap( tmp_ptrFieldRecord -> RecordBitMap,
                       RECORD_BITMAP_SIZE,
                       (UBYTE)record_num );

#ifdef INSERTION_SORT

  /* get the sort lists in RAM from FFS */
  get_sort_lists_from_FFS( (UBYTE)db_handle, fld_ctr );

  /* delete in available sort lists
     yeah, we call it *before* updating UsedRecords */
  delete_in_sort_lists( (UBYTE)db_handle, fld_ctr, (UBYTE)record_num );

#endif /* INSERTION_SORT */

  --tmp_ptrFieldRecord -> UsedRecords;

  DbmMaster[db_handle].Tracked_Clean &= ~ (CLEAN);
  DbmMaster[db_handle].DBState = IN_USE;

  /* history log updation */

  return update_history_log( db_handle, field_id, record_num );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_read_record       |
+--------------------------------------------------------------------+

    PURPOSE : Read length bytes at offset from the record in given (database, field)
*/
int db_read_record ( int     db_handle, 
                     USHORT  field_id, 
                     USHORT  record_num, 
                     USHORT  offset, 
                     USHORT  length, 
                     UBYTE*  record_buffer )
{
  T_DB_CODE  db_ret_code;

  UBYTE fld_ctr,
        tmp_byte;

  char  user_field_file[FILENAME_LEN];              

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;


  TRACE_FUNCTION("db_read_record()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  /* Using RecordBitMap, check whether record_num exists; 
     if it does not exist, return DB_EMPTY_RECORD. */

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  tmp_byte = 
    (tmp_ptrFieldRecord -> RecordBitMap)[ RECORD_BITMAP_SIZE - BITMAP_INDEX(record_num) - 1 ];

  if( NOT ( tmp_byte & ( 0x01 << BIT_POS(record_num) ) ) )  /* if record does *not* exist */
    DB_RETURN( DB_EMPTY_RECORD );

  /* Check if record exceeds the record size (i.e. offset + length > record size); 
     if it exceeds, return error DB_INVALID_SIZE */
  if( (offset + length) > 
      tmp_ptrFieldRecord -> RecordSize )
    DB_RETURN( DB_INVALID_SIZE );

  /* Check if FFS file is already opened using FFSFileHandle, part of field record. 
     If it is not yet opened, open the FFS file and update FFSFileHandle. 
     This is taken care in read_user_record_from_FFS below. */

  /* Read record_num from "<DBDirectory>/UD_<field_id>"
     Put "length" number of bytes from "offset" in buffer. (assuming that 
     buffer contain enough memory i.e. caller should take care of allocating 
     enough memory space for buffer) */

  /* User data file name is "<DBDirectory>/UD_<field_id>" */

  sprintf( user_field_file, 
           "%s/UD_%d", 
           DbmMaster[db_handle].DBDirectory, 
           tmp_ptrFieldRecord -> FieldID );

  db_ret_code =  
    read_user_record_from_FFS ( user_field_file,

#ifndef FFS_CLOSE_BEFORE_OPEN
                                &(tmp_ptrFieldRecord -> FFSFileHandle),
#else
                                db_handle,
                                fld_ctr,
                                &Dummy_FFSFileHandle,
#endif
                                (UBYTE)record_num,
                                tmp_ptrFieldRecord -> RecordSize,
                                offset,
                                length,
                                record_buffer );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  DB_VALUE_RETURN( record_num );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_remove            |
+--------------------------------------------------------------------+

    PURPOSE : Remove a database. The database must not be in use.
*/
T_DB_CODE db_remove ( const char* directory )
{
  int db_handle;

  UBYTE db_ctr,
        fld_ctr;

  T_DB_CODE          db_ret_code;

  UBYTE* dbm_data;
  UBYTE field_file[FILENAME_LEN];


 
  TRACE_FUNCTION("db_remove()");

  /* DBM_State check */
  DBM_State_check;

  /* Search for "directory" in DbmMaster
     If not found, return DB_INVALID_DB */

  for( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
  {
    if ( DbmMaster[db_ctr].DBState NEQ UNUSED_ENTRY )
    {
        if( memcmp( DbmMaster[db_ctr].DBDirectory, 
                    directory, 
                    strlen(directory) ) EQ 0 )
          break;
    }
  }

  if( db_ctr EQ MAX_DBs )
    DB_RETURN( DB_INVALID_DB );

  db_handle = db_ctr;

  /* If DBState for found database in DbmMaster is OPEN or IN_USE, return error DB_IN_USE */

  if( ( DbmMaster[db_ctr].DBState EQ OPEN ) OR
      ( DbmMaster[db_ctr].DBState EQ IN_USE ) )
     DB_RETURN( DB_IN_USE );


  /* Delete all files under directory "<DBDirectory>" (DBDirectory = directory) */

  for( fld_ctr = 0; fld_ctr < DbmMaster[db_handle].NumOfFiles; ++fld_ctr )
  {
    db_ret_code = remove_field_from_FFS( (UBYTE)db_handle, fld_ctr );

    if( db_ret_code NEQ DB_OK )
      DB_RETURN( db_ret_code );
  }

  /* Delete the DBDirectory */
  db_ret_code = delete_file_dir_from_FFS ( (const char*)DbmMaster[db_handle].DBDirectory );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );
  
  /* Delete Field file = DD_<pos. in DD_master> */

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_ctr);  

  db_ret_code = delete_file_dir_from_FFS ( (const char*)field_file );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /* Update FFS data i.e. file, "~/dbm/DD_master" */

  /* prepare the dbm data and write */

  DB_MALLOC( dbm_data, T_DB_MASTER_RECORD_SIZE );
  memset( dbm_data, 0xFF, T_DB_MASTER_RECORD_SIZE );

  db_ret_code = 
    update_dbm_data_in_FFS ( DB_MASTER_FILE,
                             (UBYTE) db_handle,   /* to supress warning */
                             T_DB_MASTER_RECORD_SIZE,
                             dbm_data,
                             0,
                             T_DB_MASTER_RECORD_SIZE );

  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( dbm_data );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( dbm_data );

  /* updation of DD_master done */

  /* Free the memory for field records. 
     Update this entry in DbmMaster as unused. (DBState = UNUSED_ENTRY). 
     Also, decrement UsedDBs. */

  DB_MFREE( DbmMaster[db_handle].ptrFieldRecord );

  DbmMaster[db_handle].DBState = UNUSED_ENTRY;

  --UsedDBs;

  /* everything ok ! */
  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_close             |
+--------------------------------------------------------------------+

    PURPOSE : Close a database; if in use, it flushes it before closing it.
*/
T_DB_CODE db_close ( int db_handle )
{
  TRACE_FUNCTION("db_close()");

  /* DBM_State check */
  DBM_State_check;

  /* Check for db_handle range */
  if( db_handle >= MAX_DBs )
    DB_RETURN( DB_INVALID_DB );

  /* If DBState for db_handleth position in DbmMaster is 
      o	UNUSED_ENTRY, return error DB_INVALID_DB
      o	CLOSED, return DB_OK.
      o	OPEN, update DBState as CLOSED and return DB_OK.
      o	IN_USE, call db_flush, update DBState as CLOSED and return DB_OK */

  switch( DbmMaster[db_handle].DBState )
  {
    case UNUSED_ENTRY: DB_RETURN( DB_INVALID_DB );

    case CLOSED: DB_RETURN( DB_OK );

    case OPEN:  
    case IN_USE:  {
                    db_flush( db_handle );
                    DbmMaster[db_handle].DBState = CLOSED; 
                    DB_RETURN( DB_OK );
                  }

    default: DB_RETURN( DB_FAIL );
  }

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_exit              |
+--------------------------------------------------------------------+

    PURPOSE : Shutdown the data base
*/
void db_exit ( void )
{
  /* It is assumed as "forced" exit and so no error would be 
     raised even if database is in use. Also, here we will not 
     delete the database, but just sync the database and free 
     the RAM structures.

    1)	Call db_flush for all databases present in DbmMaster.
    2)	Free the memory for field records for all databases.
    3)	Update all entries in DbmMaster as unused (this step is not required)
    4)	Set DBM_State as DBM_NOT_INITIALISED. */

  UBYTE db_ctr;

  for ( db_ctr = 0; db_ctr < MAX_DBs; ++db_ctr )
  {
    if( DbmMaster[db_ctr].DBState EQ UNUSED_ENTRY )
      continue;

    db_close( db_ctr );

    DB_MFREE( DbmMaster[db_ctr].ptrFieldRecord );

    DbmMaster[db_ctr].DBState = UNUSED_ENTRY;

  }

  DBM_State = DBM_NOT_INITIALISED;

  UsedDBs = 0;

  return;
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_remove_field      |
+--------------------------------------------------------------------+

    PURPOSE : Delete field in given database
*/
T_DB_CODE db_remove_field ( int    db_handle, 
                            USHORT field_id )
{
  UBYTE fld_ctr,
        db_ret_code;

  UBYTE* field_data;

  char  field_file[FILENAME_LEN]; 

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  TRACE_FUNCTION("db_remove_field()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field id not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

#ifndef FFS_CLOSE_BEFORE_OPEN

  /* Check if FFS file is open using FFSFileHandle, part of field record; 
     if so, return error DB_IN_USE. */
  if( tmp_ptrFieldRecord -> FFSFileHandle NEQ NULL )
    DB_RETURN( DB_IN_USE );

#else

  if( db_status_user_field_file( db_handle, fld_ctr ) EQ OPENED_FOR_WRITE )
   DB_RETURN( DB_IN_USE );
  
#endif

  /* Remove file "<DBDirectory>/UD_<field_id>" */

  db_ret_code = remove_field_from_FFS( (UBYTE)db_handle, fld_ctr );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /* Update "~/dbm/DD_<db_handle>" for removed field id. */

  /* create the field data that is to be written */

  DB_MALLOC( field_data, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );
  memset( field_data, 0xFF, ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  db_ret_code = 
    update_field_data_in_FFS ( (const char*)field_file,
                               &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                               db_handle,
#endif
                               fld_ctr,
                               T_DB_FIELD_RECORD_SIZE,
                               field_data,
                               RecordBitMap_OFFSET,
                               ( T_DB_FIELD_RECORD_SIZE - RecordBitMap_OFFSET ) );

  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( field_data );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( field_data );
  /* updation of DD_<db_handle> is over */

  /* update in RAM */

  tmp_ptrFieldRecord -> FieldID = INVALID_FIELD_ID;

  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_read_change_log   |
+--------------------------------------------------------------------+

    PURPOSE : Get an information about the changed records since the last 
              call of this function
*/
T_DB_CODE db_read_change_log ( int           db_handle, 
                               T_DB_CHANGED* changed)
{
  T_DB_CODE db_ret_code;
  TRACE_FUNCTION("db_read_change_log()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /* Copy contents of internal history log to HistoryRecord */
  *changed = DB_History_log;  /* This would work ?? */
   
  /* Clear internal history log. */
  DB_History_log.entries = 0; /* this is enough for clearing history log ! */

  /* done */
  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_remove_index      |
+--------------------------------------------------------------------+

    PURPOSE : Removes a sort index
*/
T_DB_CODE db_remove_index ( int    db_handle,
                            USHORT field_id,
                            UBYTE  sort_index )
{
  UBYTE db_ret_code,
        fld_ctr,
        sort_index_ctr;
  UBYTE field_file[FILENAME_LEN];

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;

  char sort_file[FILENAME_LEN];              

  UBYTE* field_data;

   
  TRACE_FUNCTION("db_remove_index()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Search given sort_index in SortIndexList for above field_id; 
     if not found, return DB_INVALID_INDEX. */
  for( sort_index_ctr = 0; sort_index_ctr < MAX_NUM_OF_SORT_INDEXS; ++sort_index_ctr )
  {
    if( tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] EQ sort_index )
      break;
  }

  if( sort_index_ctr EQ MAX_NUM_OF_SORT_INDEXS )
    DB_RETURN( DB_INVALID_INDEX );

  /* Remove file, "<DBDirectory>/UD_<field_id>_sort_<sort_index>" */

  sprintf( sort_file, 
           "%s/UD_%d_sort_%d", 
           DbmMaster[db_handle].DBDirectory, 
           tmp_ptrFieldRecord -> FieldID,
           tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] );

  db_ret_code = delete_file_dir_from_FFS ( sort_file );

  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );        

  /* Update file , "~/dbm/DD_<db_handle>" for SortIndexes. */

  /* create the field data that is to be written */

  DB_MALLOC( field_data, MAX_NUM_OF_SORT_INDEXS );
  memcpy( field_data, tmp_ptrFieldRecord -> SortIndexList, MAX_NUM_OF_SORT_INDEXS );

  field_data[sort_index_ctr] = INVALID_SORT_INDEX;

  sprintf( field_file, "%s/DD_%d", DB_DIR, db_handle );

  db_ret_code = 
    update_field_data_in_FFS ( (const char*)field_file,
                               &DbmMaster[db_handle].FFSFileHandle,
#ifdef FFS_CLOSE_BEFORE_OPEN
                               db_handle,
#endif
                               fld_ctr,
                               T_DB_FIELD_RECORD_SIZE,
                               field_data,
                               SortIndexList_OFFSET,
                               MAX_NUM_OF_SORT_INDEXS );
  if( db_ret_code NEQ DB_OK )
  {
    DB_MFREE( field_data );
    DB_RETURN( db_ret_code );
  }

  DB_MFREE( field_data );
  /* updation of DD_<db_handle> is over */

  /* Update corresponding RAM structure i.e. DbmFieldRecord 
     Check if we have sort list corresponding to this sort index; if so, free it. */

  tmp_ptrFieldRecord -> SortIndexList[sort_index_ctr] = INVALID_SORT_INDEX;
  if( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] NEQ NULL )
  {
    DB_MFREE( tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] );
    tmp_ptrFieldRecord -> SortedLists[sort_index_ctr] = NULL;
  }

  DB_RETURN( DB_OK );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_field_changed     |
+--------------------------------------------------------------------+

    PURPOSE : Check whether a database field has been changed since last 
              db_flush().
*/
T_DB_CODE db_field_changed ( int    db_handle, 
                             USHORT field_id,
                             BOOL*  changed )
{
  UBYTE fld_ctr;
  T_DB_CODE db_ret_code;

  TRACE_FUNCTION("db_field_changed()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  /* Check "Clean" field in field record; 
     a) if (Clean = true), changed = false. */

  *changed = NOT DbmMaster[db_handle].ptrFieldRecord[fld_ctr].Clean;

  DB_RETURN( DB_OK );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_record_empty      |
+--------------------------------------------------------------------+

    PURPOSE : Check whether a database field is empty. The result is 
              given to the caller in the *empty variable
*/
T_DB_CODE db_record_empty ( int    db_handle,
                            USHORT field_id,
                            USHORT record_num,
                            BOOL*  empty )
{
  UBYTE fld_ctr,
        tmp_byte;
  T_DB_CODE db_ret_code;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;


  TRACE_FUNCTION("db_record_empty()");

  *empty = TRUE;

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Using RecordBitMap, check whether record_num is free; 
     if so, empty = true and false otherwise */

  tmp_byte = 
    (tmp_ptrFieldRecord -> RecordBitMap)[ RECORD_BITMAP_SIZE - BITMAP_INDEX(record_num) - 1 ];

  if( tmp_byte & ( 0x01 << BIT_POS(record_num) ) )  /* if record exist */
    *empty = FALSE;

  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_find_free_record  |
+--------------------------------------------------------------------+

    PURPOSE : Find an free record in given (database, field)
*/
int db_find_free_record	( int    db_handle,
                          USHORT field_id )
{
  UBYTE fld_ctr;
  T_DB_CODE  db_ret_code;

  int record_num;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;


  TRACE_FUNCTION("db_find_free_record()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Using RecordBitMap, search for free record.
     If found , return the found record_num
     Return DB_RECORD_NOT_FOUND */

  record_num = 1 + search_clear_bit_in_bitmap( tmp_ptrFieldRecord -> RecordBitMap,
                                               RECORD_BITMAP_SIZE );

  if( record_num > tmp_ptrFieldRecord -> NumOfRecords )
    DB_RETURN( DB_RECORD_NOT_FOUND );

  DB_VALUE_RETURN( record_num );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_info              |
+--------------------------------------------------------------------+

    PURPOSE : Get information about an open database
*/
T_DB_CODE db_info ( int        db_handle, 
                    T_DB_INFO* db_info )
{
  T_DB_CODE db_ret_code;

  TRACE_FUNCTION("db_info()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  (*db_info).clean   = ( DbmMaster[db_handle].Tracked_Clean & CLEAN ) ? TRUE : FALSE ;
  (*db_info).tracked = ( DbmMaster[db_handle].Tracked_Clean & TRACKED ) ? TRUE : FALSE ;

  DB_RETURN( DB_OK );
}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_info_field        |
+--------------------------------------------------------------------+

    PURPOSE : Get general information about a field in an open database.
*/
T_DB_CODE db_info_field ( int              db_handle,	
                          USHORT           field_id, 
                          T_DB_INFO_FIELD* info_field )
{
  UBYTE fld_ctr;
  T_DB_CODE db_ret_code;

  T_DBM_FIELDRECORD* tmp_ptrFieldRecord;


  TRACE_FUNCTION("db_info_field()");

  /* DBM_State check */
  DBM_State_check;

  /* db_handle check */
  db_ret_code = db_handle_check( db_handle );
  if( db_ret_code NEQ DB_OK )
    DB_RETURN( db_ret_code );

  /*	field id search; if field not found, return error DB_INVALID_FIELD */
  fld_ctr = field_id_search( db_handle, field_id );
  if( fld_ctr EQ DbmMaster[db_handle].NumOfFiles )
    DB_RETURN( DB_INVALID_FIELD );

  tmp_ptrFieldRecord = DbmMaster[db_handle].ptrFieldRecord + fld_ctr;

  /* Using data available in RAM in field record, fill up info_field 
     (i.e. clean, db_type, num_of_records and used_records). */

  (*info_field).clean        = tmp_ptrFieldRecord -> Clean;
  (*info_field).entry_type   = tmp_ptrFieldRecord -> DBType;
  (*info_field).record_size  = tmp_ptrFieldRecord -> RecordSize;
  (*info_field).num_records  = tmp_ptrFieldRecord -> NumOfRecords;
  (*info_field).used_records = tmp_ptrFieldRecord -> UsedRecords;

  DB_RETURN( DB_OK );

}

/*
+--------------------------------------------------------------------+
| PROJECT: PHB                          MODULE: DBM                  |
|                                       ROUINE: db_get_last_fs_error |
+--------------------------------------------------------------------+

    PURPOSE : Delivers the last error provided by the (flash) file system
              that resulted in DB_FAIL_FS.
*/
int db_get_last_fs_error ( void )
{
   DB_VALUE_RETURN( LastFFS_ReturnCode );
}

#endif /* #ifdef TI_PS_FFS_PHB */