FreeCalypso > hg > fc-magnetite
view src/aci2/atb/ATBPb.c @ 533:09afc37e8069
armio.c: added support for C155 target
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Mon, 05 Nov 2018 09:54:16 +0000 |
parents | 93999a60b835 |
children |
line wrap: on
line source
/******************************************************************************* CONDAT (UK) ******************************************************************************** This software product is the property of Condat (UK) Ltd and may not be disclosed to any third party without the express permission of the owner. ******************************************************************************** $Project name: $Project code: $Module: $File: ATBPb.c $Revision: $Author: Condat(UK) $Date: ******************************************************************************** Description: ******************************************************************************** $History: ATBPb.c Jan 18, 2004 REF: CRR MMI-SPR-25332 xnkulkar Description: Moving entries from Sim Card to phone memory fails. Solution: When entries are moved, corresponding updations are done in cache as well. $End *******************************************************************************/ #if defined (NEW_FRAME) #include "typedefs.h" #include "vsi.h" #include "pei.h" #include "custom.h" #include "gsm.h" #else #include "STDDEFS.H" #include "custom.h" #include "gsm.h" #include "vsi.h" #endif #include <stdio.h> #include <string.h> #include "mfw_mfw.h" #include "mfw_sys.h" #include "cus_aci.h" #include "prim.h" #include "pcm.h" #include "ATBPb.h" #include "ATBPb_i.h" /* Global data for phonebook */ static T_PB_DATA *phonebook[PB_PHONEBOOKS_MAX] = {0}; /******************************************************************************* $Function: ATB_pb_GetPhonebook $Description: Checks to see which file system is selected $Returns: PB_OK Action completed OK. PB_FILEWRITEFAIL File write encountered an error $Arguments: phonebook_id The phonebook identifier current_type Place to store type of phonebook selected. *******************************************************************************/ PB_RET ATB_pb_GetPhonebook(SHORT phonebook_id, T_PB_TYPE *current_type) { PB_RET result; T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); tracefunction("ATB_pb_GetPhonebook"); if (data) { *current_type = data->current_type; return PB_OK; } result = FS_pb_GetPhonebook(phonebook_id, current_type); return result; } /******************************************************************************* $Function: ATB_pb_SetPhonebook $Description: Select file system $Returns: PB_OK Action completed OK. PB_FILEREADFAIL File read encountered an error $Arguments: phonebook_id The phonebook identifier current_type Type of phonebook selected. *******************************************************************************/ PB_RET ATB_pb_SetPhonebook(SHORT phonebook_id, T_PB_TYPE current_type) { PB_RET result; T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); tracefunction("ATB_pb_SetPhonebook"); if (data) data->current_type = current_type; result = FS_pb_SetPhonebook(phonebook_id, current_type); return result; } /******************************************************************************* $Function: ATB_pb_Initialise $Description: Initialise an instance of phonebook. Dynamically allocates a record cache in RAM. Creates the necessary file(s) if they do not already exist. Sorts the phonebook by name and by number, creating the appropriate index tables. IMPORTANT: if the file reading is non-blocking - i.e. if a request to read a file does not return with an answer straight away, but the system awaits a callback - then "cache_max" must equal "records_max". This ensures that all records are stored in RAM and can be retrieved without waiting for a response. If the file reading is blocking, then cache_max can be smaller than records_max. The memory allocated by this operation will not be freed until ATB_pb_Exit() is called. $Returns: PB_OK Action completed OK. PB_EXCT Action currently executing, callback awaited. GI_pb_OK will be called if successful, GI_pb_Error otherwise. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_FILEWRITEFAIL File write encountered an error PB_BOOKALREADYEXISTS Tried to initialise a phonebook that already exists $Arguments: phonebook_id The phonebook identifier type Type of phonebook. records_max Indicates the maximum number of entries the phonebook can hold. cache_max Indicates the maximum number of records that the PB will store concurrently in RAM. alpha_max Maximum size of unicode alpha tag in characters number_max Maximum size of phone number in digits ext_max Maximum size of extended data in bytes *******************************************************************************/ PB_RET ATB_pb_Initialise(SHORT phonebook_id, T_PB_TYPE type, SHORT records_max, SHORT cache_max, SHORT alpha_max, SHORT number_max, SHORT ext_max) { T_PB_DATA *data; tracefunction("ATB_pb_Initialise()"); /* Check to see if this phonebook has already been allocated */ if (ATB_hnd_GetPbData(phonebook_id)!=NULL) { trace("* ERROR * - Phonebook already exists"); GI_pb_Error(phonebook_id, PB_INITIALISE, PB_BOOKALREADYEXISTS); return PB_BOOKALREADYEXISTS; } /* Allocate memory for phonebook data */ data = (T_PB_DATA *)GI_pb_MemAlloc(sizeof(T_PB_DATA)); ATB_hnd_SetPbData(phonebook_id, data); data->records_max = records_max; data->records_used = 0; data->search_results = 0; data->alpha_max = alpha_max; data->number_max = number_max; data->ext_max = ext_max; /* Allocate memory for index tables */ data->name_table = (SHORT *)GI_pb_MemAlloc(records_max*sizeof(SHORT)); memset(data->name_table, 0, records_max*sizeof(SHORT)); data->number_table = (SHORT *)GI_pb_MemAlloc(records_max*sizeof(SHORT)); memset(data->number_table, 0, records_max*sizeof(SHORT)); data->search_table = (SHORT *)GI_pb_MemAlloc(records_max*sizeof(SHORT)); memset(data->search_table, 0, records_max*sizeof(SHORT)); data->in_memory = (SHORT *)GI_pb_MemAlloc(records_max*sizeof(SHORT)); memset(data->in_memory, PB_EMPTY_RECORD, records_max*sizeof(SHORT)); /* Allocate memory for cache */ data->cache_max = cache_max; data->cache_size = 0; data->cache = (T_PB_RECORD **)GI_pb_MemAlloc(records_max*sizeof(T_PB_RECORD *)); memset(data->cache, 0, records_max*sizeof(T_PB_RECORD *)); /* Set up command */ data->command_id = PB_INITIALISE; data->status = PB_STATUS_INIT; data->param.Initialise.type = type; data->param.Initialise.records_max = records_max; return ATB_status_Initialise(phonebook_id); } /* Status function for Initialise */ PB_RET ATB_status_Initialise(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); T_PB_INITIALISE *Initialise; SHORT blocking; PB_RET result; /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } Initialise = &data->param.Initialise; blocking = TRUE; while (blocking) { switch(data->status) { case PB_STATUS_INIT: trace("ATB_status_Initialise: INIT"); result = FS_pb_Initialise(phonebook_id, Initialise->type, Initialise->records_max, data->alpha_max, data->number_max, data->ext_max); /* Select next state */ data->status = PB_STATUS_EXEC; Initialise->phys_index = 0; Initialise->first_record = TRUE; Initialise->record = ATB_pb_AllocRec(phonebook_id); /* Allow exit from function */ if (result!=PB_OK) blocking = FALSE; break; case PB_STATUS_EXEC: trace("ATB_status_Initialise: EXEC"); /* Try to read in index tables from file system. If we can't find them, the * records will be read in and sorted. */ if (Initialise->first_record) { result = FS_pb_ReadTables(phonebook_id, &data->records_used, data->name_table, data->number_table); if (result==PB_OK) { /* Escape to PB_STATUS_COMPLETE */ result = PB_OK; data->status = PB_STATUS_COMPLETE; break; } } /* If we've already read a record, and it's an existing record, add it to the * index tables and to the cache */ else { if (Initialise->record->alpha.data[0]!=(USHORT)0xFFFF) { ATB_mem_UpdateCache(phonebook_id, Initialise->phys_index, Initialise->record); ATB_index_AddRec(phonebook_id, INDEX_NAME, Initialise->phys_index, Initialise->record, NULL); ATB_index_AddRec(phonebook_id, INDEX_NUMBER, Initialise->phys_index, Initialise->record, NULL); data->records_used++; } Initialise->phys_index++; } /* Start processing after first record is read */ Initialise->first_record = FALSE; /* If we haven't just read the last record, read the next one */ if (Initialise->phys_index<data->records_max) { result = FS_pb_ReadRec(phonebook_id, Initialise->phys_index, Initialise->record); } else { /* Set next state as finished */ result = PB_OK; data->status = PB_STATUS_COMPLETE; } /* Allow exit from function */ if (result!=PB_OK) blocking = FALSE; break; case PB_STATUS_COMPLETE: trace("ATB_status_Initialise: COMPLETE"); blocking = FALSE; /* Close file */ FS_pb_Finished(phonebook_id); FS_pb_WriteTables(phonebook_id, data->records_used, data->name_table, data->number_table); /* Free the allocated record */ ATB_pb_FreeRec(phonebook_id, Initialise->record); /* Notify the GI of success */ GI_pb_OK(phonebook_id, data->command_id, NULL); result = PB_OK; data->status = PB_STATUS_NONE; break; } } return result; } /******************************************************************************* $Function: ATB_pb_Exit $Description: Frees memory associated with phonebook. To be called when the phonebook is no longer required, or at shutdown. $Returns: PB_OK Action completed OK. $Arguments: phonebook_id The phonebook identifier *******************************************************************************/ PB_RET ATB_pb_Exit(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT phys_index; trace("ATB_pb_Exit"); /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } for (phys_index = 0; phys_index<data->records_max; phys_index++) { if (data->cache[phys_index]!=NULL) ATB_pb_FreeRec(phonebook_id, data->cache[phys_index]); } GI_pb_MemFree((UBYTE *)data->cache, data->records_max*sizeof(T_PB_RECORD *)); GI_pb_MemFree((UBYTE *)data->in_memory, data->records_max*sizeof(SHORT)); GI_pb_MemFree((UBYTE *)data->search_table, data->records_max*sizeof(SHORT)); GI_pb_MemFree((UBYTE *)data->number_table, data->records_max*sizeof(SHORT)); GI_pb_MemFree((UBYTE *)data->name_table, data->records_max*sizeof(SHORT)); GI_pb_MemFree((UBYTE *)data, sizeof(T_PB_DATA)); ATB_hnd_SetPbData(phonebook_id, NULL); return PB_OK; } /******************************************************************************* $Function: ATB_pb_Status $Description: Returns PB_OK if the phonebook is ready for a new command, or PB_BUSY if the phonebook is busy. $Returns: PB_OK All tasks completed, phonebook ready. PB_BUSY Commands currently executing. $Arguments: phonebook_id The phonebook identifier *******************************************************************************/ PB_RET ATB_pb_Status(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); PB_RET result; /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } // Jan 18, 2004 REF: CRR MMI-SPR-25332 xnkulkar // Description: Moving entries from Sim Card to phone memory fails. // Solution: result should be set as PB_OK when PB_STATUS_COMPLETE is true also. if ((data->status==PB_STATUS_NONE) ||(data->status==PB_STATUS_COMPLETE)) result = PB_OK; else result = PB_BUSY; return result; } /******************************************************************************* $Function: ATB_pb_Info $Description: Returns information about the phonebook, which will be returned in the data structure pointed to by phonebook_info. The caller must allocate the T_PB_INFO structure. $Returns: PB_OK Action completed OK. $Arguments: phonebook_id The phonebook identifier phonebook_info Pointer to data structure to contain phonebook information (allocated by caller). *******************************************************************************/ PB_RET ATB_pb_Info(SHORT phonebook_id, T_PB_INFO *phonebook_info) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } phonebook_info->records_used = data->records_used; phonebook_info->records_max = data->records_max; phonebook_info->records_free = data->records_max - data->records_used; phonebook_info->search_results = data->search_results; return PB_OK; } /******************************************************************************* $Function: ATB_pb_ReadRec $Description: Reads a record from the logical position index. The record information will be returned in the data structure pointed to by record. The T_PB_RECORD structure must be allocated by the user. This function calls the ATB_mem_ReadRec function, with an additional check to make sure no other commands are currently executing. $Returns: PB_OK Action completed OK. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist $Arguments: phonebook_id The phonebook identifier index_type The index list from which the record is to be read log_index Logical index in the index list of the record to read record Structure in which to store record data *******************************************************************************/ PB_RET ATB_pb_ReadRec(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index, T_PB_RECORD *record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); PB_RET result; tracefunction("ATB_pb_ReadRec"); /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } /* Make sure phonebook is not busy */ if (ATB_pb_Status(phonebook_id)==PB_BUSY) { trace("* ERROR * - Phonebook is busy"); result = PB_BUSY; } else { /* Read the record from cache or from file */ result = ATB_mem_ReadRec(phonebook_id, index_type, log_index, record); } /* Send success/failure info to the GI */ if (result==PB_OK) GI_pb_OK(phonebook_id, PB_READREC, NULL); else if (result!=PB_EXCT) GI_pb_Error(phonebook_id, PB_READREC, result); /* Close any open file */ FS_pb_Finished(phonebook_id); return result; } /******************************************************************************* $Function: ATB_pb_WriteRec $Description: Writes a record to the logical position index. If the index is PB_NEW_RECORD, a new record is added, otherwise the previous record at that position is overwritten. The name and number index lists will be updated with the new entry in the appropriate place in each. GI_pb_OK will be called to confirm that the write completed successfully, otherwise GI_pb_Error will be called with an error code. Since the order of the records may change due to sorting, GI_pb_OK will receive a pointer to an integer new_log_index, which will store the new position of the record in the chosen index list. This is allocated by and will be destroyed by the phonebook, so must be copied by the GI before function return. $Returns: PB_OK Action completed OK. PB_EXCT Action currently executing, callback awaited. GI_pb_OK will be called if successful, GI_pb_Error otherwise. GI_pb_OK will receive a pointer to the following: SHORT new_log_index - The new position of the record in the chosen index list PB_BUSY Failed, phonebook is busy. PB_FILEWRITEFAIL File write encountered an error PB_RECDOESNOTEXIST (Overwriting only) Tried to access a record that does not exist $Arguments: phonebook_id The phonebook identifier index_type The index list of the record to write log_index Logical index in the index list of the record to write record Pointer to record data to write to phonebook (allocated by caller) *******************************************************************************/ PB_RET ATB_pb_WriteRec(SHORT phonebook_id, T_PB_TYPE index_type, SHORT log_index, T_PB_RECORD *record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT phys_index; tracefunction("ATB_pb_WriteRec"); /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } /* Make sure phonebook is not busy */ if (ATB_pb_Status(phonebook_id)==PB_BUSY) { trace("* ERROR * - Phonebook is busy"); GI_pb_Error(phonebook_id, PB_WRITEREC, PB_BUSY); return PB_BUSY; } /* Are we adding a new record, or overwriting an old one? */ if (log_index==PB_NEW_RECORD) { /* Is phonebook full? */ if (data->records_used==data->records_max) { GI_pb_Error(phonebook_id, PB_WRITEREC, PB_BOOKFULL); return PB_BOOKFULL; } /* Find an empty physical record */ for (phys_index=0; phys_index<data->records_max; phys_index++) { if (data->in_memory[phys_index]==PB_EMPTY_RECORD) break; } } else { /* Check that record exists */ if (log_index<0 || (log_index>=data->records_used && (index_type==INDEX_NAME || index_type==INDEX_NUMBER)) || (index_type==INDEX_SEARCH && log_index>data->search_results) || (index_type==INDEX_PHYSICAL && log_index>data->records_max)) { GI_pb_Error(phonebook_id, PB_WRITEREC, PB_RECDOESNOTEXIST); return PB_RECDOESNOTEXIST; } phys_index = ATB_index_GetPhysIndex(phonebook_id, index_type, log_index); } /* Set up command */ data->command_id = PB_WRITEREC; data->status = PB_STATUS_INIT; data->param.WriteRec.log_index = log_index; data->param.WriteRec.index_type = index_type; data->param.WriteRec.phys_index = phys_index; data->param.WriteRec.record = record; return ATB_status_WriteRec(phonebook_id); } /* Status function for WriteRec */ PB_RET ATB_status_WriteRec(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); T_PB_WRITEREC *WriteRec = &data->param.WriteRec; SHORT blocking; PB_RET result; SHORT name_log_index; SHORT number_log_index; SHORT *log_index_return; blocking = TRUE; while (blocking) { switch(data->status) { case PB_STATUS_INIT: result = FS_pb_WriteRec(phonebook_id, WriteRec->phys_index, WriteRec->record); /* Select next state */ data->status = PB_STATUS_COMPLETE; /* Allow exit from function */ if (result!=PB_OK) blocking = FALSE; break; case PB_STATUS_COMPLETE: if (WriteRec->log_index!=PB_NEW_RECORD) { name_log_index = ATB_index_GetLogIndex(phonebook_id, INDEX_NAME, WriteRec->phys_index); ATB_index_RemoveRec(phonebook_id, INDEX_NAME, name_log_index); number_log_index = ATB_index_GetLogIndex(phonebook_id, INDEX_NUMBER, WriteRec->phys_index); ATB_index_RemoveRec(phonebook_id, INDEX_NUMBER, number_log_index); data->records_used--; } /* Indicate that record is new, then add to the cache */ data->in_memory[WriteRec->phys_index] = 0; ATB_mem_UpdateCache(phonebook_id, WriteRec->phys_index, WriteRec->record); /* Add the record to the index lists */ ATB_index_AddRec(phonebook_id, INDEX_NAME, WriteRec->phys_index, WriteRec->record, &name_log_index); ATB_index_AddRec(phonebook_id, INDEX_NUMBER, WriteRec->phys_index, WriteRec->record, &number_log_index); data->records_used++; blocking = FALSE; /* Close any open file */ FS_pb_Finished(phonebook_id); FS_pb_WriteTables(phonebook_id, data->records_used, data->name_table, data->number_table); /* Notify the GI of success */ log_index_return = NULL; if (WriteRec->index_type==INDEX_NAME) log_index_return = &name_log_index; if (WriteRec->index_type==INDEX_NUMBER) log_index_return = &number_log_index; GI_pb_OK(phonebook_id, data->command_id, log_index_return); result = PB_OK; data->status = PB_STATUS_NONE; break; } } /* Report any errors to the GI */ if (result!=PB_OK && result!=PB_EXCT) { GI_pb_Error(phonebook_id, PB_WRITEREC, result); } return result; } /******************************************************************************* $Function: ATB_pb_DeleteRec $Description: Deletes a record at a logical position index. GI_pb_OK will be called to confirm that the delete completed successfully, otherwise GI_pb_Error will be called with an error code. $Returns: PB_OK Action completed OK. PB_EXCT Action currently executing, callback awaited. GI_pb_OK will be called if successful, GI_pb_Error otherwise. PB_BUSY Failed, phonebook is busy. PB_FILEWRITEFAIL File write encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist $Arguments: phonebook_id The phonebook identifier index_type The index list of the record to delete log_index Logical index in the index list of the record to delete *******************************************************************************/ PB_RET ATB_pb_DeleteRec(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); tracefunction("ATB_pb_DeleteRec"); /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } /* Make sure phonebook is not busy */ if (ATB_pb_Status(phonebook_id)==PB_BUSY) { trace("* ERROR * - Phonebook is busy"); GI_pb_Error(phonebook_id, PB_DELETEREC, PB_BUSY); return PB_BUSY; } /* Check that record exists */ if (log_index<0 || (log_index>=data->records_used && (index_type==INDEX_NAME || index_type==INDEX_NUMBER)) || (index_type==INDEX_SEARCH && log_index>data->search_results) || (index_type==INDEX_PHYSICAL && log_index>data->records_max)) { GI_pb_Error(phonebook_id, PB_DELETEREC, PB_RECDOESNOTEXIST); return PB_RECDOESNOTEXIST; } /* Set up the command */ data->command_id = PB_DELETEREC; data->status = PB_STATUS_INIT; data->param.DeleteRec.log_index = log_index; data->param.DeleteRec.index_type = index_type; data->param.DeleteRec.phys_index = ATB_index_GetPhysIndex(phonebook_id, index_type, log_index); return ATB_status_DeleteRec(phonebook_id); } /* Status function for DeleteRec */ PB_RET ATB_status_DeleteRec(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); T_PB_DELETEREC *DeleteRec = &data->param.DeleteRec; SHORT blocking; PB_RET result; SHORT name_log_index; SHORT number_log_index; blocking = TRUE; while (blocking) { switch(data->status) { case PB_STATUS_INIT: result = FS_pb_DeleteRec(phonebook_id, DeleteRec->phys_index); /* Select next state */ data->status = PB_STATUS_COMPLETE; /* Allow exit from function */ if (result!=PB_OK) blocking = FALSE; break; case PB_STATUS_COMPLETE: name_log_index = ATB_index_GetLogIndex(phonebook_id, INDEX_NAME, DeleteRec->phys_index); ATB_index_RemoveRec(phonebook_id, INDEX_NAME, name_log_index); number_log_index = ATB_index_GetLogIndex(phonebook_id, INDEX_NUMBER, DeleteRec->phys_index); ATB_index_RemoveRec(phonebook_id, INDEX_NUMBER, number_log_index); data->records_used--; data->in_memory[DeleteRec->phys_index] = -1; // Jan 18, 2004 REF: CRR MMI-SPR-25332 xnkulkar // Description: Moving entries from Sim Card to phone memory fails. // Solution: When entries are moved, corresponding updations are done in cache as well. if(data->cache[DeleteRec->phys_index]!=NULL) { ATB_pb_FreeRec(phonebook_id, data->cache[DeleteRec->phys_index]); data->cache[DeleteRec->phys_index]=NULL; data->cache_size--; } /* Close any open file */ FS_pb_Finished(phonebook_id); FS_pb_WriteTables(phonebook_id, data->records_used, data->name_table, data->number_table); blocking = FALSE; /* Notify the GI of success */ GI_pb_OK(phonebook_id, data->command_id, NULL); result = PB_OK; data->status = PB_STATUS_NONE; break; } } /* Report any errors to the GI */ if (result!=PB_OK && result!=PB_EXCT) { GI_pb_Error(phonebook_id, PB_DELETEREC, result); } return result; } /******************************************************************************* $Function: ATB_pb_Find $Description: Find the nearest match to the supplied record. Only the index_type specified will be considered: if the index_type is INDEX_NAME, then the name only will be matched; if INDEX_NUMBER, then just the number. The new_log_index parameter should be a pointer to a user allocated integer. This will return the logical index of the closest match in the index list specified. This index either corresponds to a perfect match, or to the record that would appear just after the provided record in the list. For example, if the list consisted of record 0, Alice and record 1, Carol, then a search for Bob would return 1. A search for a record that would occur before the first record always returns 0. A search for a record that would occur after the last record returns the index of the last record +1. The "match" parameter should be a pointer to a user allocated variable of type T_PB_MATCH. This returns information about how closely the found entry matches the record. In the example above, a search for Bob would return 1 and MATCH_NONE; a search for Ca would return 1 and MATCH_START, and a search for Carol would return 1 and MATCH_EXACT. Rarely, match may be MATCH_FRAGMENT: this can be considered equivalent to MATCH_NONE in this context. $Returns: PB_OK Action completed OK. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist (should never happen) $Arguments: phonebook_id The phonebook identifier index_type The index_type of the provided record that is to be searched for, and the index list that the resulting new_log_index pertains to record The record whose index_type is to be matched new_log_index Returns with the logical index of the closest matching record match Specifies how good the resulting match was (MATCH_NONE, MATCH_START, MATCH_FRAGMENT or MATCH_EXACT). *******************************************************************************/ PB_RET ATB_pb_Find (SHORT phonebook_id, T_PB_INDEX index_type, T_PB_RECORD *record, SHORT *new_log_index, T_PB_MATCH *match) { PB_RET result; tracefunction("ATB_pb_Find"); result = ATB_index_Find(phonebook_id, index_type, record, new_log_index, match); /* Close any open file */ FS_pb_Finished(phonebook_id); /* Send success/failure info to the GI */ if (result==PB_OK) GI_pb_OK(phonebook_id, PB_FIND, NULL); else if (result!=PB_EXCT) GI_pb_Error(phonebook_id, PB_FIND, result); return result; } /******************************************************************************* $Function: ATB_pb_ReadRecList $Description: Fetches record information of sequential records in an index list, starting at logical record start_log_index and fetching recs_count records in total. The information will be stored in the caller allocated array of records provided. $Returns: PB_OK Action completed OK. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist $Arguments: phonebook_id The phonebook identifier index_type The index list to use start_log_index Logical index in the index list of the first record to read recs_count Total number of sequential records to read. record Array of entries in which to store the fetched records. The array must be recs_count in size. (Caller allocated). *******************************************************************************/ PB_RET ATB_pb_ReadRecList(SHORT phonebook_id, T_PB_INDEX index_type, SHORT start_log_index, SHORT num_recs, T_PB_LIST *list) { PB_RET result; SHORT log_index; tracefunction("ATB_pb_ReadRecList"); list->records_used = 0; /* Make sure phonebook is not busy */ if (ATB_pb_Status(phonebook_id)==PB_BUSY) { trace("* ERROR * - Phonebook is busy"); /* Report any errors to the GI */ GI_pb_Error(phonebook_id, PB_READRECLIST, PB_BUSY); return PB_BUSY; } /* Read in list of records */ for (log_index = start_log_index; log_index<start_log_index+num_recs && log_index<list->records_max; log_index++) { result = ATB_mem_ReadRec(phonebook_id, index_type, log_index, &list->record[log_index-start_log_index]); if (result!=PB_OK) break; list->records_used++; } /* Close any open file */ FS_pb_Finished(phonebook_id); /* Send success/failure info to the GI */ if (result==PB_OK) GI_pb_OK(phonebook_id, PB_READRECLIST, NULL); else if (result!=PB_EXCT) GI_pb_Error(phonebook_id, PB_READRECLIST, result); return result; } /******************************************************************************* $Function: ATB_pb_Search $Description: Searches every record in the index list for a fragment. The index list must be either INDEX_NAME or INDEX_NUMBER. If it is INDEX_NAME, then a name fragment will be searched for; if it is INDEX_NUMBER, a number fragment will be searched for. The results are stored in an index list. They may be accessed by using the normal read and write functions, using the index type INDEX_SEARCH. The size of the search list is returned in the variable recs_count. $Returns: PB_OK Action completed OK. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist (should never happen) $Arguments: phonebook_id The phonebook identifier index_type The index list to use record Record containing the fragment to search for recs_count Pointer to an int (user allocated) in which will be returned the number of search results. *******************************************************************************/ PB_RET ATB_pb_Search(SHORT phonebook_id, T_PB_INDEX index_type, T_PB_RECORD *record, SHORT *recs_count) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); PB_RET result; SHORT count = 0; SHORT log_index; T_PB_MATCH match; T_PB_RECORD *cur_record; /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } /* Allocate record for temporary use */ cur_record = ATB_pb_AllocRec(phonebook_id); /* Make sure phonebook is not busy */ if (ATB_pb_Status(phonebook_id)==PB_BUSY) { trace("* ERROR * - Phonebook is busy"); GI_pb_Error(phonebook_id, PB_SEARCH, PB_BUSY); return PB_BUSY; } /* Make sure we're searching a valid index type */ if (index_type==INDEX_PHYSICAL || index_type==INDEX_SEARCH) { GI_pb_Error(phonebook_id, PB_SEARCH, PB_INDEXINVALID); return PB_INDEXINVALID; } for (log_index=0; log_index<data->records_used; log_index++) { result = ATB_mem_ReadRec(phonebook_id, index_type, log_index, cur_record); if (result!=PB_OK) break; match = ATB_index_Match(record, cur_record, index_type); if (match==MATCH_EXACT || match==MATCH_START || match==MATCH_FRAGMENT) { data->search_table[count] = ATB_index_GetPhysIndex(phonebook_id, index_type, log_index); count++; } } data->search_results = count; *recs_count = count; /* Free allocated record */ ATB_pb_FreeRec(phonebook_id, cur_record); /* Close any open file */ FS_pb_Finished(phonebook_id); /* Send success/failure info to the GI */ if (result==PB_OK) GI_pb_OK(phonebook_id, PB_SEARCH, NULL); else if (result!=PB_EXCT) GI_pb_Error(phonebook_id, PB_SEARCH, result); return result; } /******************************************************************************* $Function: ATB_pb_ConvIndex $Description: Returns the index in table dest_index_type corresponding to the index in table src_index_type. $Returns: PB_OK Action completed OK. $Arguments: phonebook_id The phonebook identifier. index_type The index table of the original index. log_index The original logical index. new_index_type The index table required. new_log_index Pointer to where the new logical index will be stored *******************************************************************************/ PB_RET ATB_pb_ConvIndex(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index, T_PB_INDEX new_index_type, SHORT *new_log_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT phys_index; /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } if (index_type==new_index_type) { *new_log_index = log_index; } else { phys_index = ATB_index_GetPhysIndex(phonebook_id, index_type, log_index); *new_log_index = ATB_index_GetLogIndex(phonebook_id, new_index_type, phys_index); } return PB_OK; } /******************************************************************************* $Function: ATB_pb_CharToBCD $Description: Converts an ascii string of digits into BCD form, with 4 bits representing each digit. $Returns: None $Arguments: src - source string (ascii) dest - destination string for BCD digits *******************************************************************************/ void ATB_pb_CharToBCD(UBYTE *dest, char *src, int max_len) { UBYTE srcIndex = 0; UBYTE destIndex = 0; BOOL leftbits = TRUE; /* Left or right nibble */ UBYTE digit = 0; while (digit!=0xF && srcIndex<max_len) { if (src[srcIndex]==NULL) digit = 0xF; /* 0xF terminates BCD */ else digit = src[srcIndex]-'0'; /* The digit, 0 to 9. */ if (leftbits) { dest[destIndex] = digit; leftbits = FALSE; } else { dest[destIndex] |= digit<<4; /* *16 shifts right 4 bits */ leftbits = TRUE; destIndex++; } srcIndex++; } return; } /******************************************************************************* $Function: ATB_pb_BCDToChar $Description: Converts a BCD string to ascii digits $Returns: None $Arguments: src - source string (BCD) dest - destination string for ascii digits *******************************************************************************/ void ATB_pb_BCDToChar(char *dest, UBYTE *src, int max_len) { SHORT numIndex = 0; UBYTE digit = 0xF; //dummy while (digit!=NULL && numIndex<max_len) { /*HELLO!*/ digit = ATB_num_Digit(src, numIndex); if (digit==0xF) digit = NULL; else digit+='0'; dest[numIndex] = digit; numIndex++; } return; } /******************************************************************************* $Function: ATB_pb_AllocRec $Description: Allocates memory for a record $Returns: $Arguments: phonebook_id The phonebook identifier *******************************************************************************/ T_PB_RECORD *ATB_pb_AllocRec(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); T_PB_RECORD *record; record = (T_PB_RECORD *)GI_pb_MemAlloc(sizeof(T_PB_RECORD)); record->alpha.data = (USHORT *)GI_pb_MemAlloc(data->alpha_max*sizeof(USHORT)); record->number = (UBYTE *)GI_pb_MemAlloc(data->number_max/2); if (data->ext_max>0) { record->ext_data = (UBYTE *)GI_pb_MemAlloc(data->ext_max); } else { record->ext_data = NULL; } record->ton_npi = 0; return record; } /******************************************************************************* $Function: ATB_pb_FreeRec $Description: Frees memory allocated for a record $Returns: $Arguments: phonebook_id The phonebook identifier record The record to destroy *******************************************************************************/ void ATB_pb_FreeRec(SHORT phonebook_id, T_PB_RECORD *record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); if (data->ext_max>0 && record->ext_data!=NULL) { GI_pb_MemFree((UBYTE *)record->ext_data, data->ext_max); } GI_pb_MemFree((UBYTE *)record->number, data->number_max/2); GI_pb_MemFree((UBYTE *)record->alpha.data, data->alpha_max*sizeof(USHORT)); GI_pb_MemFree((UBYTE *)record, sizeof(T_PB_RECORD)); return; } /******************************************************************************* $Function: ATB_pb_AllocRecList $Description: Allocates memory for a list of records $Returns: $Arguments: phonebook_id The phonebook identifier num_recs The number of records to allocate *******************************************************************************/ T_PB_LIST *ATB_pb_AllocRecList(SHORT phonebook_id, SHORT num_recs) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); T_PB_LIST *list; SHORT rec_index; list = (T_PB_LIST *)GI_pb_MemAlloc(sizeof(T_PB_LIST)); list->records_max = num_recs; list->records_used = 0; list->record = (T_PB_RECORD *)GI_pb_MemAlloc(sizeof(T_PB_RECORD)*num_recs); for (rec_index=0; rec_index<num_recs; rec_index++) { list->record[rec_index].alpha.data = (USHORT *)GI_pb_MemAlloc(data->alpha_max*sizeof(USHORT)); list->record[rec_index].number = (UBYTE *)GI_pb_MemAlloc(data->number_max/2); if (data->ext_max>0) { list->record[rec_index].ext_data = (UBYTE *)GI_pb_MemAlloc(data->ext_max); } else { list->record[rec_index].ext_data = NULL; } } return list; } /******************************************************************************* $Function: ATB_pb_FreeRecList $Description: Frees memory allocated for a list of records $Returns: $Arguments: phonebook_id The phonebook identifier record The records to destroy num_recs Number of records in the list *******************************************************************************/ void ATB_pb_FreeRecList(SHORT phonebook_id, T_PB_LIST *list) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT rec_index; for (rec_index=0; rec_index<list->records_max; rec_index++) { if (data->ext_max>0 && list->record[rec_index].ext_data!=NULL) { GI_pb_MemFree((UBYTE *)list->record[rec_index].ext_data, data->ext_max); } GI_pb_MemFree((UBYTE *)list->record[rec_index].number, data->number_max/2); GI_pb_MemFree((UBYTE *)list->record[rec_index].alpha.data, data->alpha_max*sizeof(USHORT)); } GI_pb_MemFree((UBYTE *)list->record, sizeof(T_PB_RECORD)*list->records_max); GI_pb_MemFree((UBYTE *)list, sizeof(T_PB_LIST)); return; } /******************************************************************************* $Function: ATB_mem_CopyRec $Description: Copies a record from one location to another. The destination should have memory allocated for its strings. $Returns: $Arguments: dest_record The destination record src_record The source record *******************************************************************************/ void ATB_pb_CopyRec(SHORT phonebook_id, T_PB_RECORD *dest_record, T_PB_RECORD *src_record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); dest_record->alpha.length = src_record->alpha.length; dest_record->alpha.dcs = src_record->alpha.dcs; memcpy((UBYTE *)dest_record->alpha.data, (UBYTE *)src_record->alpha.data, data->alpha_max*sizeof(USHORT)); memcpy(dest_record->number, src_record->number, data->number_max/2); if (data->ext_max>0) { memcpy(dest_record->ext_data, src_record->ext_data, data->ext_max); } dest_record->ton_npi = src_record->ton_npi; return; } /******************************************************************************* $Function: ATB_pb_OK $Description: This function is called if the requested FS command executed successfully. $Returns: None. $Arguments: phonebook_id The phonebook identifier *******************************************************************************/ PB_RET ATB_pb_OK(SHORT phonebook_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); PB_RET result; switch(data->command_id) { case PB_INITIALISE: result = ATB_status_Initialise(phonebook_id); break; case PB_WRITEREC: result = ATB_status_WriteRec(phonebook_id); break; case PB_DELETEREC: result = ATB_status_DeleteRec(phonebook_id); break; } return result; } /******************************************************************************* $Function: ATB_pb_Error $Description: This function is called if an error was returned while executing the requested command. $Returns: None. $Arguments: phonebook_id The phonebook identifier error_id Identifies the error that occurred. *******************************************************************************/ void ATB_pb_Error(SHORT phonebook_id, SHORT error_id) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); /* Forward the error report to the GI */ GI_pb_Error(phonebook_id, data->command_id, error_id); data->status = PB_STATUS_NONE; return; } /******************************************************************************* $Function: ATB_index_GetTable $Description: Returns a pointer to the index table specified by the index_type $Returns: None. $Arguments: phonebook_id The phonebook identifier index_type The index table required. *******************************************************************************/ SHORT * ATB_index_GetTable(SHORT phonebook_id, T_PB_INDEX index_type) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT *table = NULL; switch(index_type) { case INDEX_NAME: table = data->name_table; break; case INDEX_NUMBER: table = data->number_table; break; case INDEX_SEARCH: table = data->search_table; break; default: table = GI_pb_GetTable(index_type); break; } return table; } /******************************************************************************* $Function: ATB_index_Find $Description: Find the nearest match to the supplied record. See ATB_pb_Find for more information. $Returns: PB_OK Action completed OK. PB_BUSY Failed, phonebook is busy. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist (should never happen) $Arguments: phonebook_id The phonebook identifier index_type The index_type of the provided record that is to be searched for, and the index list that the resulting new_log_index pertains to record The record whose index_type is to be matched new_log_index Returns with the logical index of the closest matching record match Specifies how good the resulting match was (MATCH_NONE, MATCH_START, MATCH_FRAGMENT or MATCH_EXACT). *******************************************************************************/ PB_RET ATB_index_Find (SHORT phonebook_id, T_PB_INDEX index_type, T_PB_RECORD *record, SHORT *new_log_index, T_PB_MATCH *match) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); PB_RET result = PB_OK; SHORT *table; SHORT lower; SHORT upper; SHORT middle; T_PB_RECORD *comp_record; T_PB_COMPARE compare; UBYTE searching; /* Ensure that phonebook exists */ if (!data) { trace("**ERROR** Phonebook does not exist"); return PB_BOOKDOESNOTEXIST; } /* Allocate record for comparison */ comp_record = ATB_pb_AllocRec(phonebook_id); /* Make sure we're searching a valid index type */ if (index_type==INDEX_PHYSICAL || index_type==INDEX_SEARCH) { GI_pb_Error(phonebook_id, PB_FIND, PB_INDEXINVALID); return PB_INDEXINVALID; } table = ATB_index_GetTable(phonebook_id, index_type); /* The initial upper and lower bounds are the bounds of the array */ lower = 0; upper = data->records_used-1; searching = TRUE; /* If the list is empty, insert at 0 */ if (data->records_used==0) { middle = 0; searching = FALSE; } while (searching) { /* Compare our record with the middle element of the partition */ middle = (lower+upper)/2; result = ATB_mem_ReadRec(phonebook_id, index_type, middle, comp_record); if (result!=PB_OK) break; compare = ATB_index_Compare(record, comp_record, index_type); switch(compare) { case COMPARE_IDENTICAL: /* We've found a match; stop searching */ searching = FALSE; break; case COMPARE_FIRSTBEFORE: if (middle==lower) { /* The partition is a single element, so stop here. */ searching = FALSE; } else { /* Our record comes before the middle element; * redefine the partiton */ upper = middle - 1; } break; case COMPARE_FIRSTAFTER: if (middle==upper) { /* The partition is a single element, so stop here. The record * we need is the record after this one. */ middle++; searching = FALSE; } else { /* Our record comes after the middle element; * redefine the partiton */ lower = middle+1; } break; } } /* Store our results */ if (new_log_index) *new_log_index = middle; if (match) { ATB_mem_ReadRec(phonebook_id, index_type, middle, comp_record); *match = ATB_index_Match(record, comp_record, index_type); } /* Free allocated record */ ATB_pb_FreeRec(phonebook_id, comp_record); /* Send success/failure info to the GI */ if (result==PB_OK) GI_pb_OK(phonebook_id, PB_FIND, NULL); else if (result!=PB_EXCT) GI_pb_Error(phonebook_id, PB_FIND, result); return result; } /******************************************************************************* $Function: ATB_index_GetPhysIndex $Description: Returns the physical index corresponding to the given logical index, in the index table specified by the index_type. $Returns: None. $Arguments: phonebook_id The phonebook identifier. index_type The index table required. log_index The logical index. *******************************************************************************/ SHORT ATB_index_GetPhysIndex(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT phys_index; SHORT *table; switch(index_type) { case INDEX_PHYSICAL: phys_index = log_index; break; case INDEX_NAME: case INDEX_NUMBER: case INDEX_SEARCH: table = ATB_index_GetTable(phonebook_id, index_type); phys_index = table[log_index]; break; } return phys_index; } /******************************************************************************* $Function: ATB_index_GetLogIndex $Description: Provided with the physical index, finds the logical index of the record in the index table specified. $Returns: None. $Arguments: phonebook_id The phonebook identifier index_type The index table required. phys_index The logical index of the record to find *******************************************************************************/ SHORT ATB_index_GetLogIndex(SHORT phonebook_id, T_PB_INDEX index_type, SHORT phys_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT *table = ATB_index_GetTable(phonebook_id, index_type); SHORT log_index; if (index_type==INDEX_PHYSICAL) { log_index = phys_index; } else { for (log_index=0; log_index<data->records_used; log_index++) { if (table[log_index]==phys_index) break; } } return log_index; } /******************************************************************************* $Function: ATB_index_AddRec $Description: Adds a record to an index list, sorting automatically as required. $Returns: None. $Arguments: phonebook_id The phonebook identifier index_type The index table required. phys_index The physical index of the record to add. record The record to add. new_log_index New logical index of record. *******************************************************************************/ void ATB_index_AddRec(SHORT phonebook_id, T_PB_INDEX index_type, SHORT phys_index, T_PB_RECORD *record, SHORT *new_log_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT *table = ATB_index_GetTable(phonebook_id, index_type); SHORT log_index; SHORT found_log_index; ATB_index_Find(phonebook_id, index_type, record, &found_log_index, NULL); /* Shift end of index table down */ for (log_index=data->records_used; log_index>found_log_index; log_index--) { table[log_index] = table[log_index-1]; } table[found_log_index] = phys_index; if (new_log_index) *new_log_index = found_log_index; return; } /******************************************************************************* $Function: ATB_index_RemoveRec $Description: Removes a record reference from an index list, sorting the index list appropriately. $Returns: None. $Arguments: phonebook_id The phonebook identifier index_type The index table required. log_index The logical index of the record to remove *******************************************************************************/ void ATB_index_RemoveRec(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT *table = ATB_index_GetTable(phonebook_id, index_type); SHORT table_index; for (table_index = log_index+1; table_index<data->records_used; table_index++) { table[table_index-1] = table[table_index]; } return; } /******************************************************************************* $Function: ATB_index_Match $Description: This function attempts a match between two records based on the index_type specified in index_type. It returns a value indicating whether there is no match (MATCH_NONE), a partial match (MAX_START), or an exact match (MATCH_EXACT). The function GI_pb_Match is called first, to check whether there is any user-specific matching required. If not, then ATB_alpha_Match is called for an alpha tag, or ATB_num_Match for a phone number. For any other index_type, MATCH_NONE is returned as default. $Returns: MATCH_EXACT, MATCH_START or MATCH_NONE $Arguments: record1 The first record to be compared record2 The second record to be compared index_type Indicator of the index_type which is to be matched *******************************************************************************/ T_PB_MATCH ATB_index_Match(T_PB_RECORD *record1, T_PB_RECORD *record2, T_PB_INDEX index_type) { T_PB_MATCH match; match = GI_pb_Match(record1, record2, index_type); if (match==MATCH_DEFAULT) { switch(index_type) { case INDEX_NAME: match = ATB_alpha_Match(&record1->alpha, &record2->alpha); break; case INDEX_NUMBER: match = ATB_num_Match(record1->number, record2->number); break; } } return match; } /******************************************************************************* $Function: ATB_index_Compare $Description: This function compares two records based on the index_type specified in index_type. It returns a value indicating whether the first record should come before or after the second. The function GI_pb_Compare is called first, to check whether there is any user-specific sorting required. If not, then ATB_alpha_Compare is called for an alpha tag, or ATB_num_Compare for a phone number. For any other index_type, COMPARE_FIRSTBEFORE is returned as default. $Returns: COMPARE_IDENTICAL The two records are identical COMPARE_FIRSTBEFORE The first record should come before the second record COMPARE_FIRSTAFTER The first record should come after the second record $Arguments: record1 The first record to be compared record2 The second record to be compared index_type Indicator of the index_type which is to be compared *******************************************************************************/ T_PB_COMPARE ATB_index_Compare(T_PB_RECORD *record1, T_PB_RECORD *record2, T_PB_INDEX index_type) { T_PB_COMPARE compare; compare = GI_pb_Compare(record1, record2, index_type); if (compare==COMPARE_DEFAULT) { switch(index_type) { case INDEX_NAME: compare = ATB_alpha_Compare(&record1->alpha, &record2->alpha); break; case INDEX_NUMBER: compare = ATB_num_Compare(record1->number, record2->number); break; } } return compare; } /******************************************************************************* $Function: ATB_alpha_Compare $Description: This function compares two alpha strings on the basis of an ascending alphanumeric latin list, and specifies which record would come first. $Returns: COMPARE_FIRSTBEFORE The first record should come before the second record COMPARE_FIRSTAFTER The first record should come after the second record $Arguments: alpha1 The first alpha tag alpha2 The second alpha tag *******************************************************************************/ T_PB_COMPARE ATB_alpha_Compare(T_PB_ALPHA *alpha1, T_PB_ALPHA *alpha2) { T_PB_COMPARE compare; SHORT charIndex; USHORT length1 = alpha1->length; USHORT length2 = alpha2->length; USHORT char1; USHORT char2; compare = COMPARE_IDENTICAL; /* Default */ /* Special case, length of first string is 0 */ if (length1==0) compare = COMPARE_FIRSTBEFORE; for (charIndex = 0; charIndex < length1; charIndex++) { if (charIndex==length2) /* E.g. "Johnson" and "John" */ { compare = COMPARE_FIRSTAFTER; break; } char1 = alpha1->data[charIndex]; if (char1>=(SHORT)'A' && char1<=(SHORT)'Z') char1 += (SHORT)('a'-'A'); char2 = alpha2->data[charIndex]; if (char2>=(SHORT)'A' && char2<=(SHORT)'Z') char2 += (SHORT)('a'-'A'); if (char1 < char2) { compare = COMPARE_FIRSTBEFORE; break; } if (char1 > char2) { compare = COMPARE_FIRSTAFTER; break; } if (charIndex==length1-1 && length2>length1) /*E.g. "John" and "Johnson" */ { compare = COMPARE_FIRSTBEFORE; break; } } return compare; } /******************************************************************************* $Function: ATB_alpha_Match $Description: This function matches two alpha strings on the basis of a latin string matched from the start, and specifies how they match. $Returns: MATCH_NONE The strings do not match MATCH_START The first string matches the start of the second string MATCH_EXACT The two strings match exactly $Arguments: alpha1 The first alpha tag alpha2 The second alpha tag *******************************************************************************/ T_PB_COMPARE ATB_alpha_Match(T_PB_ALPHA *alpha1, T_PB_ALPHA *alpha2) { T_PB_MATCH match; SHORT charIndex; USHORT length1 = alpha1->length; USHORT length2 = alpha2->length; SHORT offset; UBYTE searching; UBYTE some_match; USHORT char1, char2; match = MATCH_EXACT; /* Default */ searching = TRUE; some_match = FALSE; offset = 0; do { for (charIndex = 0; charIndex < length1; charIndex++) { if (charIndex==(length2-offset)) /* E.g. "Johnson" and "John" */ { searching = FALSE; break; /* No match, will exit do/while */ } char1 = alpha1->data[charIndex]; if (char1>=(SHORT)'A' && char1<=(SHORT)'Z') char1 += (SHORT)('a'-'A'); char2 = alpha2->data[charIndex+offset]; if (char2>=(SHORT)'A' && char2<=(SHORT)'Z') char2 += (SHORT)('a'-'A'); if (char1 != char2) { some_match = FALSE; /* Any fragment so far identified does not fit */ break; /* No match, keep looking */ } some_match = TRUE; if (charIndex==length1-1 && length2>length1) /*E.g. "John" and "Johnson" */ { if (offset==0) match = MATCH_START; else match = MATCH_FRAGMENT; break; } } if (some_match==TRUE) { searching = FALSE; } else { offset++; /* If the fragment won't fit, don't keep looking */ if ((offset+length1)>length2) { match=MATCH_NONE; searching = FALSE; } } }while (searching); return match; } /******************************************************************************* $Function: ATB_num_Digit $Description: Extracts the four-bit digit from a BCD number $Returns: The four bit digit $Arguments: num The BCD number numIndex The position in the BCD number to look *******************************************************************************/ UBYTE ATB_num_Digit(UBYTE *num, SHORT numIndex) { UBYTE digit; UBYTE shift = (numIndex&0x1)*0x4; digit = num[numIndex>>1]; /* Each BCD digit takes up half a byte */ digit &= (0xF<<shift); /* Isolate the digit */ digit >>= shift; /* Shift it so it has a value 0x0 - 0xF */ return digit; } /******************************************************************************* $Function: ATB_num_Length $Description: Returns the length of a BCD number in digits $Returns: Length of BCD number $Arguments: num The BCD number *******************************************************************************/ SHORT ATB_num_Length(UBYTE *num) { SHORT length; for (length = 0; ATB_num_Digit(num, length)!=0xF; length++) { } return length; } /******************************************************************************* $Function: ATB_num_Compare $Description: This function compares two phone numbers by the standard comparison method (ascending numeric, from the end of the number) and specifies which record would come first if sorted in this method. $Returns: COMPARE_FIRSTBEFORE The first record should come before the second record COMPARE_FIRSTAFTER The first record should come after the second record $Arguments: num1 The first number num2 The second number *******************************************************************************/ T_PB_COMPARE ATB_num_Compare(UBYTE *num1, UBYTE *num2) { T_PB_COMPARE compare; SHORT charIndex; SHORT length1 = ATB_num_Length(num1); SHORT length2 = ATB_num_Length(num2); UBYTE digit1; UBYTE digit2; compare = COMPARE_IDENTICAL; /* Default */ for (charIndex = 0; charIndex < length1; charIndex++) { if (charIndex==length2) /* E.g. "123456" and "1234" */ { compare = COMPARE_FIRSTAFTER; break; } digit1 = ATB_num_Digit(num1, (SHORT)(length1-charIndex-1)); digit2 = ATB_num_Digit(num2, (SHORT)(length2-charIndex-1)); if (digit1 < digit2) { compare = COMPARE_FIRSTBEFORE; break; } if (digit1 > digit2) { compare = COMPARE_FIRSTAFTER; break; } if (charIndex==length1-1 && length2>length1) /*E.g. "1234" and "123456" */ { compare = COMPARE_FIRSTBEFORE; break; } } return compare; } /******************************************************************************* $Function: ATB_num_Match $Description: This function matches phone numbers, from the end backwards, and specifies how they match. $Returns: MATCH_NONE The numbers do not match MATCH_START The first number matches the end of the second number MATCH_EXACT The two numbers match exactly $Arguments: num1 The first number num2 The second number *******************************************************************************/ T_PB_MATCH ATB_num_Match(UBYTE *num1, UBYTE *num2) { T_PB_MATCH match; SHORT charIndex; SHORT length1 = ATB_num_Length(num1); SHORT length2 = ATB_num_Length(num2); SHORT offset; UBYTE searching; UBYTE some_match; UBYTE digit1; UBYTE digit2; match = MATCH_EXACT; /* Default */ searching = TRUE; some_match = FALSE; offset = 0; do { for (charIndex = 0; charIndex < length1; charIndex++) { if (charIndex==(length2-offset)) /* E.g. "12345" and "123" */ { searching = FALSE; break; /* No match, will exit do/while */ } digit1 = ATB_num_Digit(num1, (SHORT)(length1-charIndex-1)); digit2 = ATB_num_Digit(num2, (SHORT)(length2-charIndex-1)); if (digit1 != digit2) { some_match = FALSE; /* Any fragment so far identified does not fit */ break; /* No match, keep looking */ } some_match = TRUE; if (charIndex==length1-1 && length2>length1) /*E.g. "123" and "12345" */ { if (offset==0) match = MATCH_START; else match = MATCH_FRAGMENT; break; } } if (some_match==TRUE) { searching = FALSE; } else { offset++; /* If the fragment won't fit, don't keep looking */ if ((offset+length1)>length2) { match=MATCH_NONE; searching = FALSE; } } } while (searching); return match; } /******************************************************************************* $Function: ATB_mem_UpdateCache $Description: Updates the cache with the record provided. If the cache is full, the least used record is overwritten. $Returns: None. $Arguments: phonebook_id The phonebook identifier index_type The index table required. phys_index The physical index of the record to add. record The record to add. *******************************************************************************/ void ATB_mem_UpdateCache(SHORT phonebook_id, SHORT phys_index, T_PB_RECORD *record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT cacheIndex; SHORT leastUsed; SHORT leastIndex; /* First check if record is already in RAM */ if (data->cache[phys_index] != NULL) { /* Copy the record in case it has changed */ ATB_pb_CopyRec(phonebook_id, data->cache[phys_index], record); if (data->in_memory[phys_index]<0xFFFF) data->in_memory[phys_index]++; return; } /* If the cache is full, find the least accessed record */ if (data->cache_size==data->cache_max) { leastIndex = 0; leastUsed = 255; for (cacheIndex=0; cacheIndex<data->records_max; cacheIndex++) { if (data->cache[cacheIndex]!=NULL && data->in_memory[cacheIndex] < leastUsed) { leastUsed = data->in_memory[cacheIndex]; leastIndex = cacheIndex; } } /* Give the new record the memory allocated for the least used record */ data->cache[phys_index] = data->cache[leastIndex]; data->cache[leastIndex] = NULL; data->in_memory[leastIndex] = 0; } /* If the cache is not full, allocate memory for record */ else { data->cache[phys_index] = ATB_pb_AllocRec(phonebook_id); data->cache_size++; } /* Copy the record into cache */ ATB_pb_CopyRec(phonebook_id, data->cache[phys_index], record); data->in_memory[phys_index] = 1; return; } /******************************************************************************* $Function: ATB_mem_ReadRec $Description: Reads a record from the logical position index. Information is read from cache if it is present in memory, otherwise it will be read from file. The record information will be returned in the data structure pointed to by record. The T_PB_RECORD structure must be allocated by the caller. $Returns: PB_OK Action completed OK. PB_FILEREADFAIL File read encountered an error PB_RECDOESNOTEXIST Tried to access a record that does not exist $Arguments: phonebook_id The phonebook identifier index_type The index list from which the record is to be read log_index Logical index in the index list of the record to read record Structure in which to store record data *******************************************************************************/ PB_RET ATB_mem_ReadRec(SHORT phonebook_id, T_PB_INDEX index_type, SHORT log_index, T_PB_RECORD *record) { T_PB_DATA *data = ATB_hnd_GetPbData(phonebook_id); SHORT phys_index; PB_RET result; /* Check that record exists */ if (log_index<0 || (log_index>=data->records_used && (index_type==INDEX_NAME || index_type==INDEX_NUMBER)) || (index_type==INDEX_SEARCH && log_index>data->search_results) || (index_type==INDEX_PHYSICAL && log_index>data->records_max)) { trace("*** Error - record does not exist ***"); return PB_RECDOESNOTEXIST; } /* Get the corresponding physical record index */ phys_index = ATB_index_GetPhysIndex(phonebook_id, index_type, log_index); /* If the record is in the cache, simply copy it. Otherwise, call FS to read the record. */ if (data->in_memory[phys_index]>0) { ATB_pb_CopyRec(phonebook_id, record, data->cache[phys_index]); result = PB_OK; } else { /* NB - assumes blocking read operation. If the read operation is blocking, * the cache must be large enough to store all the phonebook entries. */ result = FS_pb_ReadRec(phonebook_id, phys_index, record); ATB_mem_UpdateCache(phonebook_id, phys_index, record); } return result; } /******************************************************************************* $Function: ATB_hnd_GetPbData $Description: Returns phonebook data handle $Returns: The phonebook data handle $Arguments: phonebook_id The phonebook identifier *******************************************************************************/ T_PB_DATA* ATB_hnd_GetPbData(SHORT phonebook_id) { return phonebook[phonebook_id]; } /******************************************************************************* $Function: ATB_hnd_SetPbData $Description: Sets phonebook data handle $Returns: None $Arguments: phonebook_id The phonebook identifier data The phonebook data *******************************************************************************/ void ATB_hnd_SetPbData(SHORT phonebook_id, T_PB_DATA *data) { phonebook[phonebook_id] = data; return; }