diff src/aci2/atb/ATBPb.c @ 3:93999a60b835

src/aci2, src/condat2: import of g23m/condat source pieces from TCS211
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Sep 2016 00:29:36 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aci2/atb/ATBPb.c	Mon Sep 26 00:29:36 2016 +0000
@@ -0,0 +1,2422 @@
+/*******************************************************************************
+
+					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;
+}
+