FreeCalypso > hg > fc-magnetite
view src/aci2/aci/db.c @ 47:472fba6e92b7
power.lib done
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 30 Sep 2016 20:48:31 +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 */