/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   datamgr.c
Author(s)   :   Kenny Ng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   Pen low level device driver
Revision    :   1.1
Note        :   
Modification History                               
25/10/98    : Split out from mmu.c for faster complie                   
30/10/98    : Add function DataCategorySort() and DataCategoryNextFee()  
03/11/98    : DataCloseRecord bug fix                                   
DataCategoryName() can accept NULL as the name parameter  
18/11/98    : Add function DataGetSortField			           
23/11/98    : Add function DataNewRecordWithID()			   
24/11/98    : Modify DataGetField to support function                     
DataNewRecordWithID()					    
20/01/99    : Fix DataCategoryNextFree(), index start from 1, not 0     
24/03/99    : Revise coding						   
12/05/99    : Add Checksum to data content			
===========================================================================
*/              


#include "stdafx.h" 
#include "mmu.h"
#include "str.h"  
#include "system.h"
//#define DEBUG
OpenedDBPtr opendb_table;
DatabaseID next_dbid;
UBYTE mmu_data[DATA_CMP_SIZE];
BYTE field_data1[DATA_CMP_SIZE];

UBYTE	*QuickSortData = NULL;
UBYTE	**QuickSortDataArray = NULL;

BYTE writable;
BOOLEAN MemoryCheckMemLow();
void DataClearFieldCache();
Err DataReadBDataWithChk(UWORD sblock, UWORD offset, UWORD num_byte, BYTE *buffer, BYTE **read_ptr, BYTE **write_ptr, UWORD *last_sblock);
void DataSCatTable(OpenedDBPtr open_ptr);

#ifdef DATA_CHECK_SUM
#define CAL_CHKSUM      MemoryUpdateCheckSum();
#else
#define CAL_CHKSUM        /* do not cal. check sum */
#endif


#ifdef DEBUG
Err DataChkField(DatabaseID dbid, RecordID rec_id, USHORT field_num);
#endif

DatabaseID DataNewDBID() {return next_dbid++;}

UWORD d_fsize,  d_start_sblock,d_first_offset ,d_last_size ;
USHORT d_num_block;
BOOLEAN d_frag;

_DBCache dbcache;

SHORT textstrcmp(UBYTE *src, UBYTE *dest);
SHORT strcmpubyte(UBYTE *string1, UBYTE *string2);

UWORD   quick_count;

void DataUpdateSortTableCat(OpenedDBPtr db_ptr, RecordID rec_id, UBYTE cat);

Err DataFieldInfo2(UWORD sblock, RecordID rec_id, USHORT field, UWORD *size, UWORD *start_sblock,
				   UWORD *first_offset, USHORT *num_block, UWORD *last_size, BOOLEAN *frag);

void data_copy_bin_str(BYTE *dest, BYTE *src)
{
	UWORD i;
	for(i=0;i<DATA_CMP_SIZE;i++)
		*(dest++) = *(src++);
}

/*********************************************************************
* Function	: DataCopyField
* Purpose	: Get the field data by the requested size
* Scope		: Internal
* Input		:
* Output	: None
* Return	: None
* Comment	: None
**********************************************************************/
void DataCopyField(BYTE *dest_ptr, UWORD sblock, RecordHeader *rec_ptr, USHORT field_num, UWORD len)
{
	BYTE 	*src_ptr;
	UBYTE   j;
	UWORD 	i;
	UWORD 	field_size, byte_use;
	
	src_ptr = (BYTE*) ((UWORD)rec_ptr + RECHEADER_SIZE); /* position to field size byte */
	
	for(i=0; i<=field_num; i++) {
		field_size = 0;
		byte_use = 0;
		do {
			j = *src_ptr;
			field_size |= ((j >> 1) << (byte_use*7));
			byte_use++;
			_valid_ptr(&sblock, &src_ptr, 1, NULL);
		} while (!(j & 1));
		if (i<field_num) _valid_ptr(&sblock, &src_ptr, field_size, NULL);
	}
	
	for(i=0; (i<len) && (i<field_size);  i++) {
		*dest_ptr++ = *src_ptr;
		_valid_ptr(&sblock, (BYTE **)&src_ptr, 1, NULL);
    }
	
}
/*********************************************************************
* Function	: Parition
* Purpose	: do a Quick Sort
* Scope		: Application/Internal
* Input		: 
* Output	: None
* Return	: None
* Comment	: None
**********************************************************************/
UWORD Partition(UWORD db_sblock, SortTableLnkPtr sort_table, UWORD total_rec, SHORT lb, SHORT ub)
{
	//	BYTE 		*ptr;
	WORD		offset, pivot, i, j;
	RecordID	temp_id;
	UBYTE		*temp_ptr;
	RecordID    pivot_recid;
	RecordID 	*record_id;
	UWORD       pivot_sblock;
	SHORT		sort_field, temp_sblock;
	UBYTE		pivot_cat;
	UBYTE		*pivot_ptr;
	
	
	
#define FIRST_SMALLER		-10
#define SECOND_SMALLER		10
#define CMP_EQUAL			0
	
	/* select pivot and exchange with 1st element */
	offset	=	(ub - lb) >> 1;
	pivot	=	lb + offset;
	
	
	record_id 	= sort_table->rec_id;
	pivot_recid = record_id[pivot];
	pivot_cat	= sort_table->cat[pivot];
	pivot_ptr	= QuickSortDataArray[pivot];
	
	i = DATA_CMP_SIZE - 1;
	while (i-- >= 0)
		field_data1[i] = pivot_ptr[i];
	
	record_id[pivot]			= record_id[lb];
    sort_table->cat[pivot]  	= sort_table->cat[lb];
    QuickSortDataArray[pivot]	= QuickSortDataArray[lb];
	
	i = lb + 1;
	j = ub;
	
	if( (sort_table)->sort_field & (USHORT) SORT_TEXT_MODE)
	{
		while (1)
		{
			while ( (i < j) && (textstrcmp((UBYTE*)field_data1, QuickSortDataArray[i]) == 10))
				i++;
			
			while ((j >= i) && (textstrcmp(QuickSortDataArray[j], (UBYTE*)field_data1) == 10))
				j--;
			
			if (i >= j)
				break;
			temp_id	= record_id[i];
			record_id[i] = record_id[j];
			record_id[j] = temp_id;
			
			temp_id = sort_table->cat[i];
			sort_table->cat[i] = sort_table->cat[j];
			sort_table->cat[j] = temp_id;
			
			temp_ptr	= QuickSortDataArray[i];
			QuickSortDataArray[i] = QuickSortDataArray[j];
			QuickSortDataArray[j] = temp_ptr;
			
			j --;
			i ++;
		}
	}
	else
	{
		while (1)
		{
			while ( (i < j) && (strcmpubyte((UBYTE*)field_data1, QuickSortDataArray[i]) > 0))
				i++;
			
			while ((j >= i) && (strcmpubyte(QuickSortDataArray[j], (UBYTE*)field_data1) > 0))
				j--;
			
			
			if (i >= j)
				break;
			temp_id	= record_id[i];
			record_id[i] = record_id[j];
			record_id[j] = temp_id;
			
			temp_id	= sort_table->cat[i];
			sort_table->cat[i] = sort_table->cat[j];
			sort_table->cat[j] = temp_id;
			
			temp_ptr	= QuickSortDataArray[i];
			QuickSortDataArray[i] = QuickSortDataArray[j];
			QuickSortDataArray[j] = temp_ptr;
			
			j --;
			i ++;
		}
	}
	
	/* pivot belongs in the array */
	record_id[lb]			= record_id[j];
	sort_table->cat[lb]		= sort_table->cat[j];
	QuickSortDataArray[lb] 	= QuickSortDataArray[j];	
	
	
	record_id[j]			= pivot_recid;
	sort_table->cat[j]		= pivot_cat;
	QuickSortDataArray[j] 	= pivot_ptr;	
	
	return j;
}

/*********************************************************************
* Function	: DataQuickSortData
* Purpose	: This function is called to do a Quick Sort on the records in a Database
* Scope		: Application/Internal
* Input		: db_sblock
: sort_table		pointer to SORT TABLE
: total_rec			total number of records	
* Output	: None
* Return	: None
* Comment	: None
**********************************************************************/
void DataQuickSortData(UWORD db_sblock, SortTableLnkPtr sort_table, UWORD total_rec, SHORT lb, SHORT ub)
{
	UWORD		pivot;
	
	while (lb < ub)
	{
		/* parition into two segments */
		pivot = Partition(db_sblock, sort_table, total_rec, lb, ub);
		
		/* sort the smallest parition */
		if (pivot - lb <= ub - pivot)
		{
			DataQuickSortData(db_sblock, sort_table, total_rec, lb, pivot - 1);
			lb = pivot + 1;
		}
		else 
		{
			DataQuickSortData(db_sblock, sort_table, total_rec, pivot + 1, ub);
			ub = pivot - 1;
		}
	}	
}


UWORD _rtm2date32(RTM dat)
{
	if(dat.year < 1972)	/* cannot convert year before 1972 */
		return 0;
	return ((((UWORD) (((dat.year-1972)<<9) | (dat.mon<<5) | dat.mday)) << 16) & 0xFFFF0000) |
	       (((UWORD) ((dat.hour<<11) | (dat.min<<5) | (dat.sec/2))) & 0x0000FFFF);
}

RTM _date322rtm(UWORD dat)
{
	RTM dest;
	
	dest.sec  = (USHORT) (dat & 0x0000001F) * 2;
	dest.min  = (USHORT) (dat >> 5) & 0x0000003F;
	dest.hour = (USHORT) (dat >> 11) & 0x0000001F;
	dest.mday = (USHORT) (dat >> 16) & 0x0000001F;
	dest.mon  = (USHORT) (dat >> 21) & 0x0000000F;
	dest.year = (USHORT) ((dat >> 25) & 0x0000007F) + 1972;
	
	return dest;
}

/*
Function         : DataNewRecord
Purpose          : Add a new record
Scope            : All
Input Parameters : dbid
cat - category of the new record
Output Parameters: rec_id - ecord ID of the new record
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_DB_LOCK
ERR_DATA_NO_SPACE
ERR_DATA_INV_PARAM - num_field = 0
Comment          : Record will open
*/
Err DataNewRecord(DatabaseID dbid, BYTE cat, USHORT num_field, RecordID *rec_id)
{
	OpenedDBPtr db_ptr;
	AppID app;
	UWORD size, rec_num;
	USHORT block;
	UWORD i;
	UWORD sblock, offset;
	SortTableLnkPtr sort_table;
	RecordHeader header;
	RTM x;
	UBYTE extra;
	BYTE *ptr;
	
	app = SysGetActiveAppID();
	
	if(num_field == 0)
		return ERR_DATA_INV_PARAM;
	
	/* check if DB opened */
	if(!DataGetDBPtr(dbid, &db_ptr, NULL))
		return ERR_DATA_DB_NOT_OPEN;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	/* check if Open Exlusive */
	if((db_ptr->ex_app != (AppID) 0) && (db_ptr->ex_app != app))
		return ERR_DATA_DB_LOCK;
	
	if(MemoryCheckMemLow())
		return ERR_DATA_NO_MEM;
	
	size = RECHEADER_SIZE + (num_field+1) * sizeof(BYTE); /* field size * num_field + field end marker */
	extra = (UBYTE) size; /* remember old size */
	if (mod(size,4))
		size +=  4 - mod(size,4); /* 32-bit alignment */
	header.size =  size;
	
	extra = (UBYTE) (size - extra);
	
	if(!MemoryGetSpace(&(mat_info[SBlockMat(sblock)]), SBlockBlock(sblock),
		size+6,  FALSE, &block, &offset))  /* size + 6 to sure the first field size is within the same block of the header and there are rooms to place DATA_CHUNK_END*/
		return ERR_DATA_NO_SPACE;
	
	header.rec_id = *rec_id = db_ptr->next_record_id++;
	((DBHeader*) SBlockAddr(sblock))->next_rec_id = *rec_id + 1;
	
	header.total_field = num_field;
	header.cat = cat;
	
	RtcGetTime(&x);
	header.modi_date = _rtm2date32(x);
	
	header.attribute = 0 | extra;
	ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset);
	*(RecordHeader*) ptr = header;
	ptr += RECHEADER_SIZE;  /* position to field 1 size */
	
	while(num_field--)
		*ptr++ = 0x01;	/* all field are zero size */
	
	*ptr = 0x00;		/* end of field */
	/* position to next chunk */
	ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset + header.size);
	*(UWORD*) ptr = DATA_CHUNK_END;
	
	/* update db sort table */
	sort_table = db_ptr->sort_table;
	while(sort_table != NULL)
	{   /* no need to sort, just insert in front */
		DataSortData(sblock, *rec_id, cat, &sort_table, FALSE, NULL);
		sort_table = sort_table->next_table;
	}
	
	if (DataRecIDtoNum(dbid, header.rec_id, &rec_num) != TRUE)
	{
		CAL_CHKSUM
			return ERR_DATA_NO_SPACE;
	}
	CAL_CHKSUM
		return DataOpenRecord(dbid, rec_num, rec_id, NULL);
}

/*
Function         : DataNewRecordWithID
Purpose          : Add a new record
Scope            : All
Input Parameters : dbid
rec_id - record id	
cat - category of the new record
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_DB_LOCK
ERR_DATA_NO_SPACE
ERR_DATA_INV_PARAM - num_field = 0
Comment          : Record will open
*/
Err DataNewRecordWithID(DatabaseID dbid, RecordID rec_id, BYTE cat, USHORT num_field)
{
	OpenedDBPtr db_ptr;
	AppID app;
	UWORD size;
	USHORT block;
	UWORD i;
	UWORD sblock, offset;
	RecordHeader header;
	UBYTE extra;
	RTM x;
	BYTE *ptr;
	
	app = SysGetActiveAppID();
	
	d_num_block = 0xffff;
	
	if(num_field == 0)
		return ERR_DATA_INV_PARAM;
	
	/* check if DB opened */
	if(!DataGetDBPtr(dbid, &db_ptr, NULL))
		return ERR_DATA_DB_NOT_OPEN;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	/* check if Open Exlusive */
	if((db_ptr->ex_app != (AppID) 0) && (db_ptr->ex_app != app))
		return ERR_DATA_DB_LOCK;
	
	size = RECHEADER_SIZE + (num_field+1) * sizeof(BYTE); /* field size * num_field + field end marker */
	extra = (UBYTE) size; /* remember old size */
	if (mod(size,4))
		size +=  4 - mod(size,4); /* 32-bit alignment */
	header.size =  size;
	
	extra = (UBYTE) (size - extra);
	
	if(!MemoryGetSpace(&(mat_info[SBlockMat(sblock)]), SBlockBlock(sblock),
		size+6,  FALSE, &block, &offset))  /* size + 6 to sure the first field size is within the same block of the header and there are rooms to place DATA_CHUNK_END*/
		return ERR_DATA_NO_SPACE;
	
	header.rec_id = rec_id;
	
	RtcGetTime(&x);
	header.modi_date = _rtm2date32(x);
	
	header.total_field = num_field;
	header.cat = cat;
	header.attribute = 0 | extra;
	ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset);
	*(RecordHeader*) ptr = header;
	ptr += RECHEADER_SIZE;  /* position to field 1 size */
	
	while(num_field--)
		*ptr++ = 0x01;	/* all field are zero size */
	
	*ptr = 0x00;		/* end of field */
	/* position to next chunk */
	ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset + header.size);
	*(UWORD*) ptr = DATA_CHUNK_END;
	
	CAL_CHKSUM
		return TRUE;
}


/*
Function         : DataRecordInfo
Purpose          : Get the attribute of the record
Scope            : All
Input Parameters : dbid
rec_id
Output Parameters: rec_size
cat
secret
lock
modi_date
Return	    : TRUE
ERR_DATA_INV_RECID
ERR_DATA_DB_MISS
Comment          :
*/
Err DataRecordInfo(DatabaseID dbid, RecordID rec_id, UWORD *rec_size, UBYTE *cat,
				   Attribute *secret, Attribute *lock, RTM *modi_date)
				   
{
	UWORD i;
	USHORT block;
	RecordHeader *rec_ptr;
	UWORD sblock;
#ifdef xxxxxxxxxxx
	if( rec_id < (UWORD) 0x80000000)
        if((!rec_size) && (!secret) && (!lock) && (!modi_date) && (cat))
        {
			/* if only query the cat, it is in the sort table */
			UWORD rec_num;
			OpenedDBPtr dbinfo_ptr = opendb_table;
			AppLnkPtr app_ptr;
			SortTableLnkPtr sort_table;
			
			if(DataRecIDtoNum(dbid, rec_id, &rec_num) == TRUE)
			{
				if(DataGetDBPtr(dbid, &dbinfo_ptr, &app_ptr) == TRUE)
				{
					sort_table = dbinfo_ptr->sort_table;
					while(sort_table != NULL)
					{
						if(sort_table->sort_field == app_ptr->sort_field)
							break;
						sort_table = sort_table->next_table;
					}
					
					if(!sort_table)  /* should never occur */
					{
						*cat = 0;
						return FALSE;
					}
					
					*cat = sort_table->cat[rec_num];
					
					return TRUE;
				}
			}
        }
#endif
		
		for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
		{
			if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
				break;
		}
		if(i>=TOTAL_SLOT)
			return ERR_DATA_DB_MISS;
		
		dbcache.enable = FALSE;
		
		if((DataRecordBlock(MakeSBlock(i,block), rec_id, &sblock, &rec_ptr))!=TRUE)
			return ERR_DATA_INV_RECID;
		
		
		if(secret)
		{
			if(IsRecSecret(rec_ptr))
				*secret = ATTR_SET;
			else
				*secret = ATTR_CLR;
		}
		if(lock)
		{
			if(IsRecLock(rec_ptr))
				*lock = ATTR_SET;
			else
				*lock = ATTR_CLR;
		}
		if(cat)
			*cat = rec_ptr->cat;
		
		if(modi_date)
			*modi_date = _date322rtm(rec_ptr->modi_date);
		
		if(rec_size)
		{
			*rec_size = rec_ptr->size;
			while(FragRec(rec_ptr))
			{
				if (MemoryNextBlock(&mat_info[SBlockMat(sblock)],
					SBlockBlock(sblock), &block) == TRUE)
				{
					sblock = MakeSBlock(SBlockMat(sblock), block);
					rec_ptr = (RecordHeader *) SBlockAddr(sblock);
					(*rec_size) += rec_ptr->size;
				}
				else
					break;
			}
		}
		
		if(!(NormRec(rec_ptr)) && rec_id < (UWORD) 0x80000000)
			return ERR_DATA_INV_RECID;
		
		return TRUE;
}

				   
/*
Function         : DataSetRecordAttribute
Purpose          : Set the attribute of the record
Scope            : All
Input Parameters : dbid
rec_id
secret
lock
cat - =0xff for unchg.
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_INV_RECID
Comment          :
*/
Err DataSetRecordAttribute(DatabaseID dbid, RecordID rec_id, Attribute secret,
						   Attribute lock, BYTE cat)
{
	UWORD i;
	USHORT block;
	RecordHeader *rec_ptr;
	UWORD sblock;
	OpenedDBPtr db_ptr;
	
	if (DataIsRecordOpen(dbid, rec_id, NULL, NULL) != TRUE)
		return ERR_DATA_REC_NOT_OPEN;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	dbcache.enable = FALSE;
	if((DataRecordBlock(MakeSBlock(i,block), rec_id, &sblock, &rec_ptr))!=TRUE)
		return ERR_DATA_INV_RECID;
	
	switch(secret)
	{
	case ATTR_SET:
		SetRecSecret(rec_ptr);
		break;
	case ATTR_CLR:
		ClrRecSecret(rec_ptr);
		break;
	}
	
	switch(lock)
	{
	case ATTR_SET:
		SetRecLock(rec_ptr);
		break;
	case ATTR_CLR:
		ClrRecLock(rec_ptr);
		break;
	}
	
	if(cat != (BYTE)0xFF)
	{
		if(DataGetDBPtr(dbid,&db_ptr,NULL) != TRUE)
			return FALSE;
		rec_ptr->cat = cat;
		DataUpdateSortTableCat(db_ptr, rec_id, cat);
	}
	
	DataUpdateModiDate(rec_ptr);
	CAL_CHKSUM
		
		return TRUE;
}



/*
Function         : DataOpenRecord
Purpose          : Open a record
Scope            : All
Input Parameters : dbid
rec_num - record number to open
Output Parameters: rec_id - id of the record
Return	    : TRUE
ERR_DATA_NOT_FOUND
ERR_DATA_DB_LOCK
Comment          :
*/
Err DataOpenRecord(DatabaseID dbid, UWORD rec_num, RecordID *rec_id, BYTE *modi)
{
	OpenedDBPtr opendb_ptr;
	RecInfoLnkPtr rec_link;
	SortTableLnkPtr sort_tbl;
	AppInfoLnkPtr appinfo_link;
	AppLnkPtr app_ptr;
	AppID app;
	
	
	if((DataGetDBPtr(dbid,&opendb_ptr,&app_ptr)) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	app = SysGetActiveAppID();
	
	/* chk if db opened exclusive */
	if ((opendb_ptr->ex_app != (AppID) 0) && (opendb_ptr->ex_app != app))
		return ERR_DATA_DB_LOCK;
	
	
	/* found the record's ID */
	sort_tbl = opendb_ptr->sort_table;
	while (sort_tbl != NULL)
	{
		if (sort_tbl->sort_field == app_ptr->sort_field)
			break;		/* found */
		sort_tbl = sort_tbl->next_table;
	}
	if (sort_tbl == NULL)    /* sort table missing */
		return ERR_DATA_DB_NOT_OPEN;
	
	if(rec_num > sort_tbl->total_entry)
		return ERR_DATA_NOT_FOUND;
	if(sort_tbl->rec_id[rec_num] == 0xFFFFFFFF)	/* empty slot in sort table */
		return ERR_DATA_NOT_FOUND;
	
	d_num_block = 0xffff;
	
	dbcache.enable = FALSE;
	
	*rec_id = sort_tbl->rec_id[rec_num];
	
	/* chk if already opened by App, if so, can exit */
	rec_link = opendb_ptr->record;
	/* examine each record */
	while(rec_link != NULL)
	{
		if(rec_link->rec_id == *rec_id)
		{
			appinfo_link = rec_link->app_info;
			/* examine each app that open the record */
			while(appinfo_link != NULL)
			{
				if (appinfo_link->app == app)  /* already open by the app */
				{
					if (modi!=NULL)
						*modi = rec_link->modi;
					return TRUE;
				}
				appinfo_link = appinfo_link->next_app;
			}
			/* this record is opened by other app
			add the app to the link */
			if((appinfo_link = (AppInfoLnkPtr) pmalloc(sizeof(AppInfoLnk))) == NULL)
				return ERR_DATA_NO_MEM;		
			appinfo_link->next_app = rec_link->app_info;;
			appinfo_link->app = app;
			appinfo_link->last_pos = 0;
			appinfo_link->last_field = 0;
			appinfo_link->last_cache_offset = 0xFFFFFFFF;
			appinfo_link->last_cache_block = 0xFFFF;
			rec_link->app_info = appinfo_link;
			if (modi!=NULL)
				*modi= rec_link->modi;
			return TRUE;
		}
		rec_link = rec_link->next_rec;
	}
	/* not opened yet, add the record to db*/
	if ((rec_link = (RecInfoLnkPtr) pmalloc(sizeof(RecInfoLnk))) == NULL)
		return ERR_DATA_NO_MEM;
	rec_link->next_rec = opendb_ptr->record;
	opendb_ptr->record = rec_link;
	rec_link->rec_id = *rec_id;
	rec_link->modi = (BYTE)0;
	if (modi != NULL)
		*modi= rec_link->modi;
	if ((rec_link->app_info = (AppInfoLnkPtr) pmalloc(sizeof(AppInfoLnk))) == NULL)
	{
		opendb_ptr->record = rec_link->next_rec;
		pfree(rec_link);
		return ERR_DATA_NO_MEM;
	}
	rec_link->app_info->next_app = NULL;
	rec_link->app_info->app = app;
	rec_link->app_info->last_pos = 0;
	rec_link->app_info->last_field = 0;
	return TRUE;
	
}


/*
Function         : DataCloseAllDB
Purpose          : Close all opened databases
Scope            : All
Input Parameters : dbid
Output Parameters: None
Return	    :	None
Comment          :
*/
void DataCloseAllDB()
{
	OpenedDBPtr open_db, open_db2;
	AppLnkPtr app_lnk;
	AppLnkPtr app_lnk2;
	SortTableLnk *sort_table, *sort_table2;
	RecInfoLnk *record_lnk, *record_lnk2;
	AppInfoLnk *appinfo_lnk, *appinfo_lnk2;
	RecInfoLnk *recinfo_lnk, *recinfo_lnk2;
	
	/* check if DBOpened */
	open_db = opendb_table;
	
	while(open_db)
	{
		app_lnk = open_db->app_mode;
		
		while (app_lnk)
		{
			app_lnk2 = app_lnk->next_app;
			pfree(app_lnk);
			app_lnk = app_lnk2;
		}
		
		sort_table = open_db->sort_table;
		
		while(sort_table)
		{
			sort_table2 = sort_table->next_table;
			if(sort_table->rec_id)
				pfree(sort_table->rec_id);
			if(sort_table->cat)
				pfree(sort_table->cat);
			pfree(sort_table);
			sort_table = sort_table2;
		}
		
		record_lnk = open_db->record;
		
		while(record_lnk)
		{
			record_lnk2 = record_lnk->next_rec;
			appinfo_lnk = record_lnk->app_info;
			while(appinfo_lnk)
			{
				appinfo_lnk2 = appinfo_lnk->next_app;
				pfree(appinfo_lnk);
				appinfo_lnk = appinfo_lnk2;
			}
			pfree(record_lnk);
			record_lnk = record_lnk2;
		}
		
		if(open_db->cat_lnk)
		{
			if(open_db->cat_lnk->catname)
				pfree(open_db->cat_lnk->catname);
			pfree(open_db->cat_lnk);
		}
		
		open_db2 = open_db->next_db;
		pfree(open_db);
		open_db = open_db2;
	}
	opendb_table = NULL;
	return;
}


/*
Function         : DataCloseDB
Purpose          : Close a database
Scope            : All
Input Parameters : dbid
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
Comment          :
*/
Err DataCloseDB(DatabaseID dbid)
{
	OpenedDBPtr open_db;
	OpenedDBPtr prevopen_db;
	AppLnkPtr app_lnk;
	AppLnkPtr app2_lnk;
	AppLnkPtr prevapp_lnk;
	AppInfoLnkPtr appinfo_lnk;
	AppInfoLnkPtr prevappinfo_lnk;
	/*	SortTableLnkPtr sort_tbl, prevsort_tbl; */
	RecInfoLnkPtr rec_lnk;
	RecInfoLnkPtr prevrec_lnk;
	USHORT sort_field;
	BOOLEAN found;
	AppID app;
	
	/* check if DBOpened */
	open_db = opendb_table;
	prevopen_db = NULL;
	
	
	
    if(DataIsDBOpen(dbid, NULL, NULL) == FALSE)
		return ERR_DATA_DB_NOT_OPEN;   /* db not open */
	
	app = SysGetActiveAppID();
	
	while(open_db != NULL)
	{
		if (open_db->dbid == dbid)
			break;
		else
		{
			prevopen_db = open_db;
			open_db = open_db->next_db;
		}
	}
	if (open_db == NULL)
		return ERR_DATA_DB_NOT_OPEN;
	
	/* check if app open the DB */
	app_lnk = open_db->app_mode;
	app2_lnk = open_db->app_mode;
	prevapp_lnk = NULL;
	while (app_lnk != NULL)
	{
		if (app_lnk->app == app)
			break;
		else
		{
			prevapp_lnk = app_lnk;
			app_lnk = app_lnk->next_app;
			app2_lnk =app_lnk->next_app;
		}
	}
	if (app_lnk == NULL)
		return ERR_DATA_DB_NOT_OPEN;
	
	/* the only app open the DB */
	if ((prevapp_lnk == NULL) && (app_lnk->next_app == NULL))
	{
		/* close all record, sort table ... */
		/* free all sort table */
		/* there are some sort table, clear & relase mem ! */
		/*
		while (open_db->sort_table != NULL)
		{
		sort_tbl = open_db->sort_table;
		prevsort_tbl = NULL;
		while (sort_tbl->next_table != NULL)
		{
		prevsort_tbl = sort_tbl;
		sort_tbl = sort_tbl->next_table;
		}
		DataFreeSortTable(dbid, sort_tbl->sort_field);
		if (prevsort_tbl != NULL)
		prevsort_tbl->next_table = NULL;
		else
		open_db->sort_table = NULL;
		}
		*/
		/* free records */
		while (open_db->record != NULL)
		{
			rec_lnk = open_db->record;
			prevrec_lnk = NULL;
			while (rec_lnk->next_rec != NULL)
			{
				prevrec_lnk = rec_lnk;
				rec_lnk = rec_lnk->next_rec;
			}
			/* free appinfo link under rec link */
			while (rec_lnk->app_info != NULL)
			{
				appinfo_lnk = rec_lnk->app_info;
				prevappinfo_lnk = NULL;
				while (appinfo_lnk->next_app != NULL)
				{
					prevappinfo_lnk = appinfo_lnk;
					appinfo_lnk =appinfo_lnk->next_app;
				}
				
				pfree(appinfo_lnk);
				if (prevappinfo_lnk != NULL)
					prevappinfo_lnk->next_app = NULL;
				else
					rec_lnk->app_info = NULL;
			}
			pfree(rec_lnk);	/* free the record node */
			if (prevrec_lnk != NULL)
				prevrec_lnk->next_rec = NULL;
			else
				open_db->record = NULL;
		}
	}
	else
	{
	/* 1 check sort table
	if only app use it, close it
	2 check all record
	if only app use if, close it
		*/
		sort_field = app_lnk->sort_field;
		/* check any app use same sort table */
		found = FALSE;
		app_lnk = open_db->app_mode;
		while (app_lnk != NULL)
		{
			if((app_lnk->app != app) &&
				(app_lnk->sort_field == sort_field))
			{
				found = TRUE;
				break;
			}
			app_lnk = app_lnk->next_app;
		}
		//                if (found == FALSE)  /* no other app use this sort table */
		//                  DataFreeSortTable(dbid, sort_field);
		/* check for opened record */
		rec_lnk = open_db->record;
		prevrec_lnk = NULL;
		while (rec_lnk != NULL)
		{
			appinfo_lnk = rec_lnk->app_info;
			prevappinfo_lnk = NULL;
			while(appinfo_lnk != NULL)
			{
				if (appinfo_lnk->app == app)
				{
					if (prevappinfo_lnk != NULL)
					{
						prevappinfo_lnk->next_app =
							appinfo_lnk->next_app;
					}
					else
					{
						if (appinfo_lnk->next_app != NULL)
							open_db->record->app_info = appinfo_lnk->next_app;
						else  /* first but last record */
							open_db->record->app_info = NULL;
					}
					
					pfree(appinfo_lnk);
					appinfo_lnk = NULL;
				}
				else
				{
					prevappinfo_lnk = appinfo_lnk;
					appinfo_lnk = appinfo_lnk->next_app;
				}
			}
			if (open_db->record->app_info == NULL) /* no other app use the record */
			{
				if (prevrec_lnk != NULL)
					prevrec_lnk->next_rec = rec_lnk->next_rec;
				else
					open_db->record = NULL;
				pfree(rec_lnk);
				if (open_db->record == NULL)
					rec_lnk = NULL;
				else
					rec_lnk = prevrec_lnk->next_rec;
			}
			else  /* no need to delete this record node */
				rec_lnk = rec_lnk->next_rec;
		}
	   }
	/* applnk */
	if (prevapp_lnk != NULL)
	{
		if(app2_lnk)
			prevapp_lnk->next_app = (app2_lnk)->next_app;
		else
			prevapp_lnk->next_app = NULL;
	}
	else
	{
		if(app2_lnk)
			open_db->app_mode = (app2_lnk)->next_app;
		else
			open_db->app_mode = NULL;
	}
	if(app2_lnk)
		pfree(app2_lnk);
	
	//           if (open_db->app_mode == NULL)  /* the db no longer use */
	/*
	{
	if (prevopen_db != NULL)
				prevopen_db->next_db = open_db->next_db;
				else
				opendb_table = open_db->next_db;
				pfree(open_db);
				}
	*/
	
	return TRUE;
}

/*
Function         : DataOpenNextRecord
Purpose          : Open next record
Scope            : All
Input Parameters : dbid
src_rec - current record id
close_current -  if TRUE, close current record if
function success
Output Parameters: next_rec_id - record id of next record
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_RECID
ERR_DATA_EOR
Comment          :
*/
Err DataOpenNextRecord(DatabaseID dbid, RecordID src_rec, BOOLEAN close_current,
					   RecordID *next_rec_id, BYTE *modi)
{
#ifdef xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx	
	Err result;
	OpenedDBPtr opendb_ptr;
	AppLnkPtr app_ptr;
	SortTableLnkPtr sort_table;
	UWORD i;
	
	if(DataGetDBPtr(dbid, &opendb_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	if (DataGetRecordPtr(dbid, src_rec, &opendb_ptr, NULL, NULL, NULL, NULL) != TRUE)
		return ERR_DATA_REC_NOT_OPEN;
	
	sort_table = opendb_ptr->sort_table;
	while(sort_table != NULL)
	{
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		else
			sort_table = sort_table->next_table;
	}
	
	if(sort_table == NULL)
	{
#ifdef DEBUG
		printf("\nErr: no sort field for app ", app_ptr->app);
#endif
		return ERR_DATA_DB_NOT_OPEN;  /* should never happen */
	}
	
	for(i=0;i<sort_table->total_entry;i++)
	{
		if(sort_table->rec_id[i] == src_rec)
			break;
		if(sort_table->rec_id[i] == 0xFFFFFFFF)  /* end of sort table */
			break;
	}
	
	if(i >= sort_table->total_entry)
		return ERR_DATA_INV_RECID;
	
#ifdef DEBUG
	if(src_rec != sort_table->rec_id[i])  /* should never happen */
		printf("\nErr: record not found in sort table [open_next]");
#endif
	
	if(sort_table->rec_id[i+1] == 0xFFFFFFFF)  /* end of sort table */
		return ERR_DATA_EOR;
	
	*next_rec_id = sort_table->rec_id[i+1];
	
	result = DataOpenRecord(dbid, i+1, next_rec_id, modi);
	if(result != TRUE)	return result;
	
	if(close_current == TRUE)
		DataCloseRecord(dbid, src_rec);		
	return TRUE;
#endif
	return FALSE;	
}


/*
Function         : DataOpenPrevRecord
Purpose          : Open previous record
Scope            : All
Input Parameters : dbid
src_rec - current record id
close_current -  if TRUE, close current record if
function success
Output Parameters: prev_rec_id - record id of previous record
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_RECID
ERR_DATA_BOR
Comment          :
*/
Err DataOpenPrevRecord(DatabaseID dbid, RecordID src_rec, BOOLEAN close_current,
					   RecordID *prev_rec_id, BYTE *modi)
{
#ifdef xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx	
	Err result;
	OpenedDBPtr opendb_ptr;
	AppLnkPtr app_ptr;
	SortTableLnkPtr sort_table;
	UWORD i;
	
	if(DataGetDBPtr(dbid, &opendb_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	if (DataGetRecordPtr(dbid, src_rec, &opendb_ptr, NULL, NULL, NULL, NULL) != TRUE)
		return ERR_DATA_REC_NOT_OPEN;
	
	sort_table = opendb_ptr->sort_table;
	while(sort_table != NULL)
	{
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		else
			sort_table = sort_table->next_table;
	}
	
	if(sort_table == NULL)
	{
#ifdef DEBUG
		printf("\nErr: no sort field for app ", app_ptr->app);
#endif
		return ERR_DATA_DB_NOT_OPEN;  /* should never happen */
	}
	
	for(i=0;i<sort_table->total_entry;i++)
	{
		if(sort_table->rec_id[i] == src_rec)
			break;
		if(sort_table->rec_id[i] == 0xFFFFFFFF)  /* end of sort table */
			break;
	}
	
	if(i >= sort_table->total_entry)
		return ERR_DATA_INV_RECID;
	
#ifdef DEBUG
	if(src_rec != sort_table->rec_id[i])  /* should never happen */
		printf("\nErr: record not found in sort table [open_next]");
#endif
	
	if (i <= 0)
		return ERR_DATA_BOR;
	
	*prev_rec_id = sort_table->rec_id[i-1];
	
	result = DataOpenRecord(dbid, i-1, prev_rec_id, modi);
	if(result != TRUE)	return result;
	
	if(close_current == TRUE)
		DataCloseRecord(dbid, src_rec);
	return TRUE;
#endif
	return FALSE;	
}

/*
Function         : DataCloseRecord
Purpose          : Close a record
Scope            : All
Input Parameters : dbid
rec_id
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
Comment          :
*/
Err DataCloseRecord(DatabaseID dbid, RecordID rec_id)
{
    OpenedDBPtr opendb_ptr;
	RecInfoLnkPtr prevrec_lnk, rec_lnk;
	AppInfoLnkPtr app_lnk, prevapp_lnk;
	AppID app;
	
	
	
	if ((DataGetRecordPtr(dbid, rec_id, &opendb_ptr, &rec_lnk, NULL, NULL, NULL)) != TRUE)
		return ERR_DATA_REC_NOT_OPEN;
	
	app = SysGetActiveAppID();
	
	app_lnk = rec_lnk->app_info;
	
	prevapp_lnk = NULL;
	if (app_lnk != NULL)         /* find app entry in the record */
	{
		while((app_lnk->app != app) && (app_lnk != NULL))
		{
			prevapp_lnk = app_lnk;
			app_lnk = app_lnk->next_app;
		}
	}
	
	
	if (app_lnk != NULL)
	{	/* cut out the link */
		if (prevapp_lnk != NULL)  /* not first node */
			prevapp_lnk->next_app = app_lnk->next_app;
		else
		{
			if (app_lnk != NULL)   /* first node */
				rec_lnk->app_info = app_lnk->next_app;
		}
		pfree(app_lnk);		/* free the node */
	}
	
	
	
	if (rec_lnk->app_info == NULL)	/* last app that use the record */
		/* should close the record */
	{
		rec_lnk = opendb_ptr->record;
		prevrec_lnk = NULL;
		if(rec_lnk != NULL)
		{
			while((rec_lnk->rec_id != rec_id) && (rec_lnk != NULL))
			{
				prevrec_lnk = rec_lnk;
				rec_lnk = rec_lnk->next_rec;
			}
		}
		if (rec_lnk != NULL)
		{
			if (rec_lnk->next_rec != NULL)  /* rec not end of link */
			{
				if(prevrec_lnk)
					prevrec_lnk->next_rec = rec_lnk->next_rec;
				else
					opendb_ptr->record = rec_lnk->next_rec;
			}
			else
			{
				if (prevrec_lnk  != NULL)
					prevrec_lnk->next_rec = NULL; /* end of link */
				else
					opendb_ptr->record = NULL;	/* only link */
			}
			pfree(rec_lnk);		/* free the node */
		}
	}
	
	d_num_block = 0xffff;
	dbcache.enable = FALSE;
	
	return TRUE;
}

/*
Function         : DataDeleteDB
Purpose          : Delete a database
Scope            : All
Input Parameters : dbid
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_MISS
ERR_DATA_DB_BUST
Comment          :
*/
Err DataDeleteDB(DatabaseID dbid)
{
	USHORT i;
	USHORT block, next_block;
	
	
	OpenedDBPtr opendb_ptr;
	
	opendb_ptr = opendb_table;
	
	while(opendb_ptr!=NULL)
	{
		if(opendb_ptr->dbid == dbid)
			return ERR_DATA_DB_BUSY;
        opendb_ptr = opendb_ptr->next_db;
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	/* find last block, then user MemoryRelaseBlock to free all blocks */
	while (MemoryNextBlock(&mat_info[i], block, &next_block) == TRUE)
		block = next_block;
	
	MemoryReleaseBlock(&mat_info[i], block, TRUE, TRUE);
	
	return TRUE;
}

/*
Function         : DataCategoryName
Purpose          : Get a category's name
Scope            : All
Input Parameters : dbid
cat
Output Parameters: name (pre-allocated buffer)
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_DB_RO
ERR_DATA_CAT_NOT_SET;
Comment          :
*/
Err DataCategoryName(DatabaseID dbid, UBYTE cat, BYTE *name)
{
	BYTE *fp = NULL;
	Err result;
	OpenedDBPtr open_ptr;
	WORD i;
	
	if (cat == (UBYTE) 0xFF)  /* reserved */
		return ERR_DATA_INV_PARAM;
	
	if(!DataGetDBPtr(dbid, &open_ptr, NULL))
		return ERR_DATA_DB_NOT_OPEN;
	
	if(!open_ptr->cat_lnk)
		DataBuildCatTable(open_ptr);
	
	if(cat == 0)
	{
		if(!name)
			name = (BYTE*) qmalloc(DATA_CAT_MAX_LEN);
		strcpy((BYTE*)name,SYUNFILED);
		return TRUE;
	}
	
	for(i=0;i<open_ptr->cat_lnk->total_entry;i++)
	{
		if(((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->cat_num == cat)
		{
			if(!name)
				name = (BYTE*) qmalloc(DATA_CAT_MAX_LEN);
			strcpy((BYTE*)name,(BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->name);
			return TRUE;
		}
	}
	
	return ERR_DATA_CAT_NOT_SET;
	
#ifdef DATA_OLD_CAT
	rec_id = MakeCatRecID(cat);
	
	result = DataGetField(dbid, rec_id, 0, &fp, &byte_read);
	if(result != TRUE)
	{	/* category name not set */
		
		
		if(name)	
			name[0] = 0x00;
		return ERR_DATA_CAT_NOT_SET;
	}
	
	
	if(name)
	{
		strcpy(name, fp);
		qfree(fp);
		fp = NULL;
		if(name[0] == 0)
		{              
			return ERR_DATA_CAT_NOT_SET;
		}
	}
	else
        if(fp[0] == 0)
		{
			qfree(fp);
			fp = NULL;
			return ERR_DATA_CAT_NOT_SET;
		}
		
        if(fp)
			qfree(fp);
		return TRUE;
#endif
}



/*
Function         : DataDeleteCategory
Purpose          : Delete a category from category sort table
Scope            : Internal
Input Parameters : opendb_ptr	entry in openeddb_ptr
cat	Category number
Output Parameters: None
Return		     : TRUE		Success
FALSE	Cat not found
Comment          :
*/
BOOLEAN DataDeleteCategory(OpenedDBPtr open_ptr, UBYTE cat)
{
	
	WORD i, j;
	
	if (cat == (UBYTE) 0xFF)  /* reserved */
		return FALSE;
	
	if(!open_ptr->cat_lnk)
		return FALSE;
	
	if(!open_ptr->cat_lnk->total_entry)  /* no entry */
		return FALSE;
	
	for(i=0;i<open_ptr->cat_lnk->total_entry;i++)
	{
		if(((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->cat_num == cat)
		{
			for(j=i;j<open_ptr->cat_lnk->total_entry-1;j++)
			{
				((CatNameLnk*)(open_ptr->cat_lnk->catname + j))->cat_num = ((CatNameLnk*)(open_ptr->cat_lnk->catname + (j+1)))->cat_num;
				strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + j ))->name,(BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + j + 1 ))->name);
			}
			open_ptr->cat_lnk->total_entry--;
			return TRUE;
		}
	}
	return FALSE;
}


/*
Function         : DataAddCategory
Purpose          : Add a category to category sort table
Scope            : Internal
Input Parameters : opendb_ptr	entry in openeddb_ptr
cat	Category number
name	Category Name
Output Parameters: None
Return		     : None
Comment          :
*/
void DataAddCategory(OpenedDBPtr open_ptr, UBYTE cat, BYTE *name)
{
	WORD i, j;
	WORD found = 0;
	BYTE *ptr;
	
	
	if (cat == (UBYTE) 0xFF)  /* reserved */
		return;
	
	if(!open_ptr->cat_lnk)
		return;
	
	if(DataDeleteCategory(open_ptr, cat) == FALSE)
	{
		BYTE *tmptr_dest, *tmptr_src;
		if(name[0] == '\0')  /* remove the cat only */
			return;
		/* need to alloc. memory */
		ptr = (BYTE*) pmalloc(sizeof(CatNameLnk) * (open_ptr->cat_lnk->total_entry + 1));
		if(ptr == NULL)
			return;
		
		if(open_ptr->cat_lnk->catname)
		{
			tmptr_dest = ptr;
			tmptr_src  = (BYTE*)(open_ptr->cat_lnk->catname);
			/* copy old to new */
			for(i=0;i<sizeof(CatNameLnk) * open_ptr->cat_lnk->total_entry;i++)
				*tmptr_dest++ = *tmptr_src++;
			pfree(open_ptr->cat_lnk->catname);
		}
		open_ptr->cat_lnk->catname = (CatNameLnk*)ptr;
	}
	
	if(name == NULL)  /* remove the cat only */
		return;
	
	
	for(i=0;i<open_ptr->cat_lnk->total_entry;i++)  /* found insertion point */
	{
		if(textstrcmp((UBYTE*)name, ((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->name) < 0)
		{
			found = (USHORT)(i+1);
			break;
		}
	}
	
	if(found)
	{
		for(i=open_ptr->cat_lnk->total_entry;i>=found;i--)
		{
			((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->cat_num = ((CatNameLnk*)(open_ptr->cat_lnk->catname + (i-1)))->cat_num;
			strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + i ))->name,(BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + i - 1 ))->name);
		}
		((CatNameLnk*)(open_ptr->cat_lnk->catname + found - 1))->cat_num = cat;
		strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + found - 1))->name, (BYTE*) name);
	}
	else   /* should append at last */
	{			
		((CatNameLnk*)(open_ptr->cat_lnk->catname + open_ptr->cat_lnk->total_entry))->cat_num = cat;
		strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + open_ptr->cat_lnk->total_entry))->name, (BYTE*)name);
	}
	
	open_ptr->cat_lnk->total_entry++;
}


/*
Function         : DataCategorySetName
Purpose          : Set a category's name
Scope            : All
Input Parameters : dbid
cat
name
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_MISS
ERR_DATA_DB_NOT_OPEN
ERR_DATA_DB_RO
Comment          : To delete a category, set name = NULL
*/
Err DataCategorySetName(DatabaseID dbid, UBYTE cat, BYTE *name)
{
	USHORT extra;
	USHORT block;
	UWORD i;
	UWORD sblock, size, offset;
	RecordID rec_id;
	RecordHeader *rec_ptr, header;
	AppLnkPtr app_lnk;
	BYTE *ptr;
	OpenedDBPtr opendb_ptr;
	
	if (cat == (UBYTE) 0xFF)  /* reserved */
		return ERR_DATA_INV_PARAM;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	if (strlen((BYTE*)name) >= CAT_NAME_LEN-1)   /* check cat name length */
		name[CAT_NAME_LEN-1] = 0x00;
	
	if(DataGetDBPtr(dbid,&opendb_ptr,&app_lnk) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	if(app_lnk->open_mode == OPEN_RO) return ERR_DATA_DB_RO;
	
	rec_id = MakeCatRecID(cat);
	
	if(name[0] == '\0')     /* delete the category */
	{
		DataDeleteCategory(opendb_ptr, cat);
		return DataDeleteRecord(dbid, rec_id, FALSE);
	}
	
	dbcache.enable = FALSE;
	
	if(DataRecordBlock(sblock,rec_id, &sblock, &rec_ptr) != TRUE)
	{
		/* not set the cat name yet */
		size = RECHEADER_SIZE + (2 * sizeof(BYTE)); /* field size * num_field + field end marker */
		extra = (USHORT) size; /* remember old size */
		if (mod(size,4))
			size +=  4 - mod(size,4); /* 32-bit alignment */
		header.size =  size;
		extra = (USHORT)(size - extra);
		if(!MemoryGetSpace(&(mat_info[SBlockMat(sblock)]), SBlockBlock(sblock),
			size+6,  FALSE, &block, &offset))  /* size + 6 to sure the first field size is within the same block of the header and there are rooms to place DATA_CHUNK_END*/
			return ERR_DATA_NO_SPACE;	
		
		header.rec_id = rec_id;
		header.total_field = 1;
		header.cat = cat;
		header.attribute = 0 | extra;
		ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset);
		*(RecordHeader*) ptr = header;
		DataUpdateModiDate((RecordHeader*)ptr);
		ptr += RECHEADER_SIZE;  /* position to field 1 size */
		*ptr++ = 0x01;	/* all field are zero size */
		*ptr = 0x00;		/* end of field */
		/* position to next chunk */
		ptr = (BYTE*) (BlockAddr1(mat_info[SBlockMat(sblock)], block) + offset + header.size);
		*(UWORD*) ptr = DATA_CHUNK_END;
		DataAddCategory(opendb_ptr, cat, name);
	}
	else
	{
		DataAddCategory(opendb_ptr, cat, name);
		DataUpdateModiDate(rec_ptr);
	}
	
	return DataWriteField(dbid, rec_id, 0,strlen(name)+1, name);
}


/*
Function         : DataMovCat
Purpose          : Move record from one category to another
Scope            : All
Input Parameters : dbid
src_cat
dest_cat
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_DB_RO
Comment          :
*/
Err DataMoveCat(DatabaseID dbid, UBYTE src_cat, UBYTE dest_cat)
{
	OpenedDBPtr db_ptr;
	AppLnkPtr app_ptr;
	SortTableLnkPtr sort_table;
	USHORT block;
	UWORD sblock;
	RecordHeader *rec_ptr;
	UWORD i;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	if(DataGetDBPtr(dbid,&db_ptr,&app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	if(app_ptr->open_mode == OPEN_RO)
		return ERR_DATA_DB_RO;
	
	sort_table = db_ptr->sort_table;
	if (sort_table == NULL)
	{
#ifdef DEBUG
		printf("\nErr: No sort table for Opened DB ");
#endif
		return FALSE;
	}
	
	for(i=0;i<sort_table->total_entry;i++)
	{
		if(sort_table->rec_id[i] == 0xFFFFFFFF)
			break;
		if(DataRecordBlock(sblock, sort_table->rec_id[i], NULL, &rec_ptr) == TRUE)
			if(((UBYTE) rec_ptr->cat == src_cat) && NormRec(rec_ptr))
			{
				rec_ptr->cat = dest_cat;
				DataUpdateModiDate(rec_ptr);
				
				DataUpdateSortTableCat(db_ptr, sort_table->rec_id[i], rec_ptr->cat);
			}
	}
	
	CAL_CHKSUM
		return TRUE;
}


/*
Function         : DataDeleteCat
Purpose          : Data record of a cateogry
Scope            : All
Input Parameters : dbid
cat
mark_archive
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_BUSY
Comment          :
*/
Err DataDeleteCat(DatabaseID dbid, BYTE cat, BOOLEAN mark_archive)
{
	OpenedDBPtr db_ptr;
	AppLnkPtr app_ptr;
	SortTableLnkPtr sort_table;
	USHORT block;
	UWORD sblock;
	RecordHeader *rec_ptr;
	UWORD i;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	if(DataGetDBPtr(dbid,&db_ptr,&app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	if(app_ptr->open_mode == OPEN_RO)
		return ERR_DATA_DB_RO;
	
	sort_table = db_ptr->sort_table;
	if (sort_table == NULL)
	{
#ifdef DEBUG
		printf("\nErr: No sort table for Opened DB ");
#endif
		return FALSE;
	}
	
	for(i=0;i<sort_table->total_entry;i++)
	{
		if(sort_table->rec_id[i] == 0xFFFFFFFF)
			break;
		if(DataRecordBlock(sblock, sort_table->rec_id[i], NULL, &rec_ptr) == TRUE)
			if(((UBYTE)rec_ptr->cat == cat) && NormRec(rec_ptr))
			{
				DataDeleteRecord(dbid, sort_table->rec_id[i], mark_archive);
				i--;
			}
	}
	return TRUE;
}


/*
Function         : DataDeleteRecord
Purpose          : Delete record
Scope            : All
Input Parameters : dbid
rec_id
archive
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_RECORD_BUSY
ERR_DATA_INV_RECID
ERR_DATA_LOCKED
ERR_DATA_DB_RO
Comment          :
*/
Err DataDeleteRecord(DatabaseID dbid, RecordID rec_id, BOOLEAN archive)
{
	UWORD i, j;
	RecordHeader *rec_ptr;
	OpenedDBPtr db_ptr;
	SortTableLnk *sort_table;
	RecordID *record_id;
	UBYTE *cat_id;
	UWORD sblock;
	USHORT block;	
	UBYTE mode;
	
	/*
	MAT     Nxt 1   Nxt 2  Nxt 0xFFFF
	-0-------1-------2-------3-------
	before 	|aaaaa  |xxxxxxx|xxbbb   |	|
	|aaaaa  |xxxxxxx|xxbbb   |      |
	---------------------------------
	<----Del---->
	
	  after
	  
		MAT     Nxt 2   Nxt0xFFFF Nxt 0xFFFF
		-0-------1-------2-------3-------
		before xx	|aaaaa  |        |bbb   |       |
		|aaaaa  |        |bbb   |       |
	---------------------------------		*/
	
	
	if(rec_id < (UWORD) 0x80000000)
	{
		if(DataIsDBOpen(dbid, &mode, NULL) == FALSE)
			return ERR_DATA_DB_NOT_OPEN;   /* db not open */
		
		if(mode == OPEN_RO)
			return ERR_DATA_DB_RO;	      /* database is open read only */
		
		if(DataGetRecordPtr(dbid, rec_id, &db_ptr, NULL, NULL, NULL, NULL) == TRUE)
			return ERR_DATA_RECORD_BUSY;  /* record in use */
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
	{
		return ERR_DATA_DB_MISS;
	}
	
	dbcache.enable = FALSE;
	if((DataRecordBlock(MakeSBlock(i,block), rec_id, &sblock, &rec_ptr))!=TRUE)
		return ERR_DATA_INV_RECID;
	
	
	if(NormRec(rec_ptr) || (archive == TRUE))
	{
		/* remove form sort table */
		sort_table = db_ptr->sort_table;
		while(sort_table != NULL)
		{
			record_id = sort_table->rec_id;
			cat_id = sort_table->cat;
			for(i=0;i<sort_table->total_entry;i++)
			{
				if (record_id[i] == rec_id)
				{
					for(j=i;j<sort_table->total_entry-1;j++)
					{
						record_id[j] =
							record_id[j+1];
						
						cat_id[j] =
							cat_id[j+1];
					}                                
					record_id[sort_table->total_entry-1] = 0xFFFFFFFF;
					break;
				}
			}
			sort_table = sort_table->next_table;
		}
	}
	
	DataDeleteRecordAction(sblock,rec_ptr, archive);
	return TRUE;
}

Err DataDeleteRecordAction(UWORD sblock, RecordHeader *rec_ptr, BOOLEAN archive)
{
	
	UWORD i;
	USHORT block;
	WORD *src, *dest;
	
	if(archive == TRUE) {
		SetRecArchive(rec_ptr);
		DataUpdateModiDate(rec_ptr);
		if(FragRec(rec_ptr)) {
			// mark archive till last block
			while(rec_ptr->size >= BLOCK_SIZE) {
				MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block);
				sblock = MakeSBlock(SBlockMat(sblock), block);
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
				SetRecArchive(rec_ptr);
				DataUpdateModiDate(rec_ptr);
			}
		}
	} else {
		if(FragRec(rec_ptr)) {
			// free the block till last block
			while(rec_ptr->size >= BLOCK_SIZE) {
				if(MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block) != TRUE)
					printf("\n Err!");
				MemoryReleaseBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), FALSE, TRUE);
				sblock = MakeSBlock(SBlockMat(sblock), block);
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
			}
		}
		// pack the last block
		dest = (WORD *) rec_ptr;
		src = (WORD *) ((UWORD)rec_ptr + rec_ptr->size);
		i = (UWORD)src % BLOCK_SIZE;
		if(i) {
			i = (BLOCK_SIZE - i) >> 2;
			while(i--) *dest++ = *src++;
		} else {
			rec_ptr->size = DATA_CHUNK_END;
		}
		rec_ptr = (RecordHeader *)((UWORD)rec_ptr & 0xfffff000);
		if(rec_ptr->size == DATA_CHUNK_END)
			MemoryReleaseBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), FALSE, TRUE);
	}
	return TRUE;
}


Err DataWriteField2(DatabaseID dbid, RecordID rec_id, USHORT field_num,
					UWORD num_byte, BYTE *buffer)
{
	UWORD sblock, sblockold, fsize, start_sblock, first_offset, last_size;
	USHORT block;
	USHORT num_block;
	UWORD i;
	Err result;
	BOOLEAN frag;
	RecInfoLnkPtr recinfo_ptr;
	WORD byte_diff;
	UWORD acc_size = 0;
	
	BYTE is_del = 0;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	sblock = MakeSBlock(i,block);
	sblockold = sblock;
	
	DataClearFieldCache();
	result = DataFieldInfo2(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
	DataClearFieldCache();
	
	if(result != TRUE)  return result; /* invalid field */
	byte_diff = num_byte - fsize;
	
	if(num_byte > 3000)
	{
		if(byte_diff > 3000)
		{
			result = TRUE;
			acc_size = fsize;
			for(i=0;i<byte_diff/3000; i++)
			{
				acc_size += 3000;
				result = DataResizeField2(dbid, rec_id, field_num, acc_size);
				if (result != TRUE)
				{
					break;
				}
			}
			if(result != TRUE)
			{
				return result;
			}
			result = DataResizeField2(dbid, rec_id, field_num, num_byte);
			if(result != TRUE)
			{
				return result;
			}
		}
		else 
			result = DataResizeField2(dbid, rec_id, field_num, num_byte);
	}
	else
		result = DataResizeField2(dbid, rec_id, field_num, num_byte);
	
	if (result != TRUE)
	{       d_num_block = 0xffff;
	return result;       /* not enough space */
	}
	d_num_block = 0xffff;
	result = DataFieldInfo2(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
#ifdef DEBUG
	if(result != TRUE)
	{
		return result; /* invalid field */
	}
#endif
	
	DataWriteBData(start_sblock, first_offset, num_byte, buffer);
	if(NormRecID(rec_id))  /* not category */
	{
		DataUpdateSortTable(dbid, rec_id, field_num);
		if( DataGetRecordPtr(dbid, rec_id, NULL, &recinfo_ptr, NULL, NULL, NULL) == TRUE)
			recinfo_ptr->modi = recinfo_ptr->modi + 1;		/* increase modi number */
#ifdef DEBUG
		else
			printf("\nErr: Record pointer not found in OpenDBTable when updating field ");
#endif
	}
	
	return TRUE;
}



/*
Function         : DataWriteField
Purpose          : change a field content
Scope            : All
Input Parameters : dbid
rec_id
field_num  - field to update
num_byte   - number of byte to write
buffer - data to write
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_PARAM
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          :
*/

Err DataWriteField(DatabaseID dbid, RecordID rec_id, USHORT field_num, UWORD num_byte, BYTE *buffer)
{
	UBYTE mode;
	UWORD sblock, sblockold, fsize, start_sblock, first_offset, last_size;
	USHORT block;
	USHORT num_block;
	UWORD i;
	Err result;
	BOOLEAN frag;
	RecInfoLnkPtr recinfo_ptr;
	WORD byte_diff;
	UWORD acc_size = 0;
	
	
	//UWORD field_len[30];
	//BYTE *fielddata[30];
	UWORD field_len[80];
	BYTE *fielddata[80];	
	RecordHeader *rec_ptr;
	RecordHeader rec_header;
	BYTE is_del = 0;
	
	if(NormRecID(rec_id))
	{
		if(DataIsDBOpen(dbid, &mode, NULL) == FALSE)
			return ERR_DATA_DB_NOT_OPEN;   /* db not open */
		if (DataIsRecordOpen(dbid, rec_id, NULL, NULL) == FALSE)
			return ERR_DATA_REC_NOT_OPEN;
		if(mode == OPEN_RO)
			return ERR_DATA_DB_RO;	      /* database is open read only */
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	sblock = MakeSBlock(i,block);
	sblockold = sblock;
	
	if(!writable)
		return ERR_DATA_DB_BUSY;
	
    dbcache.valid = FALSE;
    result = DataFieldInfo2(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
	if(result != TRUE)  return result; /* invalid field */
	
	
	d_fsize = fsize;
	d_start_sblock = start_sblock;
	d_first_offset = first_offset;
	d_num_block = num_block;
	d_last_size = last_size;
	d_frag = frag;
	
	
	byte_diff = num_byte - fsize;
	
	if(!writable)
		return ERR_DATA_DB_BUSY;
	
	
	if( (byte_diff < -2500 ) || (byte_diff >= 3000) || (num_byte > 3000))
	{
		is_del = 1;
		
		DataRecordBlock(sblock, rec_id, &sblock, &rec_ptr);
		rec_header = *rec_ptr;
		for(i=0;i<rec_ptr->total_field;i++)
		{
			if(i == field_num)
				continue;
			
			field_len[i] = 0;
			fielddata[i] = NULL;
			DataGetField(dbid, rec_id, i, &fielddata[i], &field_len[i]);
        }
        if(!writable)
			return ERR_DATA_DB_BUSY;
		
		
        DataDeleteRecordAction(sblock, rec_ptr, FALSE);
        DataClearFieldCache();
        DataNewRecordWithID(dbid, rec_id, rec_header.cat, rec_header.total_field);
        for(i=0;i<rec_header.total_field;i++)
        {
			if(i == field_num)
				continue;
			else
			{
				DataWriteField2(dbid, rec_id, i, field_len[i], (BYTE*) fielddata[i]);
				if(fielddata[i])
					qfree(fielddata[i]);
			}
        }
        
		DataWriteField2(dbid, rec_id, field_num, num_byte, (BYTE*) buffer);
	}
	else
	{		
		if(num_byte > 3000)
		{
			if(byte_diff > 1000)
			{
				result = TRUE;
				acc_size = fsize;
				for(i=0;i<byte_diff/1000; i++)
				{
					acc_size += 1000;
					
					result = DataResizeField2(dbid, rec_id, field_num, acc_size);
					if (result != TRUE)
					{
						break;
					}
					d_num_block = 0xffff;
				}
				if(result != TRUE)
				{
					return result;
				}
				
				
				result = DataResizeField2(dbid, rec_id, field_num, num_byte);
				if(result != TRUE)
				{
					return result;
				}
			}
			else 
				result = DataResizeField2(dbid, rec_id, field_num, num_byte);
		}
		else
			result = DataResizeField2(dbid, rec_id, field_num, num_byte);
		
        if (result != TRUE)
        {
			d_num_block = 0xffff;
			return result;       /* not enough space */
        }
        d_num_block = 0xffff;
        result = DataFieldInfo2(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
			&num_block, &last_size, &frag);
		
		
		DataWriteBData(start_sblock, first_offset, num_byte, buffer);
		if(NormRecID(rec_id))  /* not category */
		{
			DataUpdateSortTable(dbid, rec_id, field_num);
			if( DataGetRecordPtr(dbid, rec_id, NULL, &recinfo_ptr, NULL, NULL, NULL) == TRUE)
				recinfo_ptr->modi = recinfo_ptr->modi + 1;		/* increase modi number */
		}
	}
	
	return TRUE;
}

/*
Function         : DataUpdateField
Purpose          : Append data to a field
Scope            : All
Input Parameters : dbid
rec_id
field_num  - field to update
num_byte   - number of byte to write
buffer - data to write
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_PARAM
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          :
*/
Err DataUpdateField(DatabaseID dbid, RecordID rec_id, USHORT field_num,
					UWORD num_byte, BYTE *buffer)
{
	UBYTE mode;
	USHORT block;
	USHORT i, num_block;
	Err result;
	BOOLEAN frag;
	UWORD sblock, fsize, start_sblock, first_offset, last_size, o_fsize;
	UWORD find_sblock, find_offset;
	RecInfoLnkPtr recinfo_ptr;
	
	
	
	if(NormRecID(rec_id))
	{
		if(DataIsDBOpen(dbid, &mode, NULL) == FALSE)
			return ERR_DATA_DB_NOT_OPEN;   /* db not open */
		
		if (DataIsRecordOpen(dbid, rec_id, NULL, NULL) == FALSE)
			return ERR_DATA_REC_NOT_OPEN;
		
		if(mode == OPEN_RO)
			return ERR_DATA_DB_RO;	      /* database is open read only */
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	if(num_byte == 0)
		return TRUE;
	
	
	//       if (MemoryTotalFree() < FREE_MEM_BLOCK_LIMIT + 5 )
	//     {
    //          return ERR_DATA_NO_MEM;
	// }
	
	sblock = MakeSBlock(i,block);
	
	dbcache.valid = TRUE;
	
	if( (d_num_block == 0xffff) )
	{	
		result = DataFieldInfo(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
			&num_block, &last_size, &frag);
		
		if(result != TRUE) 
		{
			dbcache.valid = FALSE;
			return result; /* invalid field */
		}
		
		d_fsize = fsize;
		d_start_sblock = start_sblock;
		d_first_offset = first_offset;
		d_num_block = num_block;
		d_last_size = last_size;
		d_frag = frag;
	}
	else
	{
		fsize = d_fsize;
		start_sblock = d_start_sblock;
		first_offset = d_first_offset;
		num_block = d_num_block;
		last_size = d_last_size;
		frag = d_frag;
	}
	
	o_fsize = fsize;  /* data should append after this */
	
	result = DataResizeField2(dbid, rec_id, field_num, num_byte + fsize);
	if (result != TRUE)
	{
		dbcache.valid = FALSE;
		return result;       /* not enough space */
	}
	
	d_num_block = 0xffff;
	
	result = DataFieldInfo(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
	d_fsize = fsize;
	d_start_sblock = start_sblock;
	d_first_offset = first_offset;
	d_num_block = num_block;
	d_last_size = last_size;
	d_frag = frag;
	
#ifdef DEBUG
	if(result != TRUE)
	{
		printf("\nErr: Field missing after re-size! ");
		dbcache.valid = FALSE;
		return result; /* invalid field */
	}
#endif
	
	/* find the append location */
	/* find num of block after first field block, then find the offset */
	find_sblock = start_sblock;
	if(o_fsize + first_offset < BLOCK_SIZE )  /* in the first block */
		find_offset = first_offset + o_fsize;
	else
	{
		o_fsize -= BLOCK_SIZE - first_offset;
		MemoryNextBlock(&mat_info[SBlockMat(find_sblock)], SBlockBlock(find_sblock), &block);
		find_sblock = MakeSBlock(SBlockMat(find_sblock), block);
		while(o_fsize > (BLOCK_SIZE - RECHEADER_SIZE))
		{
			o_fsize -= (BLOCK_SIZE - RECHEADER_SIZE);
			MemoryNextBlock(&mat_info[SBlockMat(find_sblock)], SBlockBlock(find_sblock), &block);
			find_sblock = MakeSBlock(SBlockMat(find_sblock), block);
		}
		find_offset = o_fsize + RECHEADER_SIZE;
	}
	
	DataWriteBData(find_sblock, find_offset, num_byte, buffer);
	
	if(NormRecID(rec_id))  /* not category */
	{
		DataUpdateSortTable(dbid, rec_id, field_num);
		
		if( DataGetRecordPtr(dbid, rec_id, NULL, &recinfo_ptr, NULL, NULL, NULL) == TRUE)
			recinfo_ptr->modi = recinfo_ptr->modi + 1;		/* increase modi number */
#ifdef DEBUG
		else
			printf("\nErr: Record pointer not found in OpenDBTable when updating field ");
#endif
	}
	dbcache.valid = FALSE;
	return TRUE;
}

/*
Function         : DataGetField
Purpose          : Get entire field content
Scope            : All
Input Parameters : dbid
rec_id
field_num
Output Parameters: buffer
byte_read
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_PARAM
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          :
*/
Err DataGetField(DatabaseID dbid, RecordID rec_id, USHORT field_num,
				 BYTE **buffer, UWORD *byte_read)
{
	UBYTE mode;
	USHORT block;
	USHORT num_block;
	Err result;
	BOOLEAN frag;
	BYTE *buf;
	UWORD i;
	UWORD sblock, fsize, start_sblock, first_offset, last_size;
	
#ifdef DEBUG
    printf("\n------------\nDataGetField");
    printf("  Record ID = %d", rec_id);
#endif
	
    dbcache.enable = FALSE;
	
	if NormRecID(rec_id)  /* the special record is not in opendb_table */
	{
		
		if(DataIsDBOpen(dbid, &mode, NULL) == FALSE)
			return ERR_DATA_DB_NOT_OPEN;   /* db not open */
		
		if (DataIsRecordOpen(dbid, rec_id, NULL, NULL) == FALSE)
			return ERR_DATA_REC_NOT_OPEN;
		
		if(mode == OPEN_RO)
			return ERR_DATA_DB_RO;	      /* database is open read only */
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i,block);
	
    result = DataFieldInfo(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
	
	if(result != TRUE)  return result; /* invalid field */
	
	
#ifdef PR31700
	if(SysGetRealActiveAppID())
	{
		if((buf = (BYTE*) qmalloc(sizeof(BYTE) * fsize))==NULL)
			return ERR_DATA_NO_SPACE;
	}
	else
#endif
	{
		if((buf = (BYTE*) pmalloc(sizeof(BYTE) * fsize))==NULL)
			return ERR_DATA_NO_SPACE;
	}
	DataReadBData(start_sblock, first_offset, fsize, buf, NULL, NULL, NULL);
	*buffer = buf;
	*byte_read = fsize;
	
	
	return TRUE;
}


/*
Function         : DataWriteBData
Purpose          : Write data to a block
Scope            : Internal
Input Parameters : sblock
offset
num_byte
buffer
Output Parameters: None
Return	    : TRUE
Comment          :
*/
Err DataWriteBData(UWORD sblock, UWORD offset, UWORD num_byte, BYTE* buffer)
{
    USHORT block;
    BYTE* dest_addr;
	UWORD i;
	UWORD base_addr;
	UWORD upper_limit;
	
	
#ifdef DEBUG
	if(num_byte > 8192)
		printf("\nDataWriteBData: num_byte > 8192 ");
#endif
	
	base_addr = SBlockAddr(sblock);
	dest_addr = (BYTE*)(base_addr + offset);
	upper_limit = base_addr + BLOCK_SIZE;
	
	for(i=0;i<num_byte;i++)
	{
		if((UWORD)dest_addr >= upper_limit)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(sblock)], (USHORT) sblock, &block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(sblock));
#endif
				CAL_CHKSUM
					return FALSE;
			}
			sblock = MakeSBlock(SBlockMat(sblock), block);
			base_addr = SBlockAddr(sblock);
			dest_addr = (BYTE*)(base_addr + RECHEADER_SIZE) ;
			upper_limit = base_addr + BLOCK_SIZE;
		}
		*(dest_addr++) = buffer[i];
	}
	CAL_CHKSUM
		return TRUE;
}

/*
Function         : DataReadBData
Purpose          : Read data from memory block
Scope            : Internal
Input Parameters : sblock
offset
num_byte
buffer
Output Parameters: read_ptr  - byte after the last byte read
write_ptr - last write poisition
last_sblock - last read block
Return	    : TRUE
Comment          :
*/
Err DataReadBData(UWORD sblock, UWORD offset, UWORD num_byte, BYTE *buffer, BYTE **read_ptr, BYTE **write_ptr, UWORD *last_sblock)
{
	BYTE *dest_addr;
	USHORT block;
	UWORD i;
	UWORD base_addr;
	
	
#ifdef DEBUG
	if(num_byte>8192)
		printf("\nDataReadBData: num_byte > 8192 ");
#endif
	
	base_addr = SBlockAddr(sblock);
	dest_addr = (BYTE*)(base_addr + offset);
	
	for(i=0;i<num_byte;i++)
	{
		if((UWORD)dest_addr >= base_addr + BLOCK_SIZE)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(sblock));
#endif
				CAL_CHKSUM
					return FALSE;
			}
			sblock = MakeSBlock(SBlockMat(sblock), block);
			base_addr = SBlockAddr(sblock);
			dest_addr = (BYTE*)(base_addr + RECHEADER_SIZE) ;
		}
		buffer[i] = *(dest_addr++);
	}
	if(read_ptr)
		*read_ptr = dest_addr;
	if(last_sblock)
		*last_sblock = sblock;
	if(write_ptr)
		*write_ptr= buffer+i;
	CAL_CHKSUM
		return TRUE;
}


Err DataReadBDataWithChk(UWORD sblock, UWORD offset, UWORD num_byte, BYTE *buffer, BYTE **read_ptr, BYTE **write_ptr, UWORD *last_sblock)
{
	BYTE *dest_addr, *buffer_ptr;
	USHORT block;
	UWORD i;
	UWORD base_addr;
	UWORD buffer_base_addr;
	
	USHORT buffer_block;
	USHORT mat_num;
	
	
#ifdef DEBUG
	if(num_byte>8192)
		printf("\nDataReadBData: num_byte > 8192 ");
#endif
	
	base_addr = SBlockAddr(sblock);
	dest_addr = (BYTE*)(base_addr + offset);
	MemoryResolveBlock((UWORD)buffer, &buffer_block, &mat_num);
	buffer_base_addr = (UWORD)buffer & PTR_ALIGIN_MASK;
	buffer_ptr = buffer;
	
	for(i=0;i<num_byte;i++)
	{
		if((UWORD)dest_addr >= base_addr + BLOCK_SIZE)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(sblock));
#endif
				CAL_CHKSUM
					return FALSE;
			}
			sblock = MakeSBlock(SBlockMat(sblock), block);
			base_addr = SBlockAddr(sblock);
			dest_addr = (BYTE*)(base_addr + RECHEADER_SIZE) ;
		}
		
		if((UWORD)buffer_ptr >= buffer_base_addr + BLOCK_SIZE)
		{
			if (MemoryNextBlock(&mat_info[0], buffer_block, &buffer_block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(buffer_block));
#endif
				CAL_CHKSUM
					return FALSE;
			}
			sblock = MakeSBlock(0, buffer_block);
			buffer_base_addr = SBlockAddr(sblock);
			buffer_ptr = (BYTE*)(buffer_base_addr + RECHEADER_SIZE) ;
		}
		*(buffer_ptr++) = *(dest_addr++);
	}
	if(read_ptr)
		*read_ptr = dest_addr;
	if(last_sblock)
		*last_sblock = sblock;
	if(write_ptr)
		*write_ptr= buffer_ptr;
	CAL_CHKSUM
		return TRUE;
}







/*
Function         : DataReadBDataUp
Purpose          : Read data from memory block
Scope            : Internal
Input Parameters : sblock
offset
num_byte
buffer
Output Parameters: read_ptr  - byte after the last byte read
write_ptr - last write poisition
last_sblock - last read block
Return	    : TRUE
Comment          : only for dataresizefield to shift data toward block end, should not accross block boundary
*/
Err DataReadBDataUp(UWORD sblock, UWORD offset, UWORD num_byte, BYTE *buffer, BYTE **read_ptr, BYTE **write_ptr, UWORD *last_sblock)
{
	USHORT block, mat_num;
	BYTE *dest_addr;
	BYTE *src_addr;
	WORD i;
	UWORD sblock1, sblock2, dsblock, tmp;
	
#ifdef DEBUG
	if(num_byte>8192)
		printf("\nDataReadBDataUp: num_byte > 8192");
#endif
	
	tmp = (SBlockAddr(sblock) + offset);
	MemoryResolveBlock( (UWORD) buffer, &block, &mat_num);
	dsblock = MakeSBlock(mat_num, block);
	for(i=num_byte;i>0;i--)
	{
		sblock1 = sblock;
		sblock2 = dsblock;
		src_addr = (BYTE*) tmp;
		dest_addr = buffer;
		_valid_ptr(&sblock1, &src_addr, i-1, NULL);
		_valid_ptr(&sblock2, &dest_addr, i-1, NULL);
		*dest_addr = *src_addr;
	}
	
	if(read_ptr)
	{
		*read_ptr = (BYTE*) tmp;
		_valid_ptr(&sblock, read_ptr, num_byte-1, NULL);
	}
	if(last_sblock)
	{
		if(read_ptr)
			*last_sblock = sblock;
		else
		{
			src_addr = (BYTE*) (sblock + offset);
			_valid_ptr(&sblock, &src_addr, num_byte-1, NULL);
			*last_sblock = sblock;
		}
	}
	if(write_ptr)
	{
		*write_ptr = buffer;
		_valid_ptr(&dsblock, write_ptr, num_byte-1, NULL);
	}
	CAL_CHKSUM   
		return TRUE;
}



/*
Function         : DataCopyBData
Purpose          : Duplicate data to a new block
Scope            : Internal
Input Parameters : sblock  \  src block
offset  /
dsblock \  dest block
doffset /
num_byte  - byte to copy
rec_ptr   - record header structure for new block of same record
Output Parameters: last_read_ptr - last read position
last_write_ptr - last write position
Return	    : TRUE
Comment          : Only used by DataResizeField(), first destination block's record header will not modified
num_byte = 0xFFFFFFFF to copy to end of record
src_ptr & dest_ptr are in same block, src_ptr must > dest_ptr
*/
Err DataCopyBData(UWORD *sblock, UWORD offset, UWORD *dsblock, UWORD doffset, UWORD num_byte, RecordHeader *rec_ptr, BYTE **last_read_ptr, BYTE **last_write_ptr)
{
	USHORT block;
	BYTE *src_ptr, *dest_ptr;
	UWORD i;
	UWORD base_addr, dbase_addr;
	
	
#ifdef DEBUG
	if(num_byte > 8192)
		printf("\nDataCopyBData: num_byte > 8192");
#endif
	
	base_addr = SBlockAddr(*sblock);
	dbase_addr = SBlockAddr(*dsblock);
	src_ptr = (BYTE*)(base_addr + offset);
	dest_ptr = (BYTE*)(dbase_addr + doffset);
	
#ifdef DEBUG
	if((UWORD)dest_ptr + num_byte >= (UWORD) dest_ptr + BLOCK_SIZE)
		printf("adf");
#endif
	
	for(i=0;i<num_byte;i++)
	{
		if((UWORD)src_ptr >= base_addr + BLOCK_SIZE)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(*sblock)], SBlockBlock(*sblock), &block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(*sblock));
#endif
				CAL_CHKSUM
					return FALSE;
			}
			*sblock = MakeSBlock(SBlockMat(*sblock), block);
			base_addr = SBlockAddr(*sblock);
			if(num_byte == 0xFFFFFFFF)  /* copy to end */
			{
				src_ptr = (BYTE*) base_addr;
				if (!FragRec(((RecordHeader*)src_ptr))) /* last fragment */
				{
					i = 0;            /* the last frag to copy */
					num_byte = ((RecordHeader*)src_ptr)->size;
				}
			}
			src_ptr = (BYTE*)(base_addr + RECHEADER_SIZE) ;
		}
		if((UWORD)dest_ptr >= dbase_addr + BLOCK_SIZE)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(*dsblock)], SBlockBlock(*dsblock), &block) != TRUE)
			{
#ifdef DEBUG
				printf("\nErr: Lost link in for block %d", SBlockBlock(*sblock));
#endif
				return FALSE;
			}
			*dsblock = MakeSBlock(SBlockMat(*dsblock), block);
			dbase_addr = SBlockAddr(*dsblock);
			dest_ptr = (BYTE*)(dbase_addr);
			*(RecordHeader*)dest_ptr = *rec_ptr;  /* duplic ate the record header for next block */
			((RecordHeader*)dest_ptr)->size = (num_byte - i) > BLOCK_SIZE ? BLOCK_SIZE:num_byte-i;
			dest_ptr = (BYTE*)(dbase_addr + RECHEADER_SIZE);
		}
		*(dest_ptr++) = *(src_ptr++);
	}
	
	if(last_read_ptr)
		*last_read_ptr = src_ptr;
	if(last_write_ptr)
		*last_write_ptr = dest_ptr;
	CAL_CHKSUM		
		return TRUE;
}

/*
Function         : DataResizeField
Purpose          : Resize a record field
Scope            : All
Input Parameters : dbid
rec_id
field_num
new_size
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_FOUND
ERR_DATA_REC_NOT_FOUND
ERR_DATA_INV_FIELD
ERR_DATA_NO_SPACE
Comment          :
*/
Err DataResizeField(DatabaseID dbid, RecordID rec_id, USHORT field_num,
					UWORD new_size)
{
	return DataResizeField2(dbid, rec_id, field_num,
		new_size);
}

Err DataResizeField2(DatabaseID dbid, RecordID rec_id, USHORT field_num,
					 UWORD new_size)
{
	USHORT i, j, num_block, extra, oextra, ofsize_byte, nfsize_byte, total_block, num_block_b4_field;
	USHORT block, next_block, new_block_need;
	UWORD sblock, start_sblock, nb_sblock, next_sblock, cur_sblock, tmpw, last_sblock, new_sblock, rec_sblock, tmpsblock1, tmpsblock2, test_sblk;
	UWORD fsize, last_size, of_size, nf_size, orec_size, nrec_size;
	WORD  byte_shift;
	UWORD first_offset, en_fsize, size_up_to_field, next_offset;
	UWORD rec_last_sblock;
	RecordHeader *rec_ptr, *last_rec_ptr, *nbrec_ptr;
	BYTE *pfields, *pfieldc;
	BYTE *dest_ptr, *src_ptr, *tmpptr1, *tmpptr2;
	BOOLEAN frag;
	USHORT a, b;
	Err result;
	UWORD last_rec_block_b4_new_block;
	USHORT oldlastblock;
	
	BYTE need_frag = 0;
	RTM x;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	result = DataRecordBlock(sblock, rec_id, &rec_sblock, &rec_ptr);
	if (result != TRUE)  return result; /* invalid field */
	
	if(d_num_block == 0xffff)
	{
		result = DataFieldInfo(sblock, rec_id, field_num, &fsize, &start_sblock,
			&first_offset, &num_block, &last_size, &frag);
	}
	else
	{
		fsize= d_fsize;
		start_sblock = d_start_sblock;
		first_offset = d_first_offset;
		num_block = d_num_block;
		last_size = d_last_size;
		frag = d_frag;
	}
	
	//  add on apr10
	if(rec_sblock != start_sblock)
		need_frag = TRUE;
	//July22
	if(FragRec(rec_ptr))
		need_frag = TRUE;
	
	if (num_block ==0) last_size = 0;
	
	if (result != TRUE)  return result; /* invalid field */
	
	of_size = fsize;
	
	nf_size = new_size;
	
	RtcGetTime(&x);
	rec_ptr->modi_date = _rtm2date32(x);;
	if(new_size == of_size) /* no change in field size */
		return TRUE;
	
	num_block_b4_field = 0;     /* count number of block to reach field block */
	tmpw = 0;
	total_block = 1;
	
	last_rec_ptr = rec_ptr;
	orec_size = last_rec_ptr->size;
	sblock = rec_sblock;
	if(rec_sblock == start_sblock)
		tmpw=1;
	
	if(FragRec(last_rec_ptr)  && (start_sblock != sblock))
		num_block_b4_field = 1;
	
	
	if(FragRec(rec_ptr))  /* cal. record size */
	{
		if( (dbcache.dbid == dbid) &&
			(dbcache.rec_id == rec_id) &&
			(dbcache.valid) &&
			(dbcache.field_num == field_num) )
		{
			
			
			next_block = sblock;
			orec_size = dbcache.rec_size;
			total_block = dbcache.rec_total_block;
			last_sblock = dbcache.rec_last_sblock;
			last_rec_ptr = (RecordHeader*) SBlockAddr(dbcache.rec_last_sblock);
			if(tmpw == 0)  /* count no. of block before field block */
			{
				MemoryNextBlock(&mat_info[0], (USHORT) sblock, &next_block);
				if(start_sblock != sblock)
					num_block_b4_field++;
				else
					tmpw = 1;
			}	  	       			
		}
		else
		{
			UWORD c_block;
			next_block = sblock;
			while(1)
			{
				c_block = next_block;
				if(MemoryNextBlock(&mat_info[SBlockMat(sblock)], (USHORT) sblock, &next_block) != TRUE)
					break;
				sblock = MakeSBlock(SBlockMat(sblock), next_block);
				
				if(((RecordHeader*) SBlockAddr(sblock))->rec_id != last_rec_ptr->rec_id)
				{
					next_block = c_block;
					break;
				}
				
				last_rec_ptr = (RecordHeader*) SBlockAddr(sblock);
				if(tmpw == 0)  /* count no. of block before field block */
				{
					if(start_sblock != sblock)
						num_block_b4_field++;
					else
						tmpw = 1;
				}
				total_block++;
				orec_size += last_rec_ptr->size;
			}
			last_sblock = next_block;
			dbcache.rec_size = orec_size;
			dbcache.rec_last_sblock = last_sblock;
			dbcache.rec_total_block = total_block;
			dbcache.dbid = 0xfffffffe;
		}    
	}
	
	rec_last_sblock = last_sblock;
	
	/* find no. of byte need to represent field size need to change */
	nrec_size = fsize;  /* just a dummy var */
	EnFieldSize(en_fsize, nrec_size, ofsize_byte);
	nrec_size = new_size;
	EnFieldSize(en_fsize, nrec_size, nfsize_byte);
	
	/* position to start address of the field size char */
	pfields = (BYTE*) (SBlockAddr(start_sblock) + first_offset - ofsize_byte);
	pfieldc = pfields;
	sblock = start_sblock;
	oextra = (USHORT) (last_rec_ptr->attribute & 0x03);
	nrec_size = orec_size - oextra; /* exclude extra byte */
	nrec_size -= total_block * RECHEADER_SIZE;  /* exclude header */
	nrec_size += (WORD) ((WORD)new_size - (WORD)fsize) +  /* size of new record after field size chg, exclude extra byte */
		(WORD)((WORD)nfsize_byte - (WORD)ofsize_byte);
	
	nrec_size += (nrec_size / (BLOCK_SIZE - RECHEADER_SIZE) ) * RECHEADER_SIZE;
	if ((nrec_size) % BLOCK_SIZE)
		nrec_size += RECHEADER_SIZE;
	
	extra = (USHORT) nrec_size;  /* dummy, remember old size */
	if (mod(nrec_size,4))       /* 32bit align */
		nrec_size += 4 - mod(nrec_size,4);
	extra = (USHORT)(nrec_size - extra);  /* extra byte in rec */
	
										  /*           | xxxxxxxxxxxx|FS|FData|xxxxxxxxxxxx |
	size up to field --^					*/
	size_up_to_field = num_block_b4_field * (BLOCK_SIZE) + first_offset - ofsize_byte;// -((UWORD)rec_ptr - SBlockAddr(sblock));
	if(rec_ptr == last_rec_ptr)  // the field is in first block
	{
		size_up_to_field -= ((UWORD)rec_ptr - SBlockAddr(sblock));
		//TRACE("\n%d ", orec_size);
	}
	
	//	dbcache.rec_size = nrec_size;
	
	
	
	//add on apr 10, correct? copy form resize field
	//	if(rec_ptr == last_rec_ptr)  // the field is in first block
	//	{
	//		size_up_to_field -= ((UWORD)rec_ptr - SBlockAddr(sblock));
	//		//TRACE("\n%d ", orec_size);
	//	}
	// add end
				oldlastblock = dbcache.rec_last_sblock;
				if(nrec_size > (orec_size - oextra))
				{
					/* chk if free space in the field's block is enough */
					if(((MemoryBlockFreeSpace(&mat_info[SBlockMat(start_sblock)], SBlockBlock(start_sblock)) >=
						(nrec_size - orec_size))) && !FragRec(rec_ptr))
					{	/* free space in this block is enough */
						/* 1. shift data to give space for new record */
						/*
						rec_ptr + oldrec_size +
						
						  old record	|           |mmmmmmmmmm?????|
						  new record	|             |mmmmmmmmm????|
						  
							rec_ptr + newrec_size    +
						<byte to move>              */
						
						if(nrec_size != orec_size)
							MemoryShiftData(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock),
							(UWORD) rec_ptr + orec_size - SBlockAddr(sblock),
							(UWORD) rec_ptr + nrec_size - SBlockAddr(sblock),
							BLOCK_SIZE - ((UWORD) rec_ptr + nrec_size - SBlockAddr(sblock)));
						
						/* 2. shift field data within the record to increase the field size */
						/*
						<       old record size         >
						xx  : old extra
						field_ptr + fsize     +
						rec_ptr + oldrec_size - oextra   +
						<  byte to move >
						
						  |      fffffffmmmmmmmmmmmmmmmmmxx|
						  |      fffffff??????mmmmmmmmmmmmmmmmmy|
						  y : extra
						  field_ptr + new_size        +
						  <           new record size            >
						*/
						
						MemoryShiftData(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock),
							(UWORD) pfieldc + of_size - SBlockAddr(sblock) + ofsize_byte,
							(UWORD) pfieldc + nf_size - SBlockAddr(sblock) + nfsize_byte,
							orec_size - ((UWORD) pfieldc - (UWORD)rec_ptr + of_size) - oextra - ofsize_byte);
						
						if(nfsize_byte != ofsize_byte)
						{
							/* 3. shift field data if no. of byte to represent the field size increase */
							/*
							field_ptr     +
							field_ptr + fsize                +
							|ssffffffffffffffffffff|
							|sssffffffffffffffffffff?????????|
							<   byte to move   >		*/
							
							MemoryShiftData(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock),
								(UWORD) pfieldc - SBlockAddr(sblock),
								(UWORD) pfieldc - SBlockAddr(sblock) + (nfsize_byte - ofsize_byte),
								((UWORD)pfieldc + fsize) - (UWORD) pfieldc);
							
						}
						
						/* 4. write out new size of the field */
						for(i=0;i<nfsize_byte;i++)
							*(pfields++) = *(BYTE*)(((BYTE*)&en_fsize) + i);
						
						rec_ptr->size += (nf_size - of_size + extra - oextra + nfsize_byte - ofsize_byte);
						(BYTE) (rec_ptr->attribute) &= 0xFC; /* mask out the data */
						(BYTE) (rec_ptr->attribute) |= (BYTE) extra;
						dbcache.rec_size = nrec_size;
						CAL_CHKSUM
							return TRUE;
					} /* end or inc. size, free space in this block enough */
					/* ====================================================================================*/
					/* free space not enough in this block */
					if(nrec_size <= BLOCK_SIZE)  /*  put into one block */
					{
						if(MemoryGetSpace(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), nrec_size,
							FALSE, &next_block, &next_offset) != TRUE)
                        {
							return ERR_DATA_NO_SPACE;
                        }
						
						nb_sblock = MakeSBlock(SBlockMat(sblock), next_block);
						cur_sblock = sblock;
						nbrec_ptr = (RecordHeader*) (SBlockAddr(nb_sblock) + next_offset);
						nbrec_ptr->size = nrec_size;
						nbrec_ptr->rec_id = rec_id;
						RtcGetTime(&x);
						nbrec_ptr->modi_date = _rtm2date32(x);
						nbrec_ptr->cat = rec_ptr->cat;
						nbrec_ptr->attribute = rec_ptr->attribute;
						nbrec_ptr->total_field = rec_ptr->total_field;
						ClrRecFrag(nbrec_ptr);
						(BYTE) nbrec_ptr->attribute &= 0xFC; /* mask out the data */
						(BYTE) nbrec_ptr->attribute |= (BYTE) extra;
						dest_ptr =  (BYTE*) nbrec_ptr;
						dest_ptr += RECHEADER_SIZE;	  /* first data byte of the record */
						src_ptr = (BYTE*) rec_ptr;
						src_ptr += RECHEADER_SIZE;
						DataReadBData(sblock,(UWORD) src_ptr - SBlockAddr(cur_sblock), size_up_to_field - RECHEADER_SIZE, dest_ptr, &src_ptr, &dest_ptr, &cur_sblock);
						/* src_ptr now point to the field size byte of the src block */
						for(i=0;i<nfsize_byte;i++)  /* wirte out the size of the field */
							*(dest_ptr++) = *(BYTE*)(((BYTE*)&en_fsize) + i);
						
						_valid_ptr(&cur_sblock, &src_ptr, ofsize_byte, NULL);  /* move src_ptr to field data start, skip fiedl size byte */
						
						DataReadBData(cur_sblock,(UWORD)src_ptr - SBlockAddr(cur_sblock),of_size, dest_ptr, &src_ptr, &dest_ptr, &cur_sblock); /* read origin field data */
						/* src_ptr now at last read pos */
						dest_ptr += (nf_size - of_size);  /* skip new data in field */
						//kenny
						//DataReadBData(cur_sblock,(UWORD)src_ptr - SBlockAddr(cur_sblock), orec_size - size_up_to_field - ofsize_byte, dest_ptr, &src_ptr, NULL, &cur_sblock);
						DataReadBData(cur_sblock,(UWORD)src_ptr - SBlockAddr(cur_sblock), orec_size - size_up_to_field - ofsize_byte - of_size, dest_ptr, &src_ptr, NULL, &cur_sblock);
						/* mark end of record */
						for(i=0;i<4;i++)
							if(( (UWORD)nbrec_ptr + nbrec_ptr->size + i) < ((UWORD) nbrec_ptr + BLOCK_SIZE))
								*(BYTE*)((UWORD)nbrec_ptr + nbrec_ptr->size + i) = 0;
							
							/* delete the origin record */
							DataDeleteRecordAction(sblock,rec_ptr, FALSE);
							dbcache.rec_size = nrec_size;
							return TRUE;
					}     /* not frag, free space not enough in original block, but size < BLOCK_SIZE */
					
					/* size > BLOCK_SIZE  */
					if FragRec(rec_ptr)
					{
						last_sblock = start_sblock;
						
						/* opt here */
						if((dbid == dbcache.dbid) && dbcache.valid)
							last_sblock = dbcache.rec_last_sblock;
						else
							for(i=0;i<num_block;i++)	/* get last frag's block */
							{
								MemoryNextBlock(&mat_info[SBlockMat(last_sblock)], SBlockBlock(last_sblock), &next_block);
								last_sblock = MakeSBlock(SBlockMat(last_sblock), next_block);
							}
							//				TRACE("\nError in RF2");
							last_rec_ptr = (RecordHeader*)SBlockAddr(last_sblock);
							/* last_rec_ptr = offset 0 of last record block */
							
							if(field_num == rec_ptr->total_field - 1)  /* last field */
							{
							/* if there are records after this record,
							if free space in this block enough
							shift them to make space
							if free space not enough
							allocate new blocks
							Update MAT to indicate the block order
							move all records to last new allocated block
							if no records after this record
							allocate new blocks
							Update MAT link to indicate the frag block order
							if byte to represent size increase, shift data to free space for byte to represet the size
							update size
								*/
								/* chk if there are records after */
								if((last_size < BLOCK_SIZE - 1) &&
									((RecordHeader*) (((UWORD)last_rec_ptr)+last_rec_ptr->size))->size != DATA_CHUNK_END)
								{  /* there are records after */
									if((MemoryBlockFreeSpace(&mat_info[SBlockMat(last_sblock)], SBlockBlock(last_sblock))) >
										(nrec_size - orec_size))
									{ /* free space in this block is enough */
										/* shift data down */
										MemoryShiftData(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock),
											last_rec_ptr->size,
											(UWORD) last_rec_ptr->size + nrec_size - orec_size,
											nrec_size - orec_size);
										last_rec_ptr->size += (nrec_size - orec_size);   /* update header size field */
									}  /* record after, free space enough */
									else
									{  /* record after, free space not enough */
									   /* allocate new block
									   |  xxx | last _sblock| xxx |
									   <data>
									   |  xxx | last_sblock | new | new | xxx |
										|<------new data----><data>		*/
										
									}
								}
								else   /* no record after this */
								{
									if(field_num == rec_ptr->total_field - 1)  /* last field */
									{
										new_block_need =  (USHORT)(nrec_size / BLOCK_SIZE - orec_size / BLOCK_SIZE);
										/* insert blocks after last frag */
										if(new_block_need)
										{
											if((MemoryInsertBlock(&mat_info[SBlockMat(last_sblock)], SBlockBlock(last_sblock),new_block_need, &next_block))!=TRUE)
											{
												return ERR_DATA_NO_SPACE;
											}
											
											cur_sblock = last_sblock;
											rec_last_sblock = next_block;
											/* make header for all new blocks */
											for(i=0;i<new_block_need;i++)
											{
												MemoryNextBlock(&mat_info[SBlockMat(last_sblock)], SBlockBlock(cur_sblock), &next_block);
												next_sblock = MakeSBlock(SBlockMat(last_sblock), next_block);
												MemoryClrBlockData(&mat_info[SBlockMat(next_sblock)], SBlockBlock(next_sblock));
												*(RecordHeader*)SBlockAddr(next_sblock) = *(RecordHeader*)SBlockAddr(last_sblock);  /* copy the header */
												SetRecFrag(((RecordHeader*)SBlockAddr(cur_sblock)));  /* mark prev last block frag */
												((RecordHeader*)SBlockAddr(cur_sblock))->size = BLOCK_SIZE;  /* adjust prev last block's size */
												cur_sblock = next_sblock;
											}
										}
										else
											next_sblock = last_sblock;  /* space is enough in orgin last frag block */
										
										
										if ( ((RecordHeader*)(SBlockAddr(next_sblock) + ((RecordHeader*)SBlockAddr(next_sblock))->size))->size == DATA_CHUNK_END)
										{
											src_ptr = (BYTE*) SBlockAddr(next_sblock);
											src_ptr += ((RecordHeader*)SBlockAddr(next_sblock))->size;
											
											while  (((UWORD) src_ptr) < SBlockAddr(next_sblock) + BLOCK_SIZE)
												*src_ptr++ = 0;  /* mark end of record */
										}
										// add end
										
										
										ClrRecFrag(((RecordHeader*)SBlockAddr(next_sblock)));  /* clr new last block frag */
										(BYTE)(((RecordHeader*)SBlockAddr(last_sblock))->attribute) &= 0xFC; /* clr extra byte */
										
										((RecordHeader*)SBlockAddr(next_sblock))->size = nrec_size % BLOCK_SIZE; /* last frag size */
										(BYTE)(((RecordHeader*)SBlockAddr(next_sblock))->attribute) &= 0xFC; /* clr extra byte */
										(BYTE)(((RecordHeader*)SBlockAddr(next_sblock))->attribute) |= extra; /* set extra byte */
										
										tmpw = nfsize_byte - ofsize_byte;
										
										if(nfsize_byte != ofsize_byte) /* size byte increase */
										{
											/* shift data to give space for new size byte */
											/* most likely shift only 1 or 2 bytes only */
											/* data shift start from last allocated block */
											/* 1. copy prev block's last (nrevb - orecb) byte to last block */
											/* 2. shift prev. block (nrevb - orecb) byte, and repeat till field size block*/
											/* 3. only shift data after the field size byte in field size block */
											
											/* copy to last block */
											if(new_block_need)
											{
												MemoryPrevBlock(&mat_info[SBlockMat(next_sblock)], SBlockBlock(next_sblock), &block);
												nb_sblock = MakeSBlock(SBlockMat(next_sblock), block);
												src_ptr = (BYTE*)(SBlockAddr(nb_sblock) + BLOCK_SIZE - tmpw);
												dest_ptr = (BYTE*)(SBlockAddr(next_sblock) + RECHEADER_SIZE);
												for(i=0;i<tmpw;i++)
													*dest_ptr++ = *src_ptr++;
											}
											else
												nb_sblock = last_sblock;
											
											/* shift & copy until field block */
											while(nb_sblock != start_sblock)
											{
												MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), RECHEADER_SIZE, RECHEADER_SIZE + tmpw, BLOCK_SIZE - RECHEADER_SIZE - tmpw);
												dest_ptr = (BYTE*)(SBlockAddr(nb_sblock) + RECHEADER_SIZE);
												MemoryPrevBlock(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), &block);
												nb_sblock = MakeSBlock(SBlockMat(nb_sblock), block);
												src_ptr = (BYTE*)(SBlockAddr(nb_sblock) + BLOCK_SIZE - tmpw);
												for(i=0;i<tmpw;i++)
													*dest_ptr++ = *src_ptr++;
											}
											/* now nb_block = start_block, only shift data after field size byte */
											MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), first_offset, first_offset + tmpw, BLOCK_SIZE - first_offset - tmpw);
										}
										/* since this is the last field, and no record after this field, 
										fill 0's to the data after it to mark end of record*/
										//					TRACE("\n%d ", BLOCK_SIZE);
										//					TRACE(" %08x ", ((RecordHeader*)SBlockAddr(next_sblock))->size);
										//					TRACE(" %08x ", SBlockAddr(next_sblock));
										for(i=0;i<BLOCK_SIZE -((RecordHeader*)SBlockAddr(next_sblock))->size;i++)
										{
											if(i>=sizeof(UWORD))
												break;  // only write 4 x 0's
											
											*(BYTE*)(SBlockAddr(next_sblock) + ((RecordHeader*)SBlockAddr(next_sblock))->size + i) = 0;
										}
										
										/* update field size */
										for(i=0;i<nfsize_byte;i++)
											*(pfields++) = *(BYTE*)(((BYTE*)&en_fsize)+i);
									}
									
			   }
			 }
			 else    /* not last field */
			 {
				 /* find new block need */
				 
				 if(nrec_size > (UWORD)(total_block * BLOCK_SIZE))
					 new_block_need = (USHORT)((nrec_size - ( ((UWORD)total_block) << 12)) / BLOCK_SIZE + 1);
				 else
					 new_block_need = 0;
				 
				 if(new_block_need)
				 {
					 UWORD next_sblock_addr;
					 
					 new_sblock = rec_sblock;	
					 if((dbcache.dbid != dbid) || !dbcache.valid)
					 {
						 for(i=0;i<total_block;i++)
						 {
							 MemoryNextBlock(&mat_info[0], new_sblock, &block);
							 new_sblock = block;
						 }
					 }
					 else
					 {
						 last_rec_block_b4_new_block = dbcache.rec_last_sblock;				  
						 if(MemoryNextBlock(&mat_info[0], dbcache.rec_last_sblock, &block) != TRUE)
							 block = dbcache.rec_last_sblock;
					 }
					 new_sblock = MakeSBlock(SBlockMat(new_sblock), block); 
#ifdef xxxxxx				  
					 printf("\nIn");
					 if((block != deldel))
						 printf("\nNot eq, %d %d %d", block, deldel, dbcache.dbid);
#endif
					 /* insert block after last block of this field */
					 next_sblock_addr = block;
					 if( MemoryInsertBlock(&mat_info[0], block, new_block_need, &block) != TRUE)
					 {
#ifdef DEBUG
						 printf("\nNo enough memory for new block");
#endif
						 return ERR_DATA_NO_SPACE;
					 }
					 test_sblk = dbcache.rec_last_sblock;
					 
					 if(next_sblock_addr == dbcache.rec_last_sblock)
						 dbcache.rec_last_sblock = block;
					 
					 rec_last_sblock = block;
					 next_sblock = MakeSBlock(SBlockMat(new_sblock), block);
					 if( (last_size + (nf_size - of_size) + (nfsize_byte - ofsize_byte)) > 4080)
						 dbcache.field_last_sblock = next_sblock;
					 next_sblock_addr = SBlockAddr(next_sblock);
					 (BYTE)(((RecordHeader*)next_sblock_addr )->attribute) = (BYTE)(((RecordHeader*)SBlockAddr(new_sblock))->attribute);
					 //(BYTE)(((RecordHeader*)SBlockAddr(new_sblock))->attribute) &= 0xFC; /* clr extra byte */
					 ((RecordHeader*)next_sblock_addr )->size = nrec_size  % BLOCK_SIZE; /* last frag size */
					 ((RecordHeader*)next_sblock_addr )->rec_id = rec_id; /* last frag size */
					 (BYTE)(((RecordHeader*)next_sblock_addr )->attribute) &= 0xFC; /* clr extra byte */
					 (BYTE)(((RecordHeader*)next_sblock_addr )->attribute) |= (BYTE) extra; /* set extra byte */
					 ClrRecFrag(((RecordHeader*)next_sblock_addr ));  /* clr new last block frag */
					 
					 next_sblock_addr = SBlockAddr(new_sblock);
					 SetRecFrag(((RecordHeader*)next_sblock_addr ));  /* set last block frag */
					 (BYTE)(((RecordHeader*)next_sblock_addr )->attribute) &= 0xFC; /* clr extra byte */
					 ((RecordHeader*)next_sblock_addr )->size =  BLOCK_SIZE; /* last frag size */
					 dbcache.rec_total_block++;
					 dbcache.dbid = 0xffffffff;
					 
				 }  /* end of new block need */
				 
				 /* shift data after this field */
				 tmpw= new_size - fsize;
				 next_offset = first_offset  +  fsize;
				 if(num_block != 0)
					 next_offset -= RECHEADER_SIZE;  /* next field offset */
				 next_sblock = start_sblock;
				 /* find next field start */
				 if((dbcache.dbid == dbid) && dbcache.valid)
				 {	
					 if(last_size != 4080)
						 //next_sblock = dbcache.rec_last_sblock;
						 next_sblock = oldlastblock;
					 else
					 {
						 //block = dbcache.rec_last_sblock;
						 block = oldlastblock;
						 MemoryNextBlock(&mat_info[0], block, &block);
						 next_sblock = block;
					 }
				 }
				 else
				 {
					 if(new_block_need)
						 next_sblock = test_sblk;
					 else
						 for(i=0;i<next_offset/(BLOCK_SIZE - RECHEADER_SIZE);i++)
						 {
							 MemoryNextBlock(&mat_info[0], next_sblock, &block);
							 next_sblock = block;
						 }
				 }
				 
				 ///// stop here
				 test_sblk = next_sblock;
				 
				 /* find last field end */
				 
#ifdef xxxxxxxxxxxxxxxx
				 next_offset = (orec_size - num_block_b4_field * BLOCK_SIZE  - oextra ) - next_offset;
				 new_sblock = next_sblock;
				 j =  (USHORT)(next_offset / BLOCK_SIZE);
				 
				 //			if ((next_offset) && ((next_offset % BLOCK_SIZE) == 0))			
				 //				j -= 1;
				 for(i=0;i<j;i++)
				 {
					 MemoryNextBlock(&mat_info[0], new_sblock, &block);
					 new_sblock = block;
				 }
				 /*
				 next_offset = ((orec_size - num_block_b4_field * 4080 - ofsize - ofsize_byte) - (4080 - last_size) ) / 4080;
				 new_sblock = next_sblock;
				 j =  (USHORT)(next_offset / BLOCK_SIZE);
				 
				   
					 if ((next_offset) && ((next_offset % BLOCK_SIZE) == 0))			
					 j -= 1;
					 for(i=0;i<j;i++)
					 {
					 MemoryNextBlock(&mat_info[0], new_sblock, &block);
					 new_sblock = block;
					 }
					 
				 */		
				 delme1 = new_sblock;
				 
#endif
				 next_offset = orec_size - num_block_b4_field * BLOCK_SIZE  - oextra + (nfsize_byte - ofsize_byte);
				 new_sblock = start_sblock;
				 j =  (USHORT)(next_offset / BLOCK_SIZE);
				 
				 if ((next_offset) && ((next_offset % BLOCK_SIZE) == 0))			
					 j -= 1;
				 for(i=0;i<j;i++)
				 {
					 MemoryNextBlock(&mat_info[0], new_sblock, &block);
					 new_sblock = block;
				 }
#ifdef xxxxxxxxx			
				 if(new_sblock != delme1)
				 {
					 printf("\nErr %d ", delme1);
					 printf(" %d ", new_sblock);
				 }
#endif
				 
				 byte_shift = orec_size - total_block * RECHEADER_SIZE - oextra - size_up_to_field - ofsize_byte - fsize;
				 byte_shift += (size_up_to_field / BLOCK_SIZE + 1) * RECHEADER_SIZE;
				 
				 if(new_block_need)
				 {
					 UWORD is_chg = 0;
					 UWORD prev_blk = new_sblock;
					 
					 if(num_block == 0)
					 {
						 last_size = first_offset + fsize;
						 src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size);
					 }
					 else
						 src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size + RECHEADER_SIZE) ;
					 
					 //modi here2
					 // 				if(last_sblock != new_sblock)
					 //				  dest_ptr = (BYTE*) (SBlockAddr(new_sblock) + RECHEADER_SIZE);
					 
					 if(last_sblock != new_sblock)
					 {
						 UWORD tmp_b = last_sblock;
						 if(num_block ==0)
							 dest_ptr = (BYTE*) (SBlockAddr(last_sblock));
						 else
							 dest_ptr = (BYTE*) (SBlockAddr(last_sblock) + RECHEADER_SIZE);
						 
						 _valid_ptr(&tmp_b, &dest_ptr, last_size, NULL);
						 _valid_ptr(&tmp_b, &dest_ptr, tmpw, NULL);
					 }
					 
					 else
					 {
						 dest_ptr = (BYTE*) (SBlockAddr(new_sblock) + last_size);
						 if(num_block != 0)
							 dest_ptr +=  RECHEADER_SIZE;
						 _valid_ptr(&new_sblock, &dest_ptr, tmpw, NULL);
					 }
					 
					 if(new_sblock != prev_blk)
						 is_chg = 1;
					 
					 tmpsblock2 = last_sblock;
					 tmpptr2 = dest_ptr;
					 
					 MemoryResolveBlock((UWORD) tmpptr2, &a, &b);
					 tmpsblock2 = MakeSBlock(b, a);
					 _valid_ptr(&tmpsblock2, &tmpptr2, byte_shift-1, NULL);
					 // July22
					 
					 for(i=0;i<RECHEADER_SIZE;i++)  // mark end of record
					 {
						 //					if( ((UWORD)tmpptr2 + 1 ) < (UWORD) ((UWORD)tmpptr2 & (PTR_ALIGIN_MASK)) + BLOCK_SIZE)
						 if( ((UWORD)tmpptr2 + i ) < (UWORD) ((UWORD)tmpptr2 & (PTR_ALIGIN_MASK)) + BLOCK_SIZE)
							 tmpptr2[i+1] = 0;
						 else
							 break;
					 }
					 
					 
					 for(i=(USHORT)byte_shift;i>0 ;i--)
					 {
						 tmpptr1 = src_ptr;
						 tmpptr2 = dest_ptr;
						 MemoryResolveBlock((UWORD) tmpptr1, &a, &b);
						 tmpsblock1 = MakeSBlock(b, a);
						 _valid_ptr(&tmpsblock1, &tmpptr1, i-1, NULL);
						 MemoryResolveBlock((UWORD) tmpptr2, &a, &b);
						 tmpsblock2 = MakeSBlock(b, a);
						 _valid_ptr(&tmpsblock2, &tmpptr2, i-1, NULL);
						 *tmpptr2 = *tmpptr1;
					 }
					 
					 
					 if(is_chg)
					 {			       	
						 new_sblock = last_rec_block_b4_new_block;
					 }
					 else
					 {			
						 MemoryPrevBlock(&mat_info[SBlockMat(new_sblock)], SBlockBlock(new_sblock), &block);
						 new_sblock = MakeSBlock(SBlockMat(new_sblock), block);
					 }		       		       
				 }
				 else
				 {
					 UWORD sblk_addr;
					 if(byte_shift)
					 {
						 if(num_block == 0)
						 {
							 last_size = first_offset + fsize;
							 src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size);
						 }
						 else
							 src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size + RECHEADER_SIZE) ;
						 //				if(num_block==0)
						 //					src_ptr += fsize + ofsize_byte;
						 
						 // modi here
						 //				if(last_sblock != next_sblock)
						 //				  dest_ptr = (BYTE*) (SBlockAddr(next_sblock) + RECHEADER_SIZE);
						 if(last_sblock != next_sblock)
						 {
							 UWORD tmp_b = last_sblock;
							 dest_ptr = (BYTE*) (SBlockAddr(last_sblock));
							 _valid_ptr(&tmp_b, &dest_ptr, last_size, NULL);
							 _valid_ptr(&tmp_b, &dest_ptr, tmpw, NULL);
						 }
						 else
						 {
							 dest_ptr = (BYTE*) (SBlockAddr(next_sblock) + last_size);
							 if(num_block != 0)
								 dest_ptr += RECHEADER_SIZE;
							 _valid_ptr(&next_sblock, &dest_ptr, tmpw, NULL);
						 }
						 
						 //if(num_block==0)
						 //	dest_ptr += fsize + ofsize_byte;
						 
						 
						 tmpsblock2 = last_sblock;
						 tmpptr2 = dest_ptr;
						 
						 MemoryResolveBlock((UWORD) tmpptr2, &a, &b);
						 tmpsblock2 = MakeSBlock(b, a);
						 _valid_ptr(&tmpsblock2, &tmpptr2, byte_shift-1, NULL);
						 
						 //July22
						 for(i=0;i<RECHEADER_SIZE;i++)  // mark end of record
						 {
							 //						if( ((UWORD)tmpptr2 + 1 ) < (UWORD) ((UWORD)tmpptr2 & (PTR_ALIGIN_MASK)) + BLOCK_SIZE)
							 if( ((UWORD)tmpptr2 + i ) < (UWORD) ((UWORD)tmpptr2 & (PTR_ALIGIN_MASK)) + BLOCK_SIZE)
								 tmpptr2[i+1] = 0;
							 else
								 break;
						 }
						 
						 
						 for(i=(USHORT)byte_shift;i>0 ;i--)
						 {
							 USHORT a,b;
							 tmpsblock1 = next_sblock;
							 tmpsblock2 = last_sblock;
							 tmpptr1 = src_ptr;
							 tmpptr2 = dest_ptr;
							 
							 MemoryResolveBlock((UWORD) tmpptr1, &a, &b);
							 tmpsblock1 = MakeSBlock(b, a);
							 _valid_ptr(&tmpsblock1, &tmpptr1, i-1, NULL);
							 MemoryResolveBlock((UWORD) tmpptr2, &a, &b);
							 tmpsblock2 = MakeSBlock(b, a);
							 _valid_ptr(&tmpsblock2, &tmpptr2, i-1, NULL);
							 *tmpptr2 = *tmpptr1;
						 }
					 }
					 sblk_addr = SBlockAddr(new_sblock);
					 (BYTE)(((RecordHeader*)sblk_addr)->attribute) &= 0xFC; /* clr extra byte */
					 if (nrec_size  % BLOCK_SIZE)
						 ((RecordHeader*)sblk_addr)->size = nrec_size  % BLOCK_SIZE; /* last frag size */
					 else
						 ((RecordHeader*)sblk_addr)->size =  BLOCK_SIZE; /* last frag size */
					 (BYTE)(((RecordHeader*)sblk_addr)->attribute) &= 0xFC; /* clr extra byte */
					 (BYTE)(((RecordHeader*)sblk_addr)->attribute) |= extra; /* set extra byte */
				 }
				 
				 next_offset = orec_size - num_block_b4_field * BLOCK_SIZE  - oextra + (nfsize_byte - ofsize_byte);
				 new_sblock = start_sblock;
				 j =  (USHORT)(next_offset / BLOCK_SIZE);
				 if ((next_offset) && ((next_offset % BLOCK_SIZE) == 0))
					 j -= 1;
#ifdef xxxxxxxxxxx
				 for(i=0;i<j;i++)
				 {
					 MemoryNextBlock(&mat_info[0], new_sblock, &block);
					 new_sblock = block;
				 }
#endif			
				 new_sblock = test_sblk;
				 
				 /* if size byte chg, shift the field data */
				 byte_shift = 0;
				 tmpw = nfsize_byte - ofsize_byte;
				 if(tmpw)
				 {
					 if (last_size + tmpw >= BLOCK_SIZE)  /* cannot shift at last block */
					 {
						 MemoryNextBlock(&mat_info[SBlockMat(last_sblock)], SBlockBlock(last_sblock), &next_block);
						 nb_sblock = MakeSBlock(SBlockMat(last_sblock), next_block);
						 next_offset = RECHEADER_SIZE + (last_size + tmpw - BLOCK_SIZE);
						 src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size);
						 dest_ptr = (BYTE*)(SBlockAddr(nb_sblock) + next_offset);
						 for(i=0;i<next_offset - RECHEADER_SIZE;i++)
							 *dest_ptr-- = *src_ptr--;
						 nb_sblock = last_sblock;
						 byte_shift = 1;
					 }
					 else
						 nb_sblock = rec_last_sblock;
					 /* shift & copy until field block */
					 while(nb_sblock != start_sblock)
					 {
						 MemoryShiftData(&mat_info[0], nb_sblock, RECHEADER_SIZE, RECHEADER_SIZE + tmpw, BLOCK_SIZE - RECHEADER_SIZE - tmpw);
						 dest_ptr = (BYTE*)(SBlockAddr(nb_sblock) + RECHEADER_SIZE);
						 MemoryPrevBlock(&mat_info[0], nb_sblock, &block);
						 nb_sblock = block;
						 src_ptr = (BYTE*)(SBlockAddr(nb_sblock) + BLOCK_SIZE - tmpw);
						 for(i=0;i<tmpw;i++)
							 *dest_ptr++ = *src_ptr++;
						 byte_shift = 1;
					 }
					 /* now nb_block = start_block, only shift data after field size byte */
					 //if(byte_shift)
					 MemoryShiftData(&mat_info[0], nb_sblock, first_offset, first_offset + tmpw, BLOCK_SIZE - first_offset - tmpw);
					 //else
					 //MemoryShiftData(&mat_info[0], nb_sblock, first_offset, first_offset + tmpw, BLOCK_SIZE -(first_offset + tmpw));// nrec_size - orec_size);//nf_size - of_size);
					 // MemoryShiftData(&mat_info[0], nb_sblock, first_offset, first_offset + tmpw, fsize);
				 }  /* if field size byte increase */
				 
				 
				 if( (last_size + (nf_size - of_size) + (nfsize_byte - ofsize_byte)) > 4080)	             
					 dbcache.field_last_sblock = dbcache.rec_last_sblock;
				 
				 if((dbcache.dbid == 0xfffffffe) && dbcache.valid)
				 {
					 dbcache.dbid = dbid;
					 dbcache.rec_id = rec_id;
					 dbcache.field_num = field_num;
					 
				 }
				 
				 /* update field size */
				 for(i=0;i<nfsize_byte;i++)
					 *(pfields++) = *(BYTE*)(((BYTE*)&en_fsize)+i);
				 dbcache.rec_size = nrec_size;
				 CAL_CHKSUM
					 return TRUE;
			}
			 }  /* free space in the block not enough, size > BLOCK_SIZE, frag  */
			 else /* free space in the block not enough, size > BLOCK_SIZE, not frag  */
			 {
				 if((MemoryGetSpace(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), nrec_size,
					 FALSE, &next_block, &next_offset)) != TRUE)
				 {
					 return ERR_DATA_NO_SPACE;
				 }
				 
				 /* copy record header to each new allocated block */
				 
				 nb_sblock = MakeSBlock(SBlockMat(sblock), next_block);
				 cur_sblock = sblock;
				 nbrec_ptr = (RecordHeader*) (SBlockAddr(nb_sblock) + next_offset);
				 nbrec_ptr->size = BLOCK_SIZE;
				 nbrec_ptr->rec_id = rec_id;
				 RtcGetTime(&x);
				 nbrec_ptr->modi_date = _rtm2date32(x);
				 nbrec_ptr->cat = rec_ptr->cat;
				 nbrec_ptr->attribute = rec_ptr->attribute;
				 SetRecFrag(nbrec_ptr);   /* set frag flag */
				 (BYTE) (nbrec_ptr)->attribute &= 0xFC; /* extra byte = 0 in first frag */
				 nbrec_ptr->total_field = rec_ptr->total_field;
				 dest_ptr =  (BYTE*) nbrec_ptr;
				 dest_ptr += RECHEADER_SIZE;	  /* first data byte of the record */
				 src_ptr = (BYTE*) rec_ptr;
				 src_ptr += RECHEADER_SIZE;
				 DataReadBData(sblock,(UWORD) src_ptr - SBlockAddr(cur_sblock), size_up_to_field - RECHEADER_SIZE, dest_ptr, &src_ptr, &dest_ptr, &cur_sblock);
				 /* src_ptr now point to the field size byte of the src block */
				 for(i=0;i<nfsize_byte;i++)  /* wirte out the size of the field */
				 {
					 *(dest_ptr) = *(BYTE*)(((BYTE*)&en_fsize) + i);
					 _valid_ptr(&nb_sblock, &dest_ptr, 1, NULL);  /* prevent on BLOCK boundary */
				 }
				 
				 _valid_ptr(&cur_sblock, &src_ptr, ofsize_byte, NULL);  /* move src_ptr to field data start, skip fiedl size byte */
				 
				 DataCopyBData(&cur_sblock,(UWORD)src_ptr - SBlockAddr(cur_sblock), &nb_sblock, (UWORD)dest_ptr - SBlockAddr(nb_sblock), of_size, rec_ptr, &src_ptr, &dest_ptr); /* read origin field data */
				 /* src_ptr now at last read pos */
				 _valid_ptr(&nb_sblock, &dest_ptr, nf_size - of_size, rec_ptr);   /* skip new data in field */
				 
				 DataCopyBData(&cur_sblock, (UWORD)src_ptr - SBlockAddr(cur_sblock), &nb_sblock, (UWORD)dest_ptr - SBlockAddr(nb_sblock) ,orec_size - size_up_to_field - of_size - ofsize_byte - oextra, rec_ptr, NULL, &dest_ptr);
				 
				 _valid_ptr(&nb_sblock, &dest_ptr, extra, NULL);  /* move src_ptr to field data start, skip fiedl size byte */
				 
				 
				 /* mark end of record */
				 for(i=0;i<16;i++)
				 {
					 if((UWORD) dest_ptr < (SBlockAddr(nb_sblock) + BLOCK_SIZE))
						 *(dest_ptr++) = 0x00;
					 else
						 break;
				 }
				 
				 
				 ClrRecFrag(((RecordHeader *)(((UWORD)dest_ptr) & PTR_ALIGIN_MASK)));
				 dest_ptr = (BYTE*) (((UWORD)dest_ptr) & PTR_ALIGIN_MASK);
				 if(nrec_size % BLOCK_SIZE)
					 ((RecordHeader *)dest_ptr)->size = nrec_size % BLOCK_SIZE;//? + extra;
				 else
					 ((RecordHeader *)dest_ptr)->size = BLOCK_SIZE;
				 (BYTE) ((RecordHeader *)dest_ptr)->attribute &= 0xFC; /* extra byte = 0 in first frag */
				 (BYTE) ((RecordHeader *)dest_ptr)->attribute |= (BYTE) extra;
				 
				 /* delete the origin record */
				 dbcache.rec_size = nrec_size;
				 DataDeleteRecordAction(sblock,rec_ptr, FALSE);
				 return TRUE;
				 
			 } /* free space in the block not enough, size > BLOCK_SIZE, not frag  */
			 
	}
	else
	{  /* nrec_size <= orec_size */
		if (ofsize_byte >= nfsize_byte)
		{
			byte_shift = (ofsize_byte - nfsize_byte);
			cur_sblock = start_sblock;
			nb_sblock = start_sblock;
			if(byte_shift)
			{  /* field size byte reduce */
				//		for(i=0;i<new_size;i++)
				/* shift data in first block */
				DataCopyBData(&cur_sblock, first_offset, &nb_sblock, first_offset - byte_shift, new_size, rec_ptr, NULL, NULL);
			}
		}
		else
			byte_shift = nfsize_byte - ofsize_byte;
		
		/* copy rest of the data to the block */
		/*  old  |            fffDDDDDDDDDDDDDD|
		new  |            ffDDDDDDDDDDDDDDx|
		^-----------src_ptr
		^------------dest_ptr */
		src_ptr = pfields + ofsize_byte;
		dest_ptr = pfields + nfsize_byte;
		new_sblock = nb_sblock = start_sblock;
		
		if(nfsize_byte > ofsize_byte)	/* size byte change */
		{
			byte_shift = 0;
			tmpw = nfsize_byte - ofsize_byte;
			if(tmpw)
			{
				if (last_size + tmpw >= BLOCK_SIZE)  /* cannot shift at last block */
				{
					MemoryNextBlock(&mat_info[SBlockMat(last_sblock)], SBlockBlock(last_sblock), &next_block);
					nb_sblock = MakeSBlock(SBlockMat(last_sblock), next_block);
					next_offset = RECHEADER_SIZE + (last_size + tmpw - BLOCK_SIZE);
					src_ptr = (BYTE*) (SBlockAddr(last_sblock) + last_size);
					dest_ptr = (BYTE*)(SBlockAddr(nb_sblock) + next_offset);
					for(i=0;i<next_offset - RECHEADER_SIZE;i++)
						*dest_ptr-- = *src_ptr--;
					nb_sblock = last_sblock;
					byte_shift = 1;
				}
				else
					nb_sblock = last_sblock;
				/* shift & copy until field block */
				while(nb_sblock != start_sblock)
				{
					MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), RECHEADER_SIZE, RECHEADER_SIZE + tmpw, BLOCK_SIZE - RECHEADER_SIZE - tmpw);
					dest_ptr = (BYTE*)(SBlockAddr(nb_sblock) + RECHEADER_SIZE);
					MemoryPrevBlock(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), &block);
					nb_sblock = MakeSBlock(SBlockMat(nb_sblock), block);
					src_ptr = (BYTE*)(SBlockAddr(nb_sblock) + BLOCK_SIZE - tmpw);
					for(i=0;i<tmpw;i++)
						*dest_ptr++ = *src_ptr++;
					byte_shift = 1;
				}
				/* now nb_block = start_block, only shift data after field size byte */
				//if(byte_shift)
				MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), first_offset, first_offset + tmpw, BLOCK_SIZE - first_offset - tmpw);
				//else
				//MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock), first_offset, first_offset + tmpw, fsize);
			}  /* if field size byte increase */
		}
		
		/* skip the field */
		_valid_ptr(&nb_sblock, &src_ptr, fsize, NULL);
		_valid_ptr(&new_sblock, &dest_ptr, new_size, NULL);
		
		if(new_size < fsize)  /* set dest_ptr to last write pos */
		{
			BYTE is_mul = 0;
			DataReadBDataWithChk(nb_sblock, (UWORD)src_ptr - SBlockAddr(nb_sblock)
				, orec_size - size_up_to_field - ofsize_byte - fsize - oextra -  num_block * RECHEADER_SIZE, dest_ptr, &src_ptr, &dest_ptr, &nb_sblock);
			/* release emptied block during field size reduce */
			MemoryResolveBlock((UWORD)dest_ptr, &a, &block);
			/* nb_sblock = last block of the old record */
			block = a;  /* a is the last block of the new record */
			while(block != nb_sblock)
			{
				MemoryNextBlock(&mat_info[0], block, &block);
				if(block != nb_sblock)
					MemoryReleaseBlock(&mat_info[0], block, FALSE, TRUE);
				is_mul = 1;
			}
			/* update last block's size */
			if(is_mul)
				((RecordHeader*)((UWORD)dest_ptr & PTR_ALIGIN_MASK))->size = ((UWORD)dest_ptr) & ~PTR_ALIGIN_MASK;
			
		}
		else
		{
			/* shift data after this field */
			tmpw= new_size - fsize;
			next_offset = first_offset  +  fsize - RECHEADER_SIZE;  /* next field offset */
			next_sblock = start_sblock;
			/* find next field start */
			for(i=0;i<next_offset/(BLOCK_SIZE - RECHEADER_SIZE);i++)
			{
				MemoryNextBlock(&mat_info[SBlockMat(next_sblock)], SBlockBlock(next_sblock), &block);
				next_sblock = MakeSBlock(SBlockMat(next_sblock), block);
			}
			/* find last field end */
			next_offset = orec_size - num_block_b4_field * BLOCK_SIZE  - oextra + (nfsize_byte - ofsize_byte);
			new_sblock = start_sblock;
			j =  (USHORT)(next_offset / BLOCK_SIZE);
			if ((next_offset) && ((next_offset % BLOCK_SIZE) == 0))
				j -= 1;
			for(i=0;i<j;i++)
			{
				MemoryNextBlock(&mat_info[SBlockMat(new_sblock)], SBlockBlock(new_sblock), &block);
				new_sblock = MakeSBlock(SBlockMat(new_sblock), block);
			}
			
			byte_shift = orec_size - size_up_to_field - ofsize_byte - fsize - oextra -  (num_block ) * RECHEADER_SIZE;
			if(byte_shift)
			{
				if(FragRec(rec_ptr))
				{
					tmpsblock1 = start_sblock;
					src_ptr = (BYTE*) ((SBlockAddr(start_sblock)) + first_offset);
					_valid_ptr(&tmpsblock1, &src_ptr, fsize + (nfsize_byte - ofsize_byte), NULL);
					if(num_block == 0)
					{
						last_size = first_offset + fsize - RECHEADER_SIZE;
						last_sblock = start_sblock;
					}
					if(last_sblock != next_sblock)
						dest_ptr = (BYTE*) (SBlockAddr(next_sblock) + RECHEADER_SIZE + tmpw);
					else
						dest_ptr = (BYTE*) (SBlockAddr(next_sblock) + last_size + tmpw + (nfsize_byte - ofsize_byte) + RECHEADER_SIZE);
				}
				else
				{
					src_ptr = (BYTE*) (SBlockAddr(start_sblock) + first_offset + fsize);
					dest_ptr = src_ptr + tmpw;
				}
				if (num_block ==0)
					last_sblock = start_sblock;
				for(i=(USHORT)byte_shift;i>0 ;i--)
				{
					tmpptr1 = src_ptr;
					tmpptr2 = dest_ptr;
					MemoryResolveBlock((UWORD) tmpptr1, &a, &b);
					tmpsblock1 = MakeSBlock(b, a);
					_valid_ptr(&tmpsblock1, &tmpptr1, i-1, NULL);
					MemoryResolveBlock((UWORD) tmpptr2, &a, &b);
					tmpsblock2 = MakeSBlock(b, a);
					_valid_ptr(&tmpsblock2, &tmpptr2, i-1, NULL);
					*tmpptr2 = *tmpptr1;
				}
				tmpsblock2 = last_sblock;  /* pos. dest_ptr to last write pos */
				_valid_ptr(&tmpsblock2, &dest_ptr, byte_shift-1, NULL);
			}
	       }
		
		if(((UWORD)dest_ptr % BLOCK_SIZE) < RECHEADER_SIZE)  /* this will happen if the  size multi of BLOCK_SIZE, the dest_ptr will jump to another page, i.e. 1FFF, after write, dest = 2000, jump to another page, but it isn't */
			dest_ptr -= 1;
		
		/* shift data in last block */
		/* last block |ooooooooooooDDDDDDDDDDDDDDDDD|
		^------src_ptr, nb_sblock  */
		if (new_size < fsize)
		{
			if((((UWORD)dest_ptr) & PTR_ALIGIN_MASK) ==(((UWORD)src_ptr) & PTR_ALIGIN_MASK))
			{ /* data after the record is on the same block after shift,
			  |DDDDDDDD??????RRRRRRRRRRR|
				^------move RRRRR to here */
				UWORD is_rec_after = TRUE;
				if(((RecordHeader*) ((UWORD) src_ptr + oextra))->size == 0)
					is_rec_after = FALSE;
				
				MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock),
					(UWORD) src_ptr - SBlockAddr(nb_sblock) + oextra,
					(UWORD) dest_ptr - SBlockAddr(nb_sblock) + extra, BLOCK_SIZE - ((UWORD)src_ptr - SBlockAddr(nb_sblock)));
				if(is_rec_after == FALSE)
					((RecordHeader*) ((UWORD) src_ptr + oextra))->size = 0;
			}
			else
			{  /* some blocks are drop after resize,
			   |DDDDD|????|???RRR|
			   ^------move RRR to here
				^---drop this block  */
				MemoryShiftData(&mat_info[SBlockMat(nb_sblock)], SBlockBlock(nb_sblock),
					(UWORD) src_ptr - SBlockAddr(nb_sblock),
					0, BLOCK_SIZE - ((UWORD)src_ptr - SBlockAddr(nb_sblock)));
				
				/* mark end of rec */
				for(i=0;i<4;i++)
					if ( (((UWORD)(dest_ptr + i)) & ~PTR_ALIGIN_MASK) < BLOCK_SIZE)
						*(dest_ptr + i) = 0;
					
					/* release the dropped block */
					if(((RecordHeader*)SBlockAddr(nb_sblock))->size == 0)
						MemoryReleaseBlock(&mat_info[0], nb_sblock, TRUE, TRUE);			 
			}
		}
		/* mark end of record */
		if(new_size < fsize)
			for(i=0;i<byte_shift;i++)
				*(BYTE*) (SBlockAddr(nb_sblock) + BLOCK_SIZE + i - byte_shift - 1) = 0x00;
			
			/* drop out all the released block */
			// src_ptr is in last block of the old rec
			// dest_ptr is in last block of new rec
			// sholud del block between them
			
			/* update field size */
			nb_sblock = start_sblock;
			for(i=0;i<nfsize_byte;i++)  /* wirte out the size of the field */
			{
				*(pfields) = *(BYTE*)(((BYTE*)&en_fsize) + i);
				_valid_ptr(&nb_sblock, &pfields, 1, NULL);  /* prevent on BLOCK boundary */
			}
			RtcGetTime(&x);
			rec_ptr->modi_date = _rtm2date32(x);
			if(nrec_size <= BLOCK_SIZE)    /* only one block */
				dest_ptr = (BYTE*) rec_ptr;
			else
				dest_ptr = (BYTE*) (((UWORD)dest_ptr) & PTR_ALIGIN_MASK);
			/* update last block */
			if(nrec_size % BLOCK_SIZE != 0)
				((RecordHeader *)dest_ptr)->size = nrec_size % BLOCK_SIZE;//? + extra;
			else
			{  /* nrec_size = 0 or nrec_size = BLOCK_SIZE */
				if(nrec_size)
					((RecordHeader *)dest_ptr)->size =  BLOCK_SIZE;
				else
					((RecordHeader *)dest_ptr)->size = 0;
			}
			
			
			if(nrec_size > BLOCK_SIZE)
			{
				if(need_frag)
					SetRecFrag(rec_ptr);   /* set frag flag */
				else
					ClrRecFrag(rec_ptr);
			}
			else
				ClrRecFrag(rec_ptr);
			
			(BYTE) (rec_ptr)->attribute &= 0xFC; /* extra byte = 0 in first frag */
			(BYTE) ((RecordHeader *)dest_ptr)->attribute &= 0xFC; /* extra byte = 0 in first frag */
			(BYTE) ((RecordHeader *)dest_ptr)->attribute |= (BYTE) extra;
	}
	dbcache.dbid = 0xffffffff;
	dbcache.rec_size = nrec_size;
	CAL_CHKSUM
		return TRUE;
}

/*
Function         : DataNewDB
Purpose          : Create a new database
Scope            : All
Input Parameters : name - database name
version - database version
owner_info - owner info.
Output Parameters: dbid - database id of new database
Return	    : TRUE
ERR_DATA_DB_EXIST
ERR_DATA_NO_SPACE
Comment          :
*/
Err DataNewDB(BYTE *name, USHORT version, BYTE *owner_info, DatabaseID *dbid)
{
	USHORT block;
	USHORT i;
	DBHeader header;
	BYTE *ptr;
	RTM x;
	
	if(DataFindDB(name, dbid) == TRUE)
		return ERR_DATA_DB_EXIST;    /* db with same name exist */
	
	if(MemoryCheckMemLow())
		return ERR_DATA_NO_MEM;
	
	*dbid = DataNewDBID();		     /* get dbid */
	
	for(i=0;i<TOTAL_SLOT;i++)
		if((MemoryNewBlock(&mat_info[i], 1, *dbid, MAT_BLOCK_DB_REC, (USHORT) 0xFFFF, &block))==TRUE)
			break;
		if (i >= TOTAL_SLOT)
			return ERR_DATA_NO_SPACE;
		
        if(strlen(owner_info) > DB_MAX_INFO_LEN - 2)
			owner_info[DB_MAX_INFO_LEN-1] = 0x00;
        header.length = DBHEADER_SIZE + strlen(name) + 1 + strlen(owner_info) + 1;
		header.dbid = *dbid;
		header.version = version;
		header.owner = SysGetActiveAppID();
		header.next_rec_id = 1000;
		RtcGetTime(&x);
		header.create_date = _rtm2date32(x);
		*(UWORD*) &header.backup_date = 0;
		if (mod(header.length,4))   /* 32-bit alignment */
			header.length += 4 - mod(header.length,4);
		
		ptr = (BYTE*) BlockAddr1(mat_info[i], block);
		*(DBHeader *)ptr = header;
		ptr += DBHEADER_SIZE;
		strcpy(ptr, name);
        ptr += strlen(name)+1;  /* the NULL character is not in strlen */
		strcpy(ptr, owner_info);
		ptr = (BYTE*) (BlockAddr1(mat_info[i], block) + header.length);  /* position to first record */
		
        for(i=0;i<4;i++)
			*((UWORD*)ptr + i) = DATA_CHUNK_END;    /* mark end of record */
        CAL_CHKSUM
			return TRUE;
}

/*
Function         : DataFindDB
Purpose          : Find a database
Scope            : All
Input Parameters : name - database name
Output Parameters: dbid - database id of the database
Return	    : TRUE
FALSE
Comment          :
*/
BOOLEAN DataFindDB(BYTE *name, DatabaseID *dbid)
{                     
	USHORT i,j;
	
	for(j=0;j<TOTAL_SLOT;j++)
		for(i=0;i<mat_info[j].total_entry;i++)
		{	/* find all database header */
			if((BlockType(mat_info[j].mat[i].attribute) == MAT_BLOCK_DB_REC))
			{
				if (!strcmp(name, (BYTE*)BlockAddr1(mat_info[j], i)+DBHEADER_SIZE))  /* equal */
				{
					*dbid = mat_info[j].mat[i].id;
					return TRUE;
				}
			}
		}
		
		return FALSE;
}

/*
Function         : DataOpenDB
Purpose          : Open a database
Scope            : All
Input Parameters : dbid
sort_field
open_mode
Output Parameters: dbid - database id of the database
Return	    : TRUE
ERR_DATA_DB_MISS
ERR_DATA_INV_PARAM - sort_field not exist
ERR_DATA_DB_LOCK
ERR_DATA_DB_BUSY   - open_mode = OPEN_EX but has app using that app
ERR_DATA_NO_MEM
ERR_DATA_INV_FIELD - invalid field
Comment          :
*/
Err DataOpenDB(DatabaseID dbid, USHORT sort_field, DBOpenMode open_mode)
{
	Err result;
	OpenedDBPtr open_db;
	AppLnkPtr app_mode, app_tmp;
	USHORT i;
	USHORT total_open, block;
	AppID app;
	SortTableLnkPtr sort_tbl;
	SortTableLnkPtr sort_tbl_tmp;
	UWORD sblock;
	
	
	/* check if DB Opened */
	if ((open_mode != OPEN_RO) && (open_mode != OPEN_RW) &&
		(open_mode != OPEN_EX))
		return ERR_MEM_INV_PARAM;
	
	/* chk if DB exist and find block location */
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	sblock = MakeSBlock(i, block);
	
	app = SysGetActiveAppID();
	
	dbcache.valid = FALSE;
	open_db = opendb_table;
	while (open_db != NULL)
	{
		if (open_db->dbid == dbid)
		{
			/* chk if exclusive */
			if ((open_db->ex_app != 0) && (open_db->ex_app != app))
			{
				result = ERR_DATA_DB_LOCK;
				return result;
			}
			if(open_mode == OPEN_EX)  /* if open in ex., this db should not open by other app */
				if(open_db->ex_app != app)
				{
					total_open = 0;   /* count total app open this db */
					app_tmp = open_db->app_mode;
					while(app_tmp != NULL)
					{
						if(app_tmp->app != app) /* exclude app itself */
							total_open++;
						app_tmp = app_tmp->next_app;
					}
					if(!total_open)
						return ERR_DATA_DB_BUSY;
				}
				/* check if this DB already opened by this app */
				app_mode = open_db->app_mode;
				while (app_mode != NULL)
				{
					if(app_mode->app == app)
					{
						app_mode->open_mode = open_mode;
						/* chk if sort field change */
						if (app_mode->sort_field != sort_field)
						{
							total_open = 0;
							app_tmp = open_db->app_mode;  /* check if any more app use the sort field */
							while(app_tmp != NULL)
							{
								if(app_tmp->app != app) /* exclude app itself */
								{
									total_open++;
									break;
								}
								app_tmp = app_tmp->next_app;
							}
							if(!total_open)  /* no more app use that field */
								DataFreeSortTable(dbid, app_mode->sort_field);
							
							app_mode->sort_field = sort_field;
							sort_tbl_tmp = open_db->sort_table;
							/* need a new sort table ? */
							while(sort_tbl_tmp != NULL)
							{
								if (sort_tbl_tmp->sort_field == sort_field)
									return TRUE;
								sort_tbl_tmp = sort_tbl_tmp->next_table;
							}
							/* sort table not found, build a new one */
							result = DataBuildSortTable(sblock, sort_field, &sort_tbl, NULL);
							if(result != TRUE)
								return result;  /* invalid field or no mem */
							sort_tbl->next_table = open_db->sort_table;
							open_db->sort_table = sort_tbl;
							
							return TRUE;
						}
						else
							return TRUE;
					}  /* if = app */
					app_mode = app_mode->next_app;
				}	/* while */
				/* DB is opened by other app, append this app to the DB */
				if ((app_mode = (AppLnkPtr) pmalloc(sizeof(AppLnk))) == NULL)
					return ERR_DATA_NO_SPACE;
				app_mode->app = app;
				app_mode->sort_field = sort_field;
				app_mode->open_mode = open_mode;
				app_mode->next_app = open_db->app_mode;
				open_db->app_mode = app_mode;
				if (open_mode==OPEN_EX)
					open_db->ex_app = app;
				/* chk if sort tbl exist */
				sort_tbl_tmp = open_db->sort_table;
				while(sort_tbl_tmp != NULL)
					if (sort_tbl_tmp->sort_field == sort_field)
						return TRUE;
					else
						sort_tbl_tmp = sort_tbl_tmp->next_table;
					
					/* sort table not found, build a new one */
					result = DataBuildSortTable(sblock, sort_field, &sort_tbl, NULL);
					if(result != TRUE)
						return result;  /* invalid field or no mem */
					sort_tbl->next_table = open_db->sort_table;
					open_db->sort_table = sort_tbl;
					return TRUE;
					
		}  /* end of if DBID==DBID */
		open_db = open_db->next_db;
	}  /* end of while DB */
	/* DB exist but not opened, open an new entry in opendb_table for it */
	if ((open_db = (OpenedDBPtr) pmalloc(sizeof(DBInfo))) == NULL)
		return ERR_DATA_NO_SPACE;
	/* add a new opened DB in the head of opendb_table */
	open_db->next_db = opendb_table;
	opendb_table = open_db;
	open_db->dbid = dbid;
	open_db->sort_table = NULL;
	open_db->app_mode = NULL;
	open_db->record = NULL;
	/* create new sort table */
	result = DataBuildSortTable(sblock, sort_field, &sort_tbl, NULL);
	if (result != TRUE)
	{	/* cannot build sort table, close the db & return error */
		opendb_table = open_db->next_db;
		pfree(open_db);
		return ERR_DATA_NO_SPACE;
	}
	open_db->next_record_id = ((DBHeader*) SBlockAddr(sblock))->next_rec_id;
	sort_tbl->next_table = open_db->sort_table;
	open_db->sort_table = sort_tbl;  /* add the sort table to head */
	if (open_mode == OPEN_EX)
		open_db->ex_app = SysGetActiveAppID();
	else
		open_db->ex_app = 0;
	
	open_db->app_mode = (AppLnkPtr) pmalloc(sizeof(AppLnk));
	open_db->app_mode->app = app;
	open_db->app_mode->open_mode = open_mode;
	open_db->app_mode->sort_field = sort_field;
	open_db->app_mode->next_app = NULL;
	open_db->record = NULL;
	open_db->cat_lnk = NULL; /* category not read yet */
	
	return TRUE;
}


/*
Function         : DataBuildSortTable
Purpose          : Build a sort table
Scope            : Internal
Input Parameters : block - the first block of the database
sort_field
Output Parameters: sort_table - sort_table with sorted records
Return	    : TRUE
ERR_DATA_DB_MISS - not a db header block
ERR_DATA_NO_MEM
Comment          :
*/
Err DataBuildSortTable(UWORD sblock, USHORT sort_field, SortTableLnkPtr *sort_table, RecordID *max_rec_id)
{
	UWORD 			i;
	BOOLEAN 		is_sort;
	UWORD 			total_rec;
	UWORD 			block_addr, db_block;
	USHORT 			block;
	DBHeader 		*db_ptr;
	RecordHeader 	*rec_ptr, *old_rec_ptr;
	RecordID 		*record_id;
	UBYTE 			*cat_id;
	DatabaseID 		dbid;
	RecordID 		m_rec_id;
	BOOLEAN			quicksort = FALSE;
	
	DatabaseID		temp_id;
	BOOLEAN			set = FALSE;
	UWORD			last_sblock = 0xFFFFFFFF;
	RecordID		last_rec_id = 0xFFFFFFFF;
	
	UBYTE			*TempData;
	USHORT			field_num; 
	
	dbid = mat_info[SBlockMat(sblock)].mat[SBlockBlock(sblock)].id;
	if (DataTotalRecord(dbid, &total_rec) != TRUE)
		return ERR_DATA_DB_MISS;
	
	if((*sort_table = (SortTableLnkPtr) pmalloc(sizeof(SortTableLnk)))==NULL)
		return ERR_DATA_NO_MEM;
	
	(*sort_table)->next_table = NULL;
    (*sort_table)->sort_field = sort_field;
    field_num = (USHORT)((*sort_table)->sort_field) & (USHORT)0x3fff;
	
	if (sort_field == DB_NO_SORT)
		is_sort = FALSE;
	else
		is_sort = TRUE;
	
	db_block = sblock;
	block_addr = SBlockAddr(sblock);         /* block address */
	block = SBlockBlock(sblock);             /* block number */
	
	(*sort_table)->total_entry = (USHORT) (total_rec+3);
	
	if(((*sort_table)->rec_id = (UWORD*) pmalloc((total_rec + 3 )* sizeof(UWORD))) == NULL)
	{
		pfree(sort_table);
		return ERR_DATA_NO_MEM;
	}
	
	if(((*sort_table)->cat = (UBYTE*) pmalloc((total_rec + 3 )* sizeof(UBYTE))) == NULL)
	{
		pfree((*sort_table)->rec_id);
		pfree(sort_table);
		return ERR_DATA_NO_MEM;
	}
	
	record_id = (*sort_table)->rec_id;
    cat_id = (*sort_table)->cat;
	
	for(i=0;i<total_rec+3;i++)
		record_id[i] = 0xFFFFFFFF;
	
	/* find all record's id */
	db_ptr = (DBHeader*) block_addr;
	rec_ptr = (RecordHeader*) (block_addr + db_ptr->length);
	i=0;
	m_rec_id = 0;
	
	if (is_sort)
	{
		QuickSortData = (UBYTE*)pmalloc(((total_rec + 3) * DATA_CMP_SIZE) * sizeof(UBYTE));
		TempData = QuickSortData;
		QuickSortDataArray = (UBYTE**)pmalloc((total_rec + 3) * sizeof(UBYTE *));
	}		
	
    if ((total_rec > QUICK_SORT_RECORD))
        quicksort = TRUE;       
	
	
    if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && NormRec(rec_ptr))
	{
		m_rec_id = (((rec_ptr->rec_id > m_rec_id)  && (rec_ptr->rec_id < 0x3fffffff)) ? rec_ptr->rec_id : m_rec_id);
		cat_id[i] = rec_ptr->cat;		
		
		if (is_sort)
		{
			DataCopyField(TempData, sblock, rec_ptr, field_num, DATA_CMP_SIZE);
			TempData += DATA_CMP_SIZE;		
		}			
		record_id[i++] = rec_ptr->rec_id;
		
		
		if (!quicksort)
			DataSortData(db_block, rec_ptr->rec_id, rec_ptr->cat, sort_table, is_sort, NULL);
	}	
	
	while(1)
	{
		while(1)
		{
			if( (((UWORD)rec_ptr) + rec_ptr->size) < ((((UWORD)rec_ptr) & PTR_ALIGIN_MASK) + BLOCK_SIZE))
			{
				rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
				break;
			}
			else
			{
				if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					goto  exit_loop;
				sblock = MakeSBlock(SBlockMat(sblock), block);
				old_rec_ptr = rec_ptr;
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
				if(!(FragRec(rec_ptr)))
					break;
            }
		}
		
		if(!FragRec(rec_ptr)) 
		{
			if ((rec_ptr->size != DATA_CHUNK_END) && NormRec(rec_ptr))
			{
				m_rec_id = (((rec_ptr->rec_id > m_rec_id)  && (rec_ptr->rec_id < 0x3fffffff)) ? rec_ptr->rec_id : m_rec_id);
				cat_id[i] = rec_ptr->cat;
				
				if (!quicksort)
				{					
					if (is_sort)
					{
						DataCopyField(TempData, sblock, rec_ptr, field_num, DATA_CMP_SIZE);
						TempData += DATA_CMP_SIZE;					
					}						
					record_id[i++] = rec_ptr->rec_id;
					DataSortData(db_block, rec_ptr->rec_id, rec_ptr->cat, sort_table, is_sort, NULL);
				}
				else
				{
					if (!set)
					{
						if (is_sort)
						{
							DataCopyField(TempData, sblock, rec_ptr, field_num, DATA_CMP_SIZE);
							TempData += DATA_CMP_SIZE;						
						}							
						record_id[i++] = rec_ptr->rec_id;
					}
					else
					{
						set = FALSE;
						if (is_sort)
						{
							DataCopyField(TempData, last_sblock, rec_ptr, field_num, DATA_CMP_SIZE);
							TempData += DATA_CMP_SIZE;						
						}							
						record_id[i++] = last_rec_id;
					}
				}
			}
		}
		
		if(((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
		{
			if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
                break;
			
			sblock = MakeSBlock(SBlockMat(sblock), block);
			old_rec_ptr = rec_ptr;
			rec_ptr = (RecordHeader *) SBlockAddr(sblock);
            if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && (rec_ptr != old_rec_ptr) && NormRec(rec_ptr))
			{
				m_rec_id = (((rec_ptr->rec_id > m_rec_id)  && (rec_ptr->rec_id < 0x3fffffff)) ? rec_ptr->rec_id : m_rec_id);
				cat_id[i] = rec_ptr->cat;
				if (is_sort)
				{
					DataCopyField(TempData, sblock, rec_ptr, field_num, DATA_CMP_SIZE);
					TempData += DATA_CMP_SIZE;				
				}					
				record_id[i++] = rec_ptr->rec_id;
				
				if (!quicksort)
					DataSortData(db_block, rec_ptr->rec_id, rec_ptr->cat, sort_table, is_sort, NULL);
			}
		}
		else
			if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
			{
				if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					break;
				
				sblock = MakeSBlock(SBlockMat(sblock), block);
				old_rec_ptr = rec_ptr;
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
				if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && (rec_ptr != old_rec_ptr) && NormRec(rec_ptr))
				{
					m_rec_id = (((rec_ptr->rec_id > m_rec_id)  && (rec_ptr->rec_id < 0x3fffffff)) ? rec_ptr->rec_id : m_rec_id);
					cat_id[i] = rec_ptr->cat;
					if (is_sort)
					{
						DataCopyField(TempData, sblock, rec_ptr, field_num, DATA_CMP_SIZE);
						TempData += DATA_CMP_SIZE;					
					}						
					record_id[i++] = rec_ptr->rec_id;
					
					if (!quicksort)
						DataSortData(db_block, rec_ptr->rec_id, rec_ptr->cat, sort_table, is_sort, NULL);
				}
				else if (NormRec(rec_ptr))
				{
					if (!set && quicksort)
					{
						set = TRUE;
						last_sblock = sblock;
						last_rec_id = rec_ptr->rec_id;
					}
				}
			}
	}
	
	
exit_loop:
	if (quicksort && is_sort)
    {   	
		for (i = 0; i < total_rec; i++)
			QuickSortDataArray[i] = QuickSortData + i * DATA_CMP_SIZE;
		
		DataQuickSortData(db_block, *sort_table, total_rec, 0, total_rec - 1);
    }
	
	if (QuickSortData)
	{
		pfree(QuickSortData);
		QuickSortData = NULL;
	}		
	
	if (QuickSortDataArray)
	{
		pfree(QuickSortDataArray);
		QuickSortDataArray = NULL;
	}			
	
	if(max_rec_id)
		*max_rec_id = m_rec_id;
	
	return TRUE;
}

UBYTE chartoupper(UBYTE x)
{
	if((x>='a') && (x<='z'))
		x-= 32;
	return x;
}

SHORT textstrcmp(UBYTE *sptr, UBYTE *dptr)
{
#define FIRST_SMALLER	-10
#define SECOND_SMALLER	10
#define CMP_EQUAL		0
	
	UWORD i = DATA_CMP_SIZE;
	UBYTE x, y;
	
	do {
		x = *sptr; y = *dptr;
		if((x>='a') && (x<='z')) x -= 32;
		if((y>='a') && (y<='z')) y -= 32;
		if(!x | (x<y)) return FIRST_SMALLER;
		if(!y | (y<x)) return SECOND_SMALLER;
		sptr++; dptr++;
	} while(--i);
    return CMP_EQUAL;
}
/*
SHORT textstrcmp(UBYTE *src, UBYTE *dest)
{
#define FIRST_SMALLER	-10
#define SECOND_SMALLER	10
#define CMP_EQUAL		0

  WORD i = 0;
  SHORT result = CMP_EQUAL;
  UBYTE *sptr = src;
  UBYTE *dptr = dest;
  
	while(*sptr && *dptr)
	{
	if(chartoupper(*sptr) < chartoupper(*dptr))
	{
	result = FIRST_SMALLER;
	break;
	}
	else if(chartoupper(*dptr) < chartoupper(*sptr))
	{
	result = SECOND_SMALLER;
	break;
	}
	sptr++; dptr++;
	if(++i >= DATA_CMP_SIZE)
	break;
	}
	if(result != CMP_EQUAL)
	return result;
	
	  if(*sptr)
	  return SECOND_SMALLER;
	  
		if(*dptr)
        return FIRST_SMALLER;
		
		  return CMP_EQUAL;
		  }
*/

SHORT strcmpubyte(UBYTE *string1, UBYTE *string2)
{
	while(*string1 && *string2)
	{
		if(*string1 > *string2)
			return 1;
		else
			if(*string1 < *string2)
				return - 1;
			string1++; string2++;
	}
	if(*string1)
		return 1;
	if(*string2)
		return -1;
	return 0;
	
}


/*
Function         : DataSortData
Purpose          : Find insert position of the record and insert into sort table
Scope            : Internal
Input Parameters : sblock
rec_id
sort_table
is_sort - sort or not sort the record
Output Parameters: sort_table
sort_pos    - insertion pointer, the record index in sort table
Return	    : TRUE
ERR_DATA_NO_MEM  -  no mem
Comment          :
*/
Err DataSortData(UWORD sblock, RecordID rec_id, UBYTE cat, SortTableLnkPtr *sort_table, BOOLEAN is_sort,
				 UWORD *sort_pos)
{
	UWORD       i, valid_entry;
	UWORD       found;
	UWORD       *rec_table;
	UBYTE       *cat_table;
	USHORT      sort_field;
	UBYTE       rec_data[DATA_CMP_SIZE];
	BYTE        *rec_data_ptr;
	RecordID    *record_id;
	UBYTE       *cat_id;
	
	found = 0;
	
	/* check if the record id already in the sort table, if true, remove it
	   from sort table
	*/
	valid_entry = 0;  /* check any used slot in the table, i.e. rec_id = 0xFFFFFFFF  */
	record_id = (*sort_table)->rec_id;
	cat_id = (*sort_table)->cat;
	for(i=0;i<(*sort_table)->total_entry;i++)
	{
		if(record_id[i] == rec_id)
		{
			found = i+1;  /* prevent record 0 = rec_id */
			if(is_sort == FALSE)
			{
				if(sort_pos)  /* not NULL */
					*sort_pos=i;
				return TRUE;   /* no need to sort */
			}
		}
		if(record_id[i] == 0xFFFFFFFF) /* end of sort table */
			break;
		else
			valid_entry++;
	}
	
	if (found)	/* the record is already in the sort table */
	{
		cat = (*sort_table)->cat[found-1];
		for(i=found-1;i<valid_entry-1;i++) /* found = found position + 1 */
		{
			record_id[i] = record_id[i+1];
			cat_id[i]     = cat_id[i+1];
		}
		
        
		record_id[--valid_entry] = 0xFFFFFFFF; /* mark end of sort table */
		/* valid entry is minus one if the record is removed */
	}
	
	if(valid_entry >= (*sort_table)->total_entry)  /* no more room in sort table */
	{
		if((rec_table = (UWORD*) pmalloc(sizeof(UWORD)*(valid_entry + 3)))==NULL)
			return ERR_DATA_NO_MEM;
		
		if((cat_table = (UBYTE*) pmalloc(sizeof(UBYTE) * (valid_entry + 3)))==NULL)
		{
			pfree(rec_table);
			return ERR_DATA_NO_MEM;
		}
		
		for(i=0;i<valid_entry;i++)   /* copy data from old table to new table */
		{
			rec_table[i] = record_id[i];
			cat_table[i] = cat_id[i];
		}
		
		rec_table[i] = rec_table[i+1] = rec_table[i+2] = 0xFFFFFFFF;   /* init new entry to nothing */
		
		pfree((*sort_table)->rec_id);
		pfree((*sort_table)->cat);
		record_id = (*sort_table)->rec_id = rec_table;
		cat_id = (*sort_table)->cat = cat_table;
		(*sort_table)->total_entry = (USHORT)(valid_entry + 3);
		
	}
	
	found = 0;
	sort_field = ((USHORT) (*sort_table)->sort_field) & (USHORT)0x3fff;
	
	rec_data_ptr    = (BYTE*)DataRecordData(sblock, rec_id, sort_field);
	if( (*sort_table)->sort_field & (USHORT) SORT_TEXT_MODE)
		strcpy((BYTE*)rec_data, rec_data_ptr);
    else
    {        	    
		data_copy_bin_str((BYTE*)rec_data, rec_data_ptr);
    }	    
	//strcpy((BYTE*)rec_data, (BYTE*)DataRecordData(sblock, rec_id, sort_field));
	for(i=0;i<valid_entry;i++)  /* found insertion point */
	{
		if( (*sort_table)->sort_field & (USHORT) SORT_TEXT_MODE)
		{
			if(textstrcmp((UBYTE*)rec_data, (UBYTE*)DataRecordData(sblock, record_id[i], sort_field)) < 0)
			{
				found = i + 1;
				break;
			}
		}
		else
		{
			if(strcmpubyte(rec_data, DataRecordData(sblock, record_id[i], sort_field)) < 0)
			{
				found = i + 1;
				break;
			}
		}
	}
	
	if(found)
	{
		for(i=valid_entry;i>=found;i--)
		{
			record_id[i] = record_id[i-1];
			cat_id[i] = cat_id[i-1];
		}
		
		record_id[found-1] = rec_id;
		cat_id[found-1] = cat;
		
		if(sort_pos) *sort_pos = found - 1;
	}
	else   /* should append at last */
	{
		record_id[valid_entry] = rec_id;
		cat_id[valid_entry] = cat;
		if(sort_pos) *sort_pos = valid_entry;
	}
	
	return TRUE;
}



/*
Function         : DataFreeSortTable
Purpose          : Free a sort table
Scope            : Internal
Input Parameters : dbid
sort_field
Output Parameters: None
Return	    : TRUE
FALSE
Comment          :
*/
BOOLEAN DataFreeSortTable(DatabaseID dbid, USHORT sort_field)
{
	OpenedDBPtr opendb_ptr;
	SortTableLnkPtr sort_table, prev_table;
	
	opendb_ptr= opendb_table;
	while(opendb_ptr != NULL)	/* find the db entry in table */
		if (opendb_ptr->dbid == dbid)
			break;
		else
			opendb_ptr = opendb_ptr->next_db;
		
		if(!opendb_ptr)
			return FALSE;
		
        sort_table = opendb_ptr->sort_table;
		prev_table = NULL;
		while((sort_table)!=NULL)   /* find the sort table */
			if ((sort_table)->sort_field == sort_field)
				break;
			else
			{
				prev_table = sort_table;
				(sort_table) = (sort_table)->next_table;
			}
			
			if(!(sort_table))
				return FALSE;
			
			if(prev_table)	/* not first table in db */
				(prev_table)->next_table = (sort_table)->next_table;
			else	/* first table in db */
                opendb_ptr->sort_table = (sort_table)->next_table;
			
			pfree((sort_table)->rec_id);
			pfree((sort_table)->cat);
			pfree(sort_table);
			
			return TRUE;
}

/*
Function         : DataUpdateSortTable
Purpose          : Update sort table after a record has been modified
Scope            : Internal
Input Parameters : dbid
rec_id
field_num - field number being updated
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_RECORD_NOT_OPEN
Comment          :
*/
Err DataUpdateSortTable(DatabaseID dbid, RecordID rec_id, USHORT field_num)
{
	OpenedDBPtr opendb_ptr;
	SortTableLnkPtr sort_table;
	UWORD i;
	USHORT block;
	UWORD sblock;
	
	if(DataGetDBPtr(dbid, &opendb_ptr, NULL) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i,block);
	sort_table = opendb_ptr->sort_table;
	while(sort_table != NULL)
	{
		if(((USHORT)sort_table->sort_field & (USHORT)0x3FFF) == field_num)
		{
			DataSortData(sblock,rec_id, 0xff, &sort_table,TRUE, NULL);
		}
		sort_table = sort_table->next_table;
	}
	return TRUE;
	
}


Err DataGetDBSortField(DatabaseID dbid, USHORT *field_num)
{
	OpenedDBPtr db_ptr;
	AppID app;
	AppLnkPtr app_lnk;
	
	app = SysGetActiveAppID();
	
	if(!DataGetDBPtr(dbid, &db_ptr, NULL))
		return ERR_DATA_DB_NOT_OPEN;
	
	app_lnk = db_ptr->app_mode;
	
	*field_num = 0;
	while(app_lnk != NULL)
	{
		if(app_lnk->app == app)
		{
			*field_num = app_lnk->sort_field;
			break;
		}
		app_lnk = app_lnk->next_app;
	}
	
	return TRUE;
	
}


/*
Function         : DataRecordBlock
Purpose          : Find the start block of a record
Scope            : Internal
Input Parameters : sblock - the db info block of the record
rec_id
Output Parameters: s_block - the block where the record found
ptr - pointer to the record
Return	    : TRUE
ERR_DATA_DB_MISS
ERR_DATA_INV_RECID
Comment          : should not chk record type since will call by all
function include funtion to organize archive and category
*/
Err DataRecordBlock(UWORD sblock, RecordID rec_id, UWORD *s_block,
					RecordHeader **ptr)
{
	DBHeader *db_ptr;
	RecordHeader *rec_ptr;
	USHORT block;
	
	
	if((dbcache.enable) && dbcache.valid)
	{
		if(dbcache.rb_rec_ptr->rec_id == rec_id)
			if(dbcache.rb_rec_ptr->size)
			{
				
				if(ptr)
					*ptr = dbcache.rb_rec_ptr;
				if(s_block)
					*s_block = dbcache.rb_sblock;
				return TRUE;
			}
	}
	//      printf("\nnot use cache");
	
	db_ptr = (DBHeader*) SBlockAddr(sblock);
	if(BlockType(mat_info[0].mat[sblock].attribute) == MAT_BLOCK_DB_REC)
		rec_ptr = (RecordHeader*) (SBlockAddr(sblock) + db_ptr->length);
	else
		rec_ptr = (RecordHeader*) (SBlockAddr(sblock));
	
	block = SBlockBlock(sblock);
	while(1)
	{
		if((rec_ptr->rec_id == rec_id) && (rec_ptr->size != DATA_CHUNK_END))
			break;
		
		rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
		if(((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
		{
			if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
				return ERR_DATA_INV_RECID;
			sblock = MakeSBlock(SBlockMat(sblock), block);
			rec_ptr = (RecordHeader *) SBlockAddr(sblock);
		}
		else
			if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
			{
				if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					return ERR_DATA_INV_RECID;
				sblock = MakeSBlock(SBlockMat(sblock), block);
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
			}
	}
	if (rec_ptr->rec_id != rec_id) 	return ERR_DATA_INV_RECID;
	if(s_block)
		*s_block = sblock;
	if(ptr)
		*ptr = rec_ptr;
		/*          
		if(FragRec(rec_ptr))
		{
	*/
	if(NormRec(rec_ptr) && dbcache.valid)
	{
        dbcache.rb_rec_ptr = rec_ptr;
        dbcache.rb_sblock = sblock;
        dbcache.enable = TRUE;
	}
	/*
	}
	else
	   dbcache.enable = FALSE;
	*/                   
	return TRUE;
}


/*
Function         : DataFindRecord
Purpose          : Find a record
Scope            : All
Input Parameters : dbid
find_field
start_rec
buffer
Output Parameters: rec_id
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_NOT_FOUND
ERR_DATA_INV_PARAM
Comment          : buffer must terminated by 0x00
*/
Err DataFindRecord(DatabaseID dbid, USHORT find_field, UWORD start_rec, BYTE *buffer, RecordID *rec_id)
{
	OpenedDBPtr dbinfo_ptr;
	AppLnkPtr app_ptr;
	USHORT buf_len,i, block;
	SortTableLnkPtr sort_table;
	BYTE field_data[DATA_CMP_SIZE];
	UWORD sblock;
	
	
	
	if(DataGetDBPtr(dbid, &dbinfo_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	buf_len = strlen(buffer);
	if(buf_len >= DATA_CMP_SIZE)
		buf_len = DATA_CMP_SIZE - 1;
	
	sort_table = dbinfo_ptr->sort_table;
	while(sort_table!=NULL)
	{
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		sort_table = sort_table->next_table;
	}
	if(sort_table==NULL)
		return ERR_DATA_INV_PARAM;
	
	for(i=(USHORT) start_rec;i<sort_table->total_entry;i++)
	{
		if(sort_table->rec_id[i] == 0xFFFFFFFF)
			break;
		strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[i],find_field));
		field_data[buf_len] = 0x00;
		if (strcmp(field_data, buffer) == 0)
		{
			*rec_id = sort_table->rec_id[i];
			return TRUE;
		}
	}
	
	return ERR_DATA_NOT_FOUND;
}

void datamgr_strcpy(BYTE *data1, BYTE *data2, USHORT len)
{
	while(len--)
		*data1++ = *data2++;
}



BYTE datamgr_strcmp(BYTE *data1, BYTE *data2, USHORT len)
{
	while(len--)
	{
		if((UBYTE)*data1 > (UBYTE)*data2)
			return -1;
		else
			if((UBYTE)*data1 < (UBYTE)*data2)
				return 1;
			data1++; data2++;
	}
	return 0;
}		


/*
Function         : DataFindBinRecord
Purpose          : Find a record
Scope            : All
Input Parameters : dbid
find_fie
ld
start_rec
buffer
compare_length
Output Parameters: rec_id
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_NOT_FOUND
ERR_DATA_INV_PARAM
Comment          : buffer must terminated by 0x00
*/
Err DataFindBinRecord(DatabaseID dbid, USHORT find_field, UWORD start_rec, BYTE *buffer, USHORT compare_length, RecordID *rec_id)
{
	OpenedDBPtr dbinfo_ptr;
	AppLnkPtr app_ptr;
	USHORT buf_len,i, block;
	SortTableLnkPtr sort_table;
	BYTE field_data[DATA_CMP_SIZE];
	UWORD sblock;
	
	UWORD   bottom, top, middle;
	UWORD   total_num_items;    
	UBYTE   temp;		
	
	if(DataGetDBPtr(dbid, &dbinfo_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	buf_len = compare_length;
	if(buf_len >= DATA_CMP_SIZE)
		buf_len = DATA_CMP_SIZE - 1;
	
	sort_table = dbinfo_ptr->sort_table;
	while(sort_table!=NULL)
	{
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		sort_table = sort_table->next_table;
	}
	if(sort_table==NULL)
		return ERR_DATA_INV_PARAM;
	
    total_num_items = sort_table->total_entry;
    do
    {
		if(sort_table->rec_id[total_num_items - 1]  != 0xFFFFFFFF)
			break;
    } while(--total_num_items > 0);
	
	if(total_num_items == 0)
		return ERR_DATA_NOT_FOUND;
	
	if(total_num_items == 1)
	{
		datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[0],find_field), buf_len);
		field_data[buf_len] = 0x00;
		if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) == 0)
		{
			*rec_id = sort_table->rec_id[0];
			return TRUE;
		}
		return ERR_DATA_NOT_FOUND;
	}
	
	
    bottom  =   0;
    top     =   total_num_items - 1;
	
	datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[bottom],find_field), buf_len);
	
	if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) < 0)
		return ERR_DATA_NOT_FOUND;
	
    if( datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) == 0)
	{
		*rec_id = sort_table->rec_id[bottom];
		return TRUE;		
	}
	
    datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[top],find_field), buf_len);
	
	if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) > 0)
		return ERR_DATA_NOT_FOUND;
	if( datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) == 0)
	{
		*rec_id = sort_table->rec_id[top];
		return TRUE;		
	}
	
    do
    {
		if(bottom ==  top - 1)
        {
			datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[bottom],find_field), buf_len);
			
			if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) < 0)
				return ERR_DATA_NOT_FOUND;
			
            if( datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) == 0)
			{
				*rec_id = sort_table->rec_id[bottom];
				return TRUE;		
			}
			
			datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[top],find_field), buf_len);
			
			if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) < 0)
				return ERR_DATA_NOT_FOUND;
			
            if( datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) == 0)
			{
				*rec_id = sort_table->rec_id[top];
				return TRUE;		
			}			
		}
		
        middle  =   (top +  bottom)/2;
		
		datamgr_strcpy((BYTE*)field_data, (BYTE*)DataRecordData(sblock,sort_table->rec_id[middle],find_field), buf_len);
		
		if (datamgr_strcmp((BYTE*)field_data, (BYTE*)buffer, buf_len) <= 0)
			top = middle;
		else
			bottom = middle;
		
	}while (top > bottom); 
	
	return ERR_DATA_NOT_FOUND;
}


/*
Function         : DataRecordData
Purpose          : Read firdst DATA_CMP_SIZE byte of a record
Scope            : Internal
Input Parameters : sblock - DB header block
rec_id
field
Output Parameters: None
Return	    : Record Data
Comment          : return 0x00 if record/field not found
*/
UBYTE *DataRecordData(UWORD sblock, RecordID rec_id, USHORT field)
{
	UWORD i;
	UWORD start_sblock, current_sblock;
	UWORD first_offset, last_size, fsize;
	USHORT num_block, next_block;
	UWORD field_size;
	BOOLEAN frag;
	BYTE *ptr;
	mmu_data[0] = 0x00;
	
#ifdef DEBUG
    quick_count ++;
#endif
	
	if (DataFieldInfo(sblock, rec_id, field, &fsize ,&start_sblock, &first_offset,
		&num_block, &last_size, &frag) != TRUE)
		return (UBYTE*) mmu_data;    /* invalid field, rec id ... */
	field_size = fsize + (BLOCK_SIZE - RECHEADER_SIZE) * (num_block);
	if(num_block != 0)
		field_size += last_size;
	if(field_size==0)
		return mmu_data;  /* nothing in the field */
	current_sblock = start_sblock;
	ptr = (BYTE*) (SBlockAddr(current_sblock) + first_offset); /* field start */
	for(i=0;i<DATA_CMP_SIZE-2;i++)
	{
		mmu_data[i] = *ptr++;
		if(i >= field_size)       /* field size < DATA_CMP_SIZE */
		{
			mmu_data[i+1] = '\0';
			return (UBYTE*) mmu_data;
		}
		
		if((UWORD) ptr - SBlockAddr(current_sblock) >= BLOCK_SIZE)
		{    /* block end */
			if((MemoryNextBlock(&(mat_info[SBlockMat(current_sblock)]),
				SBlockBlock(current_sblock), &next_block)) != TRUE)
			{   /* no next block */
				mmu_data[i+1] = '\0';
				return (UBYTE*) mmu_data;
			}
			current_sblock = MakeSBlock(SBlockMat(current_sblock), next_block);
			ptr = (BYTE*) (SBlockAddr(current_sblock) + RECHEADER_SIZE);
		}
	}
	mmu_data[i] = '\0';
	return (UBYTE*) mmu_data;
}


/*
Function         : DataFieldSize
Purpose          : Get size of a field
Scope            : All
Input Parameters : dbid
rec_id
field_num
Output Parameters: field_size  - size of the field
Return	    : TRUE
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          :
*/
Err DataFieldSize(DatabaseID dbid, RecordID rec_id, USHORT field_num,
				  UWORD *field_size)
{
	USHORT i, num_block;
	USHORT block;
	UWORD sblock, start_sblock;
	UWORD first_offset, last_size;
	BOOLEAN frag;
	Err result;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	result = DataRecordBlock(sblock, rec_id, &start_sblock, NULL);
	if (result != TRUE)  return result; /* invalid field */
	
	result = DataFieldInfo(sblock, rec_id, field_num, field_size, &start_sblock,
		&first_offset, &num_block, &last_size, &frag);
	if (result != TRUE)  return result; /* invalid field */
	
										/*	*field_size = fsize + num_block * (BLOCK_SIZE - RECHEADER_SIZE);
										if(num_block != 0)
										*field_size += last_size;
	*/
	return TRUE;
	
}

/*
Function         : DataFieldInfo
Purpose          : Get info. about a record field
Scope            : All
Input Parameters : sblock  - first block of the db
rec_id
field
Output Parameters: size  - size of the field
start_sblock - first block of the record field
first_offset - the offset of the block of the field of the first char (exclude field size)
num_block    - number of block occupied by the field
(exclude first block)
last_size    - if more than one block, the size of the
field in the last block, exclude RECHEADER_SIZE,
if not fragment, this is the field size
frag	   - the record is fragment or not
Return	    : TRUE
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          : Do not chk record type
*/
Err DataFieldInfo(UWORD sblock, RecordID rec_id, USHORT field_num, UWORD *size, UWORD *start_sblock,
				  UWORD *first_offset, USHORT *num_block, UWORD *last_size, BOOLEAN *frag)
{
	RecordHeader *rec_ptr;
	BYTE 	*field_ptr;
	WORD    i;
	UBYTE 	j;
	UWORD 	field_size, byte_use;
	
	/* find record physical address */
	if((DataRecordBlock(sblock, rec_id, &sblock, &rec_ptr)) != TRUE)
		return ERR_DATA_INV_RECID;
	
	if(field_num >= rec_ptr->total_field)
		return ERR_DATA_INV_FIELD;
	
	field_ptr = (BYTE*) ((UWORD)rec_ptr + RECHEADER_SIZE);		// position to 1st field size byte
	for(i=0; i<=field_num; i++) {
		field_size = 0;
		byte_use = 0;
		do {
			j = *field_ptr;
			field_size |= ((j >> 1) << (byte_use*7));
			byte_use++;
			_valid_ptr(&sblock, &field_ptr, 1, NULL);
		} while (!(j & 1));
		if (i<field_num) _valid_ptr(&sblock, &field_ptr, field_size, NULL);
	}
	*size = field_size;
	*start_sblock = sblock;
	*first_offset = (UWORD)field_ptr & 0xfff;
	
	if(FragRec(rec_ptr)) {
		*frag = TRUE;
		i = field_size - (BLOCK_SIZE - *first_offset);
		/* if the field size + space left >= BLOCK_SIZE, another block is needed */
		if (i>=0) {
			*num_block = (i/(BLOCK_SIZE - RECHEADER_SIZE))+1;
			*last_size = i % (BLOCK_SIZE - RECHEADER_SIZE);
		}
		else {
			*num_block = 0;
			*last_size = field_size;
		}
	}
	else {
		*frag = FALSE;
		*num_block = 0;
		*last_size = field_size;
	}
	return TRUE;
}

/*
Function         : DataFieldInfo2
Purpose          : Get info. about a record field
Scope            : All
Input Parameters : sblock  - first block of the db
rec_id
field
Output Parameters: size  - size of the field
start_sblock - first block of the record field
first_offset - the offset of the block of the field of the first char (exclude field size)
num_block    - number of block occupied by the field
(exclude first block)
last_size    - if more than one block, the size of the
field in the last block, exclude RECHEADER_SIZE,
if not fragment, this is the field size
frag	   - the record is fragment or not
Return	    : TRUE
ERR_DATA_INV_RECID
ERR_DATA_INV_FIELD
Comment          : Do not chk record type
*/
Err DataFieldInfo2(UWORD sblock, RecordID rec_id, USHORT field, UWORD *size, UWORD *start_sblock,
				   UWORD *first_offset, USHORT *num_block, UWORD *last_size, BOOLEAN *frag)
{
	
	return DataFieldInfo(sblock, rec_id, field, size, start_sblock,
		first_offset, num_block, last_size, frag);
}

/*
Function         : DataIsExclusive
Purpose          : Check if the database is opened by other app. in ex. mode
Scope            : All
Input Parameters : dbid
Output Parameters: app  - if return TRUE, the app that open the db in ex. mode
Return	    : TRUE
FALSE
Comment          :
*/
BOOLEAN DataIsExclusive(DatabaseID dbid, AppID *app)
{
	OpenedDBPtr open_db;
	
	/* check if DB opened */
	open_db = opendb_table;
	
	while (open_db != NULL)
	{
		if (open_db->dbid == dbid)
			break;
		open_db = open_db->next_db;
	}
	
	/* DB not open, so not open ex. */
	if (open_db == NULL)
		return FALSE;
	
	/* if ExApp <> 0, open ex. */
	if (open_db->ex_app != (AppID) 0)
	{
		if (app != NULL)
			*app = open_db->ex_app;
		return TRUE;
	}
	else
		return FALSE;
}

/*
Function         : DataIsDBOpen
Purpose          : Check if the database is opened by the app
Scope            : All
Input Parameters : dbid
Output Parameters: open_mode
sort_field
Return	    : TRUE
FALSE
Comment          :
*/
BOOLEAN DataIsDBOpen(DatabaseID dbid, UBYTE *open_mode, USHORT *sort_field)
{
	OpenedDBPtr open_db;
	AppLnkPtr   app_mode;
	AppID app;
	
	app = SysGetActiveAppID();
	/* examine each DB entry in the table */
	open_db = opendb_table;
	while (open_db != NULL)
	{
		/* compare the DBID */
		if (open_db->dbid == dbid)
		{
			/* examine each App that open the DB */
			app_mode = open_db->app_mode;
			while (app_mode != NULL)
			{
				/* compare the AppID with match DBID */
				if (app_mode->app == app)
				{
					if (open_mode != NULL)
						*open_mode = app_mode->open_mode;
					
					if(sort_field != NULL)
						*sort_field = app_mode->sort_field;
					
					return TRUE;
				}
				app_mode = app_mode->next_app;
			}
		}
		open_db = open_db->next_db;
	}
	return FALSE;
}

/*
Function         : DataDBInfo
Purpose          : Get a database's information
Scope            : All
Input Parameters : dbid
Output Parameters: version
owner
create_date
backup_date
name
owner_info
Return	    : TRUE
ERR_DATA_DB_MISS
Comment          :
*/
Err DataDBInfo(DatabaseID dbid, USHORT *version, AppID *owner, RTM *create_date,
			   RTM *backup_date, BYTE name[], BYTE *owner_info)
{
	USHORT i;
	USHORT block;
	UWORD sblock;
	DBHeader *hdr;
	BYTE *pb;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	hdr = (DBHeader*) SBlockAddr(sblock);
	
	if(version)
		*version = hdr->version;
	
	if(owner)
		*owner = hdr->owner;
	
	if(create_date)
		*create_date = _date322rtm(hdr->create_date);
	
	if(backup_date)
		*backup_date = _date322rtm(hdr->backup_date);
	
	if(name)
	{
		pb = (BYTE*)(SBlockAddr(sblock) + DBHEADER_SIZE);
		for(i=0;i<32;i++)
		{
			if (pb[i] == 0x00)
				break;
			name[i] = pb[i];
		}
		name[i] = 0x00;
	}
	
	if(owner_info)
	{
		while(*(pb++) != 0x00);  /* find the start position of owner info */
		i=0;
		
		do{
			owner_info[i++] = *(pb++);
		} while (*(pb) != 0x00);
		owner_info[i] = 0x00;
	}
	
	
	
	return TRUE;
}


/*
Function         : DataReadFieldSysMem
Purpose          : Read a part of the field into system memory
Scope            : All
Input Parameters : dbid
rec_id
field_num
start_pos
num_byte
Output Parameters: buffer
byte_read
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_REC_NOT_OPEN
ERR_DATA_INV_PARAM
ERR_DATA_NO_MEM
Comment          :
*/
Err DataReadFieldSysMem(DatabaseID dbid, RecordID rec_id, USHORT field_num,
						UWORD start_pos, UWORD num_byte, BYTE **buffer,
						UWORD *byte_read)
{
	BYTE *buf;
	UWORD size, last_size, first_offset;
	UWORD start_sblock, sblock, prev_blk;
	AppInfoLnkPtr app_lnk;
	USHORT num_block, i, block;
	BOOLEAN frag;
	UWORD field_size;
	UWORD r_offset, r_len, r_start, com;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	if(DataGetRecordPtr(dbid, rec_id, NULL, NULL, &app_lnk, NULL, NULL) == FALSE)
		return ERR_DATA_REC_NOT_OPEN;
	
	dbcache.valid = TRUE;
	
	if((DataFieldInfo(sblock, rec_id, field_num, &size, &start_sblock,
		&first_offset, &num_block, &last_size, &frag)) != TRUE)
	{
		dbcache.valid = FALSE;
		return ERR_DATA_INV_FIELD;
	}
	
	prev_blk = sblock = start_sblock;
	
	field_size = size + (num_block) * (BLOCK_SIZE - RECHEADER_SIZE);
	if(num_block != 0)
		field_size += last_size;
	
	if(app_lnk->last_field != field_num)	/* change field since last read */
	{
		app_lnk->last_field = field_num;
		app_lnk->last_pos = 0;
		app_lnk->last_cache_block = 0xFFFF;
		app_lnk->last_cache_offset = 0xFFFFFFFF;
	}
	
	switch(start_pos)
	{
	case READ_RELATIVE:
		r_offset = app_lnk->last_pos;
		break;
	default:
		r_offset = start_pos;
		break;
	}
	
	switch(num_byte)
	{
	case READ_TO_END:
		r_len = field_size;
		break;
	default:
		r_len = num_byte;
		break;
	}
	
	*buffer = NULL;
	
	if(r_offset >= field_size)
	{
		dbcache.valid = FALSE;
		return ERR_DATA_INV_PARAM;
	}
	
	if((r_offset + r_len) > field_size)
		r_len = field_size - r_offset;
	*byte_read = r_len;
	
	
	writable = 0;
	
	num_block = 0;
	
	if( (field_size > BLOCK_SIZE) &&
		(app_lnk->last_cache_offset < r_offset))
	{
		com = app_lnk->last_cache_offset;
		sblock = app_lnk->last_cache_block;
		num_block = app_lnk->last_cache_num_block;
		while(com < r_offset)
		{
			prev_blk = sblock;
            MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block);
			//                return ERR_DATA_INV_PARAM;
			com += (BLOCK_SIZE - RECHEADER_SIZE);
			sblock = MakeSBlock(SBlockMat(sblock), block);
			num_block++;
		}
		//com -= (first_offset - RECHEADER_SIZE);
		//r_start = BLOCK_SIZE - (com - r_offset);
		//r_start = r_offset - app_lnk->last_cache_offset + (num_block-2)*BLOCK_SIZE - (BLOCK_SIZE - first_offset);
		r_start = r_offset - ( (num_block - 2) * (BLOCK_SIZE - RECHEADER_SIZE) + (BLOCK_SIZE - first_offset)) + RECHEADER_SIZE;
		sblock = prev_blk;
		
		if(num_block != 1)
		{
			app_lnk->last_cache_offset = (num_block-2) * (BLOCK_SIZE - RECHEADER_SIZE) + (BLOCK_SIZE - first_offset);
		}
		else
			app_lnk->last_cache_offset = 0;
		
		app_lnk->last_cache_num_block = num_block - 1;
		
		//		app_lnk->last_cache_offset = com - BLOCK_SIZE + RECHEADER_SIZE ;
		app_lnk->last_cache_block = prev_blk;
		
		
	}
	else
		if(r_offset > (BLOCK_SIZE - first_offset))
		{
			app_lnk->last_cache_offset = 0;
			com = BLOCK_SIZE - first_offset;
			while(com < r_offset)
			{
				prev_blk = sblock;
				MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block);
				com += (BLOCK_SIZE - RECHEADER_SIZE);
				//app_lnk->last_cache_offset += (BLOCK_SIZE - RECHEADER_SIZE);
				num_block++;
				sblock = MakeSBlock(SBlockMat(sblock), block);
			}
			if(num_block != 1)
			{
				app_lnk->last_cache_offset = (num_block-2) * (BLOCK_SIZE - RECHEADER_SIZE) + (BLOCK_SIZE - first_offset);
			}
			else
				app_lnk->last_cache_offset = 0;
			
			app_lnk->last_cache_num_block = num_block - 1;
			//app_lnk->last_cache_offset -= BLOCK_SIZE - RECHEADER_SIZE;
			//app_lnk->last_cache_offset - (BLOCK_SIZE  - RECHEADER_SIZE - (first_offset - RECHEADER_SIZE));
			app_lnk->last_cache_block = prev_blk;
			r_start = BLOCK_SIZE - (com - r_offset);
		}
		else
		{
			app_lnk->last_cache_offset = 0xFFFFFFFF;
			r_start = first_offset + r_offset;
		}
		
		writable = 1;
		dbcache.valid = FALSE;
		
        if((buf = (BYTE*) pmalloc(sizeof(BYTE) * r_len)) == NULL)
		{
#ifdef DEBUG
			printf("\nDataReadField: not enough mem ");
#endif
			return ERR_DATA_NO_MEM;
		}
		
		DataReadBData(sblock, r_start, r_len, buf, NULL, NULL, NULL);
		
		*buffer = buf;
		
		return TRUE;
}




Err DataReadField(DatabaseID dbid, RecordID rec_id, USHORT field_num,
				  UWORD start_pos, UWORD num_byte, BYTE **buffer,
				  UWORD *byte_read)
{
	UWORD size, last_size, first_offset;
	UWORD start_sblock, sblock;
	AppInfoLnkPtr app_lnk;
	USHORT num_block, i, block;
	BOOLEAN frag;
	UWORD field_size;
	UWORD r_offset, r_len, r_start, com;
	BYTE *buf;
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	if(DataGetRecordPtr(dbid, rec_id, NULL, NULL, &app_lnk, NULL, NULL) == FALSE)
		return ERR_DATA_REC_NOT_OPEN;
	
	dbcache.enable = FALSE;
	
	if((DataFieldInfo(sblock, rec_id, field_num, &size, &start_sblock,
		&first_offset, &num_block, &last_size, &frag)) != TRUE)
		return ERR_DATA_INV_FIELD;
	
	sblock = start_sblock;
	
	field_size = size + (num_block) * (BLOCK_SIZE - RECHEADER_SIZE);
	if(num_block != 0)
		field_size += last_size;
	
	if(app_lnk->last_field != field_num)	/* change field since last read */
	{
		app_lnk->last_field = field_num;
		app_lnk->last_pos = 0;
	}
	
	switch(start_pos)
	{
	case READ_RELATIVE:
		r_offset = app_lnk->last_pos;
		break;
	default:
		r_offset = start_pos;
		break;
	}
	
	switch(num_byte)
	{
	case READ_TO_END:
		r_len = field_size;
		break;
	default:
		r_len = num_byte;
		break;
	}
	
	*buffer = NULL;
	
	if(r_offset >= field_size)
		return ERR_DATA_INV_PARAM;
	
	if((r_offset + r_len) > field_size)
		r_len = field_size - r_offset;
	*byte_read = r_len;
	if(r_offset > (BLOCK_SIZE - first_offset))
	{
		com = BLOCK_SIZE - first_offset;
		while(com < r_offset)
		{
			if (MemoryNextBlock(&mat_info[SBlockMat(sblock)], SBlockBlock(sblock), &block)!= TRUE)
				return ERR_DATA_INV_PARAM;
			
			com += (BLOCK_SIZE - RECHEADER_SIZE);
			sblock = MakeSBlock(SBlockMat(sblock), block);
		}
		r_start = BLOCK_SIZE - (com - r_offset);
	}
	else
		r_start = first_offset + r_offset;
	
	if((buf = (BYTE*) qmalloc(sizeof(BYTE) * r_len)) == NULL)
	{
#ifdef DEBUG
		printf("\nDataReadField: not enough mem ");
#endif
		return ERR_DATA_NO_MEM;
	}
	
	DataReadBData(sblock, r_start, r_len, buf, NULL, NULL, NULL);
	
	*buffer = buf;
	
	return TRUE;
				  }
				  
				  /*
				  Function         : DataSeekField
				  Purpose          : Position the record pointer
				  Scope            : All
				  Input Parameters : dbid
				  rec_id
				  field_num
				  position
				  Output Parameters: None
				  Return	    :  TRUE
				  ERR_DATA_DB_NOT_OPEN
				  ERR_DATA_REC_NOT_OPEN
				  ERR_DATA_INV_FIELD
				  Comment          :
				  */
				  Err DataSeekField(DatabaseID dbid, RecordID rec_id, USHORT field_num, WORD position)
				  {
					  AppInfoLnkPtr app_lnk;
					  UWORD fsize;
					  
					  if(DataGetRecordPtr(dbid, rec_id, NULL, NULL, &app_lnk, NULL, NULL) == FALSE)
						  return ERR_DATA_REC_NOT_OPEN;
					  
					  if((DataFieldSize(dbid, rec_id, field_num, &fsize) != TRUE))
						  return ERR_DATA_INV_FIELD;
					  
					  switch (position)
					  {
					  case SEEK_BOF:
						  app_lnk->last_pos = 0;
						  break;
					  case SEEK_EOF:
						  app_lnk->last_pos = fsize - 1;  /* start form 0 */
						  break;
					  default:
						  if(position >= 0)
						  {
							  if ((app_lnk->last_field) == field_num)  /* the same field as before */
								  app_lnk->last_pos += position;
							  else
								  app_lnk->last_pos = position;
							  if(app_lnk->last_pos >= fsize)
								  app_lnk->last_pos = fsize - 1;
						  }
						  else     /* cannot compute app_lnk->last_pos - position since app_lnk->last_pos is always +ve ! */
						  {
							  if(app_lnk->last_field != field_num)
								  app_lnk->last_pos = 0;
							  else
							  {
								  if(( (WORD) app_lnk->last_pos - position) < 0)
									  app_lnk->last_pos = 0;
								  else
									  app_lnk->last_pos -= position;
							  }
						  }
						  break;
					  }
					  app_lnk->last_field = field_num;
					  
					  return TRUE;
				  }
				  
				  /*
				  Function         : DataSetDBInfo
				  Purpose          : Set a database's information
				  Scope            : All
				  Input Parameters : dbid
				  version
				  owner
				  backup_date
				  Output Parameters: None
				  Return	    : TRUE
				  ERR_DATA_DB_MISS
				  Comment          : owner = DB_OWNER_NOCHG / version = DB_VER_NOCHG / backup_date = NULL
				  -> not change the value
				  */
				  Err DataSetDBInfo(DatabaseID dbid, USHORT version, AppID owner, RTM *backup_date)
				  {
					  USHORT i;
					  USHORT block;
					  UWORD sblock;
					  DBHeader *hdr;
					  
					  
					  for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
					  {
						  if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
							  break;
					  }
					  if(i>=TOTAL_SLOT)
						  return ERR_DATA_DB_MISS;
					  
					  sblock = MakeSBlock(i, block);
					  
					  hdr = (DBHeader*) SBlockAddr(sblock);
					  
					  if(version != DB_VER_NOCHG)
						  hdr->version = version;
					  
					  if(owner != DB_OWNER_NOCHG)
						  hdr->owner = owner;
					  
					  if(backup_date)
						  hdr->backup_date = _rtm2date32(*backup_date);
					  
					  CAL_CHKSUM
						  return TRUE;
				  }
				  
				  /*
				  Function         : DataDBSize
				  Purpose          : Get the database size
				  Scope            : All
				  Input Parameters : dbid
				  Output Parameters: total_block
				  total_byte
				  Return	    : TRUE
				  ERR_DATA_DB_MISS
				  Comment          :
				  */
				  Err DataDBSize(DatabaseID dbid, USHORT *total_block, UWORD *total_byte)
				  {
					  USHORT i;
					  USHORT block;
					  UWORD sblock;
					  RecordHeader *rec_ptr;
					  DBHeader *db_ptr;
					  
					  for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
					  {
						  if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
							  break;
					  }
					  if(i>=TOTAL_SLOT)
						  return ERR_DATA_DB_MISS;
					  
					  sblock = MakeSBlock(i, block);
					  
					  if(total_block)
						  *total_block = MemoryGroupUsage(&mat_info[i], dbid);
					  
					  if(total_byte == NULL)
						  return TRUE;
					  
					  db_ptr = (DBHeader*) SBlockAddr(sblock);
					  rec_ptr = (RecordHeader*) (SBlockAddr(sblock) + db_ptr->length);
					  
					  *total_byte = db_ptr->length;	/* header size */
					  
					  while(1)
					  {
						  if(rec_ptr->size != DATA_CHUNK_END)
						  {
							  *total_byte = (*total_byte) + rec_ptr->size;
							  rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
						  }
						  if (((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
						  {
							  if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
								  break;
							  sblock = MakeSBlock(SBlockMat(sblock), block);
							  rec_ptr = (RecordHeader *) SBlockAddr(sblock);
						  }
						  else
							  if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
							  {
								  if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
									  break;
								  sblock = MakeSBlock(SBlockMat(sblock), block);
								  rec_ptr = (RecordHeader *) SBlockAddr(sblock);
							  }
					  }
					  return TRUE;
				  }
				  
				  /*
				  Function         : DataTotalField
				  Purpose          : Get total field of a record
				  Scope            : All
				  Input Parameters : dbid
				  rec_id
				  Output Parameters: total_field
				  Return	    : TRUE
				  ERR_DATA_DB_MISS
				  ERR_DATA_INV_RECID
				  Comment          : The pointer can direct access to opendb_table
				  */
				  Err DataTotalField(DatabaseID dbid, RecordID rec_id, USHORT *total_field)
				  {
					  USHORT i;
					  USHORT block;
					  UWORD sblock, start_sblock;
					  Err result;
					  RecordHeader *rec_ptr;
					  
					  for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
					  {
						  if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
							  break;
					  }
					  
					  if(i>=TOTAL_SLOT)
						  return ERR_DATA_DB_MISS;
					  
					  sblock = MakeSBlock(i, block);
					  
					  result = DataRecordBlock(sblock, rec_id, &start_sblock, &rec_ptr);
					  if (result != TRUE)  return result; /* invalid field */
					  
					  *total_field = rec_ptr->total_field;
					  
					  return TRUE;
					  
				  }
				  
				  
				  
				  /*
				  Function         : DataGetDBPtr
				  Purpose          : Get the db pointer and app pointer in opendb_table
				  Scope            : Internal
				  Input Parameters : dbid
				  Output Parameters: dbinfo_ptr, app_ptr
				  Return	    : TRUE
				  FALSE - db not open
				  Comment          : The pointer can direct access to opendb_table
				  */
				  BOOLEAN DataGetDBPtr(DatabaseID dbid, OpenedDBPtr *dbinfo_ptr, AppLnkPtr *app_ptr)
				  {
					  OpenedDBPtr open_db;
					  AppLnkPtr   app_mode;
					  AppID app;
					  
					  app = SysGetActiveAppID();
					  /* examine each DB entry in the table */
					  open_db = opendb_table;
					  while (open_db != NULL)
					  {
						  /* compare the DBID */
						  if (open_db->dbid == dbid)
						  {
							  /* examine each App that open the DB */
							  app_mode = open_db->app_mode;
							  while (app_mode != NULL)
							  {
								  /* compare the AppID with match DBID */
								  if (app_mode->app == app)
								  {
									  if (dbinfo_ptr != NULL)
										  *dbinfo_ptr = open_db;
									  if(app_ptr != NULL)
										  *app_ptr = app_mode;
									  
									  return TRUE;
								  }
								  app_mode = app_mode->next_app;
							  }
						  }
						  open_db = open_db->next_db;
					  }
					  return FALSE;
				  }
				  
				  /*
				  Function         : DataIsRecordChange
				  Purpose          : Check if the record has been modified
				  Scope            : All
				  Input Parameters : dbid
				  rec_id
				  modi
				  Output Parameters: modi
				  Return	    : TRUE
				  FALSE
				  Comment          : The pointer can direct access to opendb_table
				  */
				  BOOLEAN DataIsRecordChange(DatabaseID dbid, RecordID rec_id, BYTE *modi)
				  {
					  RecInfoLnkPtr rec_ptr;
					  
					  if( DataGetRecordPtr(dbid, rec_id, NULL, &rec_ptr, NULL, NULL, NULL) != TRUE)
						  return FALSE;
					  
					  if(*modi != rec_ptr->modi)
					  {
						  *modi = rec_ptr->modi;
						  return FALSE;
					  }
					  return TRUE;
				  }
				  
				  /*
				  Function         : DataGetRecordPtr
				  Purpose          : Get record pointer in opendb_table
				  Scope            : Internal
				  Input Parameters : dbid
				  rec_id
				  Output Parameters: dbinfo_ptr
				  app_ptr
				  rec_ptr
				  app_ptr
				  last_field
				  read_pointer
				  Return	    : TRUE
				  FALSE - record not open
				  Comment          : The pointer can direct access to opendb_table
				  */
				  BOOLEAN DataGetRecordPtr(DatabaseID dbid, RecordID rec_id, OpenedDBPtr *db_ptr,
					  RecInfoLnkPtr *rec_ptr, AppInfoLnkPtr *app_ptr, USHORT *last_field, UWORD *read_pointer)
				  {
					  OpenedDBPtr opendb_lnk;
					  RecInfoLnkPtr rec_lnk;
					  AppInfoLnkPtr app_lnk;
					  AppID app;
					  
					  app = SysGetActiveAppID();
					  /* chk if db opened */
					  if (DataGetDBPtr(dbid, &opendb_lnk, NULL) != TRUE)
						  return FALSE;
					  
					  if (db_ptr != NULL)
						  *db_ptr = opendb_lnk;
					  
					  /* chk if record opened */
					  rec_lnk = opendb_lnk->record;
					  while(rec_lnk != NULL)
					  {
						  if (rec_lnk->rec_id == rec_id)
						  {
							  /* check if record opened by app */
							  app_lnk = rec_lnk->app_info;
							  while (app_lnk != NULL)
							  {
								  /* record find */
								  if (app_lnk->app == app)
								  {
									  if (read_pointer)
										  *read_pointer = app_lnk->last_pos;
									  
									  if (last_field)
										  *last_field = app_lnk->last_field;
									  
									  if (rec_ptr)
										  *rec_ptr = rec_lnk;
									  
									  if (app_ptr)
										  *app_ptr = app_lnk;
									  
									  return TRUE;
								  }
								  app_lnk = app_lnk->next_app;
							  }
							  /* record is opened by other but not this App */
							  return FALSE;
						  }
						  rec_lnk = rec_lnk->next_rec;
					  }
					  return FALSE;
				  }
				  
				  
				  
				  /*
				  Function         : DataIsRecordOpen
				  Purpose          : Check if the record is opened by the app
				  Scope            : All
				  Input Parameters : dbid
				  rec_id
				  Output Parameters: None
				  Return	    : TRUE
				  FALSE - not open
				  Comment          :
				  */
				  BOOLEAN DataIsRecordOpen(DatabaseID dbid, RecordID rec_id, USHORT *last_read_field,
					  UWORD *read_pointer)
				  {
					  OpenedDBPtr opendb_lnk;
					  RecInfoLnkPtr rec_lnk;
					  AppInfoLnkPtr app_lnk;
					  AppID app;
					  
					  app = SysGetActiveAppID();
					  
					  /* chk if db opened */
					  if(DataGetDBPtr(dbid, &opendb_lnk, NULL) != TRUE)
						  return FALSE;
					  
					  /* chk if record opened */
					  rec_lnk = opendb_lnk->record;
					  while(rec_lnk != NULL)
					  {
						  if (rec_lnk->rec_id == rec_id)
						  {
							  /* check if record opened by app */
							  app_lnk = rec_lnk->app_info;
							  while (app_lnk != NULL)
							  {
								  /* record find */
								  if (app_lnk->app == app)
								  {
									  if(last_read_field)
										  *last_read_field = app_lnk->last_field;
									  
									  if(read_pointer)
										  *read_pointer = app_lnk->last_pos;
									  
									  return TRUE;
								  }
								  app_lnk = app_lnk->next_app;
							  }
							  /* record is opened by other but not this App */
							  return FALSE;
						  }
						  rec_lnk = rec_lnk->next_rec;
					  }
					  return FALSE;
				  }
				  
				  /*
				  Function         : DataTotalRecord
				  Purpose          : Get total record in a db
				  Scope            : All
				  Input Parameters : dbid
				  Output Parameters: total_rec
				  Return	    : TRUE
				  ERR_DATA_DB_MISS
				  Comment          :
				  */
				  Err DataTotalRecord(DatabaseID dbid, UWORD *total_rec)
				  {
					  OpenedDBPtr popendb;
					  UWORD sblock;
					  USHORT i, block;
					  RecordHeader *rec_ptr, *old_rec_ptr = NULL;
					  DBHeader *db_ptr;
					  RecordID *record_id;
					  
#ifdef DEBUG
					  printf("\nTotalRec: ");
#endif
					  
					  if(DataGetDBPtr(dbid, &popendb, NULL)==TRUE)
					  {
						  if(popendb->sort_table)  /* sort table exist */
							  if(popendb->sort_table->total_entry)
							  {
								  *total_rec = 0;
								  record_id = popendb->sort_table->rec_id;
								  while(*total_rec < popendb->sort_table->total_entry)
								  {
									  if(record_id[*total_rec] == 0xffffffff)
										  break;                
									  else
										  (*total_rec)++;                 
								  }
								  
								  return TRUE;
							  }
					  }
					  
					  sblock = 0xFFFFFFFF;
					  for(i=0;i<TOTAL_SLOT;i++)
						  if ( (MemoryFindStartBlock(&(mat_info[i]), dbid, &block)) == TRUE)
						  {
							  sblock = MakeSBlock(i, block);
							  break;
						  }
						  
						  if(sblock == 0xFFFFFFFF)
						  {
							  return ERR_DATA_DB_MISS;
						  }
						  
						  db_ptr = (DBHeader*) SBlockAddr(sblock);
						  rec_ptr = (RecordHeader*) (SBlockAddr(sblock) + db_ptr->length);
						  *total_rec = 0;
						  if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && NormRec(rec_ptr))
						  {
							  if( rec_ptr->size > BLOCK_SIZE)
								  rec_ptr->size = 0;
							  else
							  {
#ifdef DEUBG
								  printf("\n%d ", rec_ptr->rec_id);
#endif
								  *total_rec = (*total_rec) + 1;
							  }
						  }
						  
						  while(1)
						  {
							  while(1)
							  {
								  if( (((UWORD)rec_ptr) + rec_ptr->size) < ((((UWORD)rec_ptr) & PTR_ALIGIN_MASK) + BLOCK_SIZE))
								  {
									  rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
									  break;
								  }
								  else
								  {
									  if(rec_ptr->size > BLOCK_SIZE)
										  rec_ptr->size = 0;
									  if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
									  {
										  
										  return TRUE;
									  }
									  sblock = MakeSBlock(SBlockMat(sblock), block);
									  old_rec_ptr = rec_ptr;
									  rec_ptr = (RecordHeader *) SBlockAddr(sblock);
									  if(!(FragRec(rec_ptr)))
										  break;
								  }
							  }
							  
							  if(!(FragRec(rec_ptr)) && (rec_ptr->size != DATA_CHUNK_END) && NormRec(rec_ptr))
							  {
								  if(rec_ptr->size > BLOCK_SIZE)
									  rec_ptr->size = 0;
								  else
								  {
#ifdef DEBUG
									  printf("\n%d ", rec_ptr->rec_id);
#endif
									  *total_rec = (*total_rec) + 1;
								  }
							  }
							  
							  if(((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
							  {
								  if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
								  {
									  
									  return TRUE;
								  }
								  sblock = MakeSBlock(SBlockMat(sblock), block);
								  old_rec_ptr = rec_ptr;
								  rec_ptr = (RecordHeader *) SBlockAddr(sblock);
								  if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && (rec_ptr != old_rec_ptr) && NormRec(rec_ptr))
								  {
									  if( rec_ptr->size > BLOCK_SIZE)
										  rec_ptr->size = 0;
									  else
									  {
#ifdef DEUBG
										  printf("\n%d ", rec_ptr->rec_id);
#endif
										  *total_rec = (*total_rec) + 1;
									  }
								  }
							  }
							  else
								  if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
								  {
									  if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
									  {
										  
										  return TRUE;
									  }
									  sblock = MakeSBlock(SBlockMat(sblock), block);
									  old_rec_ptr = rec_ptr;
									  rec_ptr = (RecordHeader *) SBlockAddr(sblock);
									  if(!FragRec(rec_ptr) && (rec_ptr->size != DATA_CHUNK_END) && (rec_ptr != old_rec_ptr) && NormRec(rec_ptr))
									  {
										  if(rec_ptr->size > BLOCK_SIZE)
											  rec_ptr->size = 0;
										  else
										  {
#ifdef DEBUG
											  printf("\n%d ", rec_ptr->rec_id);
#endif
											  *total_rec = (*total_rec) + 1;
										  }
									  }
								  }
						  }
						  
						  return TRUE;
}


/*
Function         : DataReasignRecID
Purpose          : Ressign record id ( + offset)
Scope            : All
Input Parameters : dbid, offset
Output Parameters: None
Return	    : TRUE
ERR_DATA_DB_MISS
Comment          :
*/
Err DataReasignRecID(DatabaseID dbid, UWORD offset, RecordID exclude_recid)
{
	UWORD sblock;
	USHORT i, block;
	RecordHeader *rec_ptr, *old_rec_ptr = NULL;
	DBHeader *db_ptr;
	RecordID max_recid;
	
	sblock = 0xFFFFFFFF;
	for(i=0;i<TOTAL_SLOT;i++)
		if ( (MemoryFindStartBlock(&(mat_info[i]), dbid, &block)) == TRUE)
		{
			sblock = MakeSBlock(i, block);
			break;
		}
		
		if(sblock == 0xFFFFFFFF)
        {
			return ERR_DATA_DB_MISS;
        }
		
		db_ptr = (DBHeader*) SBlockAddr(sblock);
		
		/*
        max_recid = db_ptr->next_rec_id - 1;
		
		  if(exclude_recid > db_ptr->next_rec_id)
		  max_recid = exclude_recid;
		*/
        max_recid = (exclude_recid > db_ptr->next_rec_id)? exclude_recid :db_ptr->next_rec_id  - 1;
		
        if(max_recid < 1000)
			max_recid = 1000;
        if((max_recid - 1000) <= offset)
			max_recid = 1000 + offset;
		
		rec_ptr = (RecordHeader*) (SBlockAddr(sblock) + db_ptr->length);
        if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
            if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
            {
                
                rec_ptr->rec_id += offset;
                if(rec_ptr->rec_id > max_recid)
					max_recid=rec_ptr->rec_id;
				
			}
			while(1)
			{
				while(1)
				{
					if( (((UWORD)rec_ptr) + rec_ptr->size) < ((((UWORD)rec_ptr) & PTR_ALIGIN_MASK) + BLOCK_SIZE))
					{
						rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
						if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
							if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
							{
								
								
								rec_ptr->rec_id += offset;
								if(rec_ptr->rec_id > max_recid)
									max_recid=rec_ptr->rec_id;
								
								
							}
							break;
					}
					else
					{
						if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
						{
							db_ptr->next_rec_id = max_recid + 1;
							
							return TRUE;
						}
						sblock = MakeSBlock(SBlockMat(sblock), block);
						old_rec_ptr = rec_ptr;
						rec_ptr = (RecordHeader *) SBlockAddr(sblock);
						
						if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
							if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
							{
								
								
								rec_ptr->rec_id += offset;
								if(rec_ptr->rec_id > max_recid)
									max_recid=rec_ptr->rec_id;
								
								
							}
							if(!(FragRec(rec_ptr)))
								break;
					}
				}
				/*
				if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
				if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
				{
                rec_ptr->rec_id += offset;
                if(rec_ptr->rec_id > max_recid)
				max_recid=rec_ptr->rec_id;
				
				  
					}
				*/
				
				if(((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
				{
					if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					{
						db_ptr->next_rec_id = max_recid + 1;
						return TRUE;
					}
					sblock = MakeSBlock(SBlockMat(sblock), block);
					old_rec_ptr = rec_ptr;
					rec_ptr = (RecordHeader *) SBlockAddr(sblock);
					/*
					if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
					{
					rec_ptr->rec_id += offset;
					if(rec_ptr->rec_id > max_recid)
					max_recid=rec_ptr->rec_id;
					
					  
						}
					*/
					
					if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
						if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
						{
							
							
							rec_ptr->rec_id += offset;
							if(rec_ptr->rec_id > max_recid)
								max_recid=rec_ptr->rec_id;
							
							
						}
						
						
				}
				else
					if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
					{
						if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
						{
							db_ptr->next_rec_id = max_recid + 1;
							
							
							return TRUE;
						}
						sblock = MakeSBlock(SBlockMat(sblock), block);
						old_rec_ptr = rec_ptr;
						rec_ptr = (RecordHeader *) SBlockAddr(sblock);
						if((rec_ptr->size != DATA_CHUNK_END) && NormRecID(rec_ptr->rec_id))
							if( (rec_ptr->rec_id < 0x3fffffff) && (rec_ptr->rec_id > exclude_recid))
							{
								
								rec_ptr->rec_id += offset;
								if(rec_ptr->rec_id > max_recid)
									max_recid=rec_ptr->rec_id;
								
								
							}
					}
	}
	
	db_ptr->next_rec_id = max_recid + 1;
	
	
	
	return TRUE;
}


/*
Function         : DataRecIDtoNum
Purpose          : Convert the record id into the entry in sort table
Scope            : All
Input Parameters : dbid
rec_id
Output Parameters: rec_num - index in sort table
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_INV_RECID
Comment          : rec_num only valid if no record delete/insert after calling this function
*/
Err DataRecIDtoNum(DatabaseID dbid, RecordID rec_id, UWORD *rec_num)
{
	OpenedDBPtr opendb_ptr;
	SortTableLnkPtr sort_table;
	AppLnkPtr app_ptr;
	UWORD i;
	RecordID *record_id;
	
	if (DataGetDBPtr(dbid, &opendb_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	sort_table = opendb_ptr->sort_table;
	while(sort_table != NULL)
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		else
			sort_table = sort_table->next_table;
		
		if(!sort_table)
			return ERR_DATA_DB_NOT_OPEN;
		
		record_id = sort_table->rec_id;
		for(i=0;i<sort_table->total_entry;i++)
			if(*record_id++ == rec_id)
			{
				*rec_num = i;
				return TRUE;
			}
			
			return ERR_DATA_INV_RECID;
}

/*
Function         : DataNumtoRecID
Purpose          : Find record id from the position in sort table
Scope            : All
Input Parameters : dbid
rec_num
Output Parameters: rec_id
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_INV_RECNUM
Comment          :
*/
Err DataNumtoRecID(DatabaseID dbid, UWORD rec_num, RecordID *rec_id)
{
	OpenedDBPtr opendb_ptr;
	SortTableLnkPtr sort_table;
	AppLnkPtr app_ptr;
	
	if (DataGetDBPtr(dbid, &opendb_ptr, &app_ptr) != TRUE)
		return ERR_DATA_DB_NOT_OPEN;
	
	sort_table = opendb_ptr->sort_table;
	while(sort_table != NULL)
		if(sort_table->sort_field == app_ptr->sort_field)
			break;
		else
			sort_table = sort_table->next_table;
		
		if(!sort_table)
			return ERR_DATA_DB_NOT_OPEN;
		
		if(rec_num>=sort_table->total_entry)
			return ERR_DATA_INV_RECNUM;
		
		if(sort_table->rec_id[rec_num] == 0xFFFFFFFF)
			return ERR_DATA_INV_RECNUM;
		
		*rec_id = sort_table->rec_id[rec_num];
		return TRUE;
		
}

/*
Function         : DataCategoryNextFree
Purpose          : Find next un-used category number
Scope            : All
Input Parameters : dbid
Output Parameters: next_free
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
ERR_DATA_CAT_FULL
Comment          :
*/
Err DataCategoryNextFree(DatabaseID dbid, UBYTE *next_free)
{
	UBYTE i;
	Err result;
	for(i=1;i<DATA_CAT_MAX;i++)
	{
		if ((result = DataCategoryName(dbid, i, NULL)) == ERR_DATA_CAT_NOT_SET)
		{
			*next_free = i;
			
			return TRUE;
		}
		if (result != TRUE)
			return result;
	}
	
	return ERR_DATA_CAT_FULL;
}


/*
Function         : DataDecodeCatName
Purpose          : Get the category name
Scope            : Internal
Input Parameters : rec_ptr            record pointer of the category record                      
Output Parameters: cat                category name
Return           : TRUE               Success
FALSE              Category name not set
Comment          :
*/
BOOLEAN DataDecodeCatName(RecordHeader *rec_ptr, UBYTE *cat)
{
/* assumption: 1) category name is less than 127 characters
2) category name is terminated by NULL characters
	*/
	UWORD i;
	BYTE *ptr = (BYTE*) rec_ptr;
	
	ptr += RECHEADER_SIZE; /* skip the header */
	if(*ptr++ == 0)        /* skip the size byte */
		return FALSE;  /* cat not set */
	
	/* ptr now point to the category name */
	
	for(i=0;i<DATA_CAT_MAX_LEN;i++)
	{
		if(( cat[i] = *ptr++) == '\0')
			break;
	}
	
	cat[DATA_CAT_MAX_LEN-1] = '\0';  /* assure null terminated */
	
	return TRUE;
}


/*
Function         : DataBuildCatTable
Purpose          : Build category sort table and read the category names
Scope            : All
Input Parameters : db_ptr     - database pointer entry in opendb_table
Output Parameters: None
Return           : None                      
Comment          :
*/
void DataBuildCatTable(OpenedDBPtr open_ptr)
{
	UWORD sblock, c, total_found = 0;
	WORD i, found = 0;
	USHORT block;
	RecordHeader *rec_ptr, *old_rec_ptr = NULL;
	DBHeader *db_ptr;
	UBYTE valid_entry=0;
	UBYTE cat[DATA_CAT_MAX][DATA_CAT_MAX_LEN];
	
	sblock = 0xFFFFFFFF;
	for(i=0;i<TOTAL_SLOT;i++)
		if ( (MemoryFindStartBlock(&(mat_info[i]), open_ptr->dbid, &block)) == TRUE)
		{
			sblock = MakeSBlock(i, block);
			break;
		}
		
		if(sblock == 0xFFFFFFFF)
        {
			return;
        }
		
        if(open_ptr->cat_lnk)
        {
			if(open_ptr->cat_lnk->catname)
				pfree(open_ptr->cat_lnk->catname);
			pfree(open_ptr->cat_lnk);
        }
		
        open_ptr->cat_lnk = (CatInfoLnk*) pmalloc(sizeof(CatInfoLnk));
        open_ptr->cat_lnk->total_entry = 0;
        open_ptr->cat_lnk->catname = NULL;
		
        for(i=0;i<DATA_CAT_MAX;i++)
			cat[i][0] = '\0';
		
		db_ptr = (DBHeader*) SBlockAddr(sblock);
		rec_ptr = (RecordHeader*) (SBlockAddr(sblock) + db_ptr->length);
        if((rec_ptr->size != DATA_CHUNK_END) && CatRecID(rec_ptr->rec_id))
        {
			if( rec_ptr->size > BLOCK_SIZE)
				rec_ptr->size = 0;
			else
				if(DataDecodeCatName(rec_ptr, cat[rec_ptr->rec_id &0x000000ff]))
					total_found++;
        }
		
		while(1)
		{
			while(1)
			{
				if( (((UWORD)rec_ptr) + rec_ptr->size) < ((((UWORD)rec_ptr) & PTR_ALIGIN_MASK) + BLOCK_SIZE))
				{
					rec_ptr = (RecordHeader*)((UWORD)rec_ptr + rec_ptr->size);  /* next record */
					break;
				}
				else
				{
					if(rec_ptr->size > BLOCK_SIZE)
						rec_ptr->size = 0;
					if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					{
						goto exit_loop;
					}
					sblock = MakeSBlock(SBlockMat(sblock), block);
					old_rec_ptr = rec_ptr;
					rec_ptr = (RecordHeader *) SBlockAddr(sblock);
					if(!(FragRec(rec_ptr)))
						break;
				}
			}
			
            if((rec_ptr->size != DATA_CHUNK_END) && CatRecID(rec_ptr->rec_id))
            {
				if(rec_ptr->size > BLOCK_SIZE)
					rec_ptr->size = 0;
				else
					if(DataDecodeCatName(rec_ptr, cat[rec_ptr->rec_id &0x000000ff]))
						total_found++;
            }
			
			if(((UWORD)rec_ptr - SBlockAddr(sblock)) >= (BLOCK_SIZE - RECHEADER_SIZE))  /* next record on next block */
			{
				if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
					goto exit_loop;
				sblock = MakeSBlock(SBlockMat(sblock), block);
				old_rec_ptr = rec_ptr;
				rec_ptr = (RecordHeader *) SBlockAddr(sblock);
				if((rec_ptr->size != DATA_CHUNK_END) && CatRecID(rec_ptr->rec_id))
				{
					if( rec_ptr->size > BLOCK_SIZE)
						rec_ptr->size = 0;
					else
						if(DataDecodeCatName(rec_ptr, cat[rec_ptr->rec_id &0x000000ff]))
							total_found++;
				}
			}
			else
				if(rec_ptr->size == DATA_CHUNK_END) /* end of records in this block, any for next block? */
				{
					if(MemoryNextBlock(&(mat_info[SBlockMat(sblock)]), block, &block) != TRUE)
                        goto exit_loop;
                    sblock = MakeSBlock(SBlockMat(sblock), block);
                    old_rec_ptr = rec_ptr;
                    rec_ptr = (RecordHeader *) SBlockAddr(sblock);
                    if((rec_ptr->size != DATA_CHUNK_END) && CatRecID(rec_ptr->rec_id))
                    {
						if(rec_ptr->size > BLOCK_SIZE)
							rec_ptr->size = 0;
						else
							if(DataDecodeCatName(rec_ptr, cat[rec_ptr->rec_id &0x000000ff]))
								total_found++;
                    }
				}
		}
		
exit_loop:	
        if(!total_found)
			return;
		
        open_ptr->cat_lnk->catname = (CatNameLnk *) pmalloc(sizeof(CatNameLnk) * total_found);
		
        for(i=0;i<total_found;i++)
        {
			((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->cat_num = 0;
			((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->name[0] = 0;
        }
		
		for(c=1;c<DATA_CAT_MAX;c++)  /* cat 0 is not sorted */
		{
			found = 0;
			if (cat[c][0] == '\0')
				continue;
			for(i=0;i<valid_entry;i++)  /* found insertion point */
			{
				if(textstrcmp(cat[c], ((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->name) < 0)
				{
					found = (USHORT)(i+1);
					break;
				}
			}
			
			if(found)
			{
				for(i=valid_entry;i>=found;i--)
				{
					((CatNameLnk*)(open_ptr->cat_lnk->catname + i))->cat_num = ((CatNameLnk*)(open_ptr->cat_lnk->catname + (i-1)))->cat_num;
					strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + i ))->name,(BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + i - 1 ))->name);
				}
				((CatNameLnk*)(open_ptr->cat_lnk->catname + found - 1))->cat_num = c;
				strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + found - 1))->name, (BYTE*) cat[c]);
			}
			else   /* should append at last */
			{
				
				((CatNameLnk*)(open_ptr->cat_lnk->catname + valid_entry))->cat_num = c;
				strcpy((BYTE*)((CatNameLnk*)(open_ptr->cat_lnk->catname + valid_entry))->name, (BYTE*)cat[c]);
			}
			valid_entry++;
        }
		
		open_ptr->cat_lnk->total_entry = valid_entry;
        return;
}

/*
Function         : DataCategorySort
Purpose          : Sort the category names and return number of category in the database
Scope            : All
Input Parameters : dbid
Output Parameters: cat[]      - a 255 byte array
Return	    : TRUE
ERR_DATA_DB_NOT_OPEN
Comment          :
*/
UBYTE DataCategorySort(DatabaseID dbid, UBYTE cat[])
{
	UWORD i;
    OpenedDBPtr db_ptr;
	UBYTE valid_entry=0;
	
	if(!DataGetDBPtr(dbid, &db_ptr, NULL))
		return ERR_DATA_DB_NOT_OPEN;
	
	if(!db_ptr->cat_lnk)  /* category not sorted */
	{
		/* sort the category */
		DataBuildCatTable(db_ptr);
	}
	
	if(db_ptr->cat_lnk)
	{
		if(cat)
		{
			for(i=0;i<db_ptr->cat_lnk->total_entry;i++)
				cat[i] = ((CatNameLnk*)(db_ptr->cat_lnk->catname + i ))->cat_num;
		}
		return (UBYTE) db_ptr->cat_lnk->total_entry;
	}
	else
		return 0;
	
	
#ifdef DATA_OLD_CAT
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i, block);
	
	for(c=1;c<DATA_CAT_MAX;c++)
	{
		found = 0;
		rec_data[0]= '\0';
		strcpy((BYTE*)rec_data, (BYTE*)DataRecordData(sblock, MakeCatRecID(c), 0));
		if (rec_data[0] == '\0')
			continue;
		for(i=0;i<valid_entry;i++)  /* found insertion point */
		{
			if(textstrcmp(rec_data, DataRecordData(sblock, MakeCatRecID(cat[i]), 0)) < 0)
			{
				found = (USHORT)(i + 1);
				break;
			}
		}
		
		if(found)
		{
			for(i=valid_entry;i>=found;i--)
				cat[i] = cat[i-1];
			cat[found-1] = (UBYTE) c;
		}
		else   /* should append at last */
		{
			cat[valid_entry] = (UBYTE) c;
		}
		valid_entry++;
	}
	return valid_entry;
#endif
	
}

void DataClearFieldCache()
{
	d_num_block = 0xffff;
	dbcache.enable = FALSE;
}

void DataUpdateModiDate(RecordHeader *ptr)
{
	RTM ct;
	RtcGetTime(&ct);
	ptr->modi_date = _rtm2date32(ct);
}

#ifdef DEBUG
Err DataChkField(DatabaseID dbid, RecordID rec_id, USHORT field_num)
{
	USHORT i, num_block;
	USHORT block;
	UWORD sblock, fsize, first_offset, last_size, start_sblock;
	AppLnkPtr app_lnk;
	BOOLEAN frag;
	Err result;
	
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	
	if(i>=TOTAL_SLOT)
    {
		return ERR_DATA_DB_MISS;
    }
	
	sblock = MakeSBlock(i, block);
	
    result = DataFieldInfo(sblock, rec_id, field_num, &fsize, &start_sblock, &first_offset,
		&num_block, &last_size, &frag);
	
	printf("\nBlock = %d ", start_sblock);
	printf(" size = %d ", fsize);
	printf(" offset = %d ", first_offset);
	printf(" num block = %d ", num_block);
	printf(" last size = %d ", last_size);
	printf(" frag = %d", frag);
	
	return TRUE;
}
#endif

Err DataGetAllField(DatabaseID dbid, RecordID rec_id, DmFieldArray *field_array)
{
	UBYTE mode;
	UWORD sblock, fsize, start_sblock, first_offset, last_size;
	USHORT block;
	USHORT i, num_block;
	Err result;
	BOOLEAN frag;
	BYTE *buf;
	UWORD rec_seg_offset = 0xffffffff;
	RecordHeader *rec_ptr;
	
	if NormRecID(rec_id)  /* the special record is not in opendb_table */
	{
		
		if(DataIsDBOpen(dbid, &mode, NULL) == FALSE)
			return ERR_DATA_DB_NOT_OPEN;   /* db not open */
		
		if (DataIsRecordOpen(dbid, rec_id, NULL, NULL) == FALSE)
			return ERR_DATA_REC_NOT_OPEN;
		
		if(mode == OPEN_RO)
			return ERR_DATA_DB_RO;	      /* database is open read only */
	}
	
	for(i=0;i<TOTAL_SLOT;i++)     /* find all slot for the db */
	{
		if(MemoryFindStartBlock(&mat_info[i], dbid, &block) == TRUE)
			break;
	}
	if(i>=TOTAL_SLOT)
		return ERR_DATA_DB_MISS;
	
	sblock = MakeSBlock(i,block);
	
	if((DataRecordBlock(sblock, rec_id, &sblock, &rec_ptr))!=TRUE)
		return ERR_DATA_INV_RECID;
	
	if(!(field_array->data_array  = (DmFieldBuffer*) qmalloc(rec_ptr->total_field * sizeof(DmFieldBuffer))))
		return ERR_DATA_NO_MEM;
	
	field_array->total_entry = rec_ptr->total_field;
	
	rec_seg_offset = ((UWORD)rec_ptr & 0x00000fff) + RECHEADER_SIZE;
	
	for(i=0;i<rec_ptr->total_field;i++)
	{
		DataReadSeqField(&rec_ptr, &sblock, &rec_seg_offset, field_array->data_array + i);
	}
	
	return TRUE;
}



Err DataReadSeqField(RecordHeader **rec_ptr, UWORD *sblock, UWORD *rec_seg_offset, DmFieldBuffer *field_array)
{
/*	rec_ptr: record header
sblock : record block number
rec_seg_offset: position of the field size byte
	*/
	
	BYTE d_sp[4];
	BYTE *p, *x;
	UWORD osblock;
	UWORD i, j, field_size, byte_use;
	
	p = (BYTE*) (SBlockAddr(*sblock) + *rec_seg_offset);
	x = p;
	osblock = *sblock;
	for(j=0;j<4;j++)
	{
		d_sp[j] = *x;
		if(( ((UWORD)x) & (UWORD)(~PTR_ALIGIN_MASK)) == (0xfffffff & (UWORD)(~PTR_ALIGIN_MASK)))
			break;
		_valid_ptr(&osblock, &x, 1, NULL);
	}
	
	x = d_sp;
	DeFieldSize(x, field_size, byte_use);	/* get field size and byte used to represent the field size */
	
	_valid_ptr(sblock, &p, byte_use, NULL);
	
	field_array->byte_read = 0;
	
	if(field_size)
	{
		if(x = (BYTE*) field_array->data = (BYTE*) qmalloc(field_size))
		{
			field_array->byte_read = field_size;
			for(i=0;i<field_size;i++)
			{
				*(x++) = *p;
				_valid_ptr(&osblock, &p, 1, *rec_ptr);
			}
		}
	}
	else
		field_array->data = NULL;
	
	*sblock = osblock;
	*rec_seg_offset = (UWORD) p & 0x00000fff;
	
	return TRUE;
}


void DataFreeFieldArray(DmFieldArray field_array) 
{
	UWORD i;
	
	if(field_array.total_entry)
	{
		for(i=0;i<field_array.total_entry;i++)
		{
			{
				if((field_array.data_array[i].data) && field_array.data_array[i].byte_read)
					qfree(field_array.data_array[i].data);
			}
		}
		qfree(field_array.data_array);
	}
}


void DataUpdateSortTableCat(OpenedDBPtr db_ptr, RecordID rec_id, UBYTE cat)
{
	UWORD i;
	SortTableLnkPtr sort_table;
	RecordID *prec_id;
	
	sort_table = db_ptr->sort_table;
	
	while(sort_table)
	{
		prec_id = sort_table->rec_id;
		for(i=0;i<sort_table->total_entry;i++)
			if(prec_id[i] == rec_id)
			{
				sort_table->cat[i] = cat;
				break;		/* exit for */
			}
			sort_table = sort_table->next_table;
	}
}

