/************************************************
* dataBase.c
*
*       This library keeps track of the register
*       items.  It also calls regGadget to
*       update the display accordingly.
*
************************************************/
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/memory.h>
 
#include <clib/intuition_protos.h>
#include <clib/dos_protos.h>
#include <clib/icon_protos.h>
#include <intuition/sghooks.h>
#include <intuition/gadgetclass.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>

#include "regGadget.h"
#include "amiCheck.h"
#include "dataBase.h"
#include "qdatePanel.h"
#include "memoPanel.h"
#include "namePanel.h"
#include "amntPanel.h"
#include "dataBaseFile.h"


extern STRPTR demoptr;

/* globals and special structures */
struct MinList  entries, filtered;
struct List templates,testtemps, memolist, datelist, namelist, amntlist;
struct List categories, sortfields, grouplist;

accntState 	usrAccount;
sortState 	sortStates;

UWORD decimalModulos[] = {
	1,
	10,
	100,
	1000,
	10000,
};

char *decimalFormat[] = {
	"%u",
	"%u.%01u",
	"%u.%02u",
	"%u.%03u",
	"%u.%04u",
};

arexxNode arexxMacros[MAXMACROS] = 
{
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},
{"UUUUUUUUUUUUUUUUUUUU",""},

#if 0
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
{"MMMMMMMMMMMMMMMMMMMM",""},
#endif
};

BOOL  amiChangedItems;
UWORD entryCount;
UWORD currCheckNumber;
dateType currDate;
UWORD currCentury;
UWORD dateFormat = DATE_MMDDYY;

struct stateVars amntState;
balanceType filtAmnt;

sortNode sortEntries[SORTENTRIES];

struct TagItem strAmntTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	/*{STRINGA_Justification, STRINGRIGHT},*/
	{GTST_MaxChars,AMNTSIZE},
	{TAG_DONE},
};

struct TagItem strDateTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTST_MaxChars,DATESIZE},
	{TAG_DONE},
};

struct TagItem strNameTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTST_MaxChars,STRINGSIZE},
	{TAG_DONE},
};

struct TagItem strMemoTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTST_MaxChars,STRINGSIZE},
	{TAG_DONE},
};

struct TagItem strTempTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTST_MaxChars,TEMPNAMESIZE},
	{TAG_DONE},
};

struct TagItem strCatTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTST_MaxChars,CATNAMESIZE},
	{TAG_DONE},
};

struct TagItem strCheckTags[] =
{
	/*{STRINGA_ExitHelp,TRUE},*/
	{GTIN_Number, 1},
	{GTIN_MaxChars,6},
	{TAG_DONE},
};

struct TagItem TxtBorder[] = 
{
	{GTTX_Border, TRUE},
	{GTTX_Text, ""},
	{TAG_DONE},
};

struct TagItem TxtNoBorder[] = 
{
	{GTTX_Border, FALSE},
	{GTTX_Text, ""},
	{TAG_DONE},
};


struct TagItem TxtZero[] =
{
	{GTTX_Border, TRUE},
	{GTTX_Text,"     "},
	{STRINGA_Justification, STRINGRIGHT},
	{TAG_DONE},
};

struct TagItem TxtInt[] =
{
	{GTIN_Number, 0},
	{GTIN_MaxChars,6},
	{TAG_DONE},
};

struct TagItem NumBorder[] =
{
	{GTNM_Border, TRUE},
	{TAG_DONE},
};

struct TagItem ListView[] =
{
        {GTLV_ShowSelected, NULL},
        {TAG_DONE},
};

struct TagItem MiscButton[] =
{
        {ALT_Image, ALTI_GetMisc},
        {TAG_DONE},
};


printControl checkPrint =
{
	5000,TRUE,TRUE,1,0,
};

STRPTR fptlabels[] =
{
    	"080935cour12",
	"081004cour10",
	"081013cour10",
   	"081064cour10",
	"081065cour10",
	"081098cour10",
	"081123cour10",

	"091098cour10",
 	"091064cour10", 
	"090935cour12",
	"Last Used",
	NULL,
};

formControl checkForms = 
{
 "PRT:",

	/* 080935 cour p12*/
	{	TRUE,		/* sheet feed */
		17,3, 		/* lines/check, checks/page */
		36, 35, 4,	/* pay field m/w/l */
		82,11,4, 	/* Date field m/w/l */
		82,10,5,	/* Amount field m/w/l */
		30,38,13,	/* Memo field m/w/l */
		1,3,27,		/* Voucher type, m/w */
		30,60,6, 	/* String field m/w/l */
		3,		/* lines to skip */
        	120             /* cols/page */       
	},

 {
	/* 080935 cour p12*/
	{	TRUE,		/* sheet feed */
		17,3, 		/* lines/check, checks/page */
		36, 35, 4,	/* pay field m/w/l */
		82,11,4, 	/* Date field m/w/l */
		82,10,5,	/* Amount field m/w/l */
		30,38,13,	/* Memo field m/w/l */
		1,3,27,		/* Voucher type, m/w */
		30,60,6, 	/* String field m/w/l */
		3,		/* lines to skip */
        	120             /* cols/page */       
	},

  
	/* 081004 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 55, 12,	/* pay field m/w/l */
		49,11,10, 	/* Date field m/w/l */
		66,9,10,	/* Amount field m/w/l */
		2,40,17,	/* Memo field m/w/l */
		3,0,0,		/* Voucher type, m/w */
		7,65,7, 	/* String field m/w/l */
		3,		/* lines to skip */
        	80              /* cols/page */
	},

	/* 081013 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 45, 13,	/* pay field m/w/l */
		50,11,7, 	/* Date field m/w/l */
		67,9,7,		/* Amount field m/w/l */
		1,40,17,	/* Memo field m/w/l */
		2,0,0,		/* Voucher type, m/w */
		5,70,10, 	/* String field m/w/l */
		3,		/* lines to skip */
        	80              /* cols/page */

	},

      
	/* 081064 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		8, 55, 8,	/* pay field m/w/l */
		66,11,6, 	/* Date field m/w/l */
		66,9,8,		/* Amount field m/w/l */
		6,35,16,	/* Memo field m/w/l */
		3,0,0,		/* Voucher type, m/w */
		1,70,10, 	/* String field m/w/l */
		3,		/* lines to skip */
        	80              /* cols/page */
	},

	/* 081065 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 55, 12,	/* pay field m/w/l */
		49,11,6, 	/* Date field m/w/l */
		66,9,6,		/* Amount field m/w/l */
		2,40,17,	/* Memo field m/w/l */
		3,0,0,		/* Voucher type, m/w */
		5,65,8, 	/* String field m/w/l */
		3,		/* lines to skip */
        	80              /* cols/page */
	},

	/* 081098 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 45, 14,	/* pay field m/w/l */
		55,11,6, 	/* Date field m/w/l */
		67,9,6,		/* Amount field m/w/l */
		1,40,17,	/* Memo field m/w/l */
		2,0,0,		/* Voucher type, m/w */
		5,70,10, 	/* String field m/w/l */
		3,		/* lines to skip */
 		80              /* cols/page */      
	},


	/* 081123 cour p10*/
	{	TRUE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 50, 14,	/* pay field m/w/l */
		66,11,5, 	/* Date field m/w/l */
		66,9,8,		/* Amount field m/w/l */
		1,40,17,	/* Memo field m/w/l */
		3,0,0,		/* Voucher type, m/w */
		5,65,9, 	/* String field m/w/l */
		3,		/* lines to skip */
        	80              /* cols/page */
	},


	/* * * line printers * * */

 	/* 091098 cour p10*/
	{	FALSE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		5, 50, 14,	/* pay field m/w/l */
		53,11,6, 	/* Date field m/w/l */
		66,9,6,		/* Amount field m/w/l */
		2,40,17,	/* Memo field m/w/l */
		4,0,0,		/* Voucher type, m/w */
		5,65,10, 	/* String field m/w/l */
		2,		/* lines to skip */
        	80              /* cols/page */
	},

 	/* 091064 cour p10*/
	{	FALSE,		/* sheet feed */
		21,1, 		/* lines/check, checks/page */
		7, 55, 8,	/* pay field m/w/l */
		66,11,5, 	/* Date field m/w/l */
		66,12,8,	/* Amount field m/w/l */
		4,35,17,	/* Memo field m/w/l */
		0,0,0,		/* Voucher type, m/w */
		1,65,10, 	/* String field m/w/l */
		2,		/* lines to skip */
        	80              /* cols/page */
	},
 	/* 090935 cour p12*/
	{	FALSE,		/* sheet feed */
		17,1, 		/* lines/check, checks/page */
		36, 35, 4,	/* pay field m/w/l */
		83,11,4, 	/* Date field m/w/l */
		83,10,5,	/* Amount field m/w/l */
		28,30,13,	/* Memo field m/w/l */
		1,3,27,		/* Voucher type, m/w */
		30,60,6, 	/* String field m/w/l */
		3,		/* lines to skip */
        	95              /* cols/page */
	},
 }
};

char daysinmonth[12] = {31,29,31,30,31,30,31,31,30,31,30,31};
char easymonths[12][4] = {
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec",
};


reportSetting reportControl;
searchState searchControl;
filterSetting filterControl;

/*
 * prototypes
 */
BOOL DataLeapYear(LONG);
BOOL DataStrSet(char *, char *);
void DataSortInit(void);
BOOL DataSortLevel(filterNode *, filterNode **, filterNode **, sortNode *);
void DataReplaceEntryCats(struct List *);
void DataReplaceTempCats(struct List *);
editCatNode *DataLocateCat(struct List *, char *);
BOOL DataSearchMatch(filterNode *, searchState *);
void DataInitBalances(void);
BOOL DataDateRange(entryNode *, filterSetting *);
int DataWriteEntry(int,entryNode *);
void DataExtractDate(dateType *, char *out);
void DataCreateDate(dateType *, char *out, BOOL year);
BOOL DataVerifyDateDec12(char *instr, char *newstr);
BOOL DataVerifyDate12Dec(char *instr, char *newstr);
BOOL DataVerifyDateMMDDYY(char *instr, char *newstr);
BOOL DataVerifyDateDDMMYY(char *instr, char *newstr);
int DataWriteTemplate(int fd, templateNode *temp);


/****************************************************
* DataListInit()
*
*  Initialize lists
****************************************************/
void DataListInit(void)
{
 NewList(&grouplist);
 NewList((struct List *)&entries);
 NewList((struct List *)&filtered);
 NewList(&templates);
 NewList(&categories);
 NewList(&testtemps);
 NewList(&sortfields);
 NewList(&memolist);
 NewList(&datelist);
 NewList(&namelist);
 NewList(&amntlist);
}

/****************************************************
* DataInit()
*
*	Initialize the lists, create default templates
*****************************************************/
BOOL DataInit(void)
{
 time_t t;
 struct tm *pt;
 templateNode temp;
 categoryNode cat;
 amountType defamount = 0;
 int x;

 /*
  * install default templates
  */
 memset(&temp, 0, sizeof(templateNode));

 strcpy(temp.tempname,"Check"); 
 strcpy(temp.template.category,"None");
 temp.template.type=CHECKTYPE; 
 DataAddTemp(&templates, &temp); 
 
 strcpy(temp.tempname,"Withdrawal"); 
 strcpy(temp.template.category,"None");
 temp.template.type=WITHDRAWALTYPE; 
 DataAddTemp(&templates, &temp);
 
 strcpy(temp.tempname,"Deposit");
 strcpy(temp.template.category,"None");
 temp.template.type=DEPOSITTYPE; 
 DataAddTemp(&templates,&temp); 

 strcpy(temp.tempname,"Script");
 strcpy(temp.template.category,"None");
 temp.template.type=SCRIPTTYPE; 
 DataAddTemp(&templates, &temp); 

 /*
  * install default categories
  */
 memset(&cat, 0, sizeof(categoryNode));

 strcpy(cat.catname,"None");
 DataAddCat(&categories,&cat);

 /*
  * other globals
  */
 amntState.currTemplate = 0;
 amntState.currCheckNumber = 1;

 /* get century */
 time (&t);
 pt=localtime(&t);
 currDate.day = pt->tm_mday;
 currDate.month = pt->tm_mon + 1;
 currDate.year = pt->tm_year + 1900;
 currCentury = 1900 + (pt->tm_year/100);

 QDateAdd(&currDate);

 entryCount = 0;

 /* initialize filter to all */
 memset(&filterControl,0,sizeof(filterSetting));
 filterControl.checks=TRUE;
 filterControl.withdrawals=TRUE;
 filterControl.deposits=TRUE;
 filterControl.checkfrom = filterControl.checkto = 1;
 memcpy(&filterControl.dateFrom,&currDate,sizeof(dateType));
 memcpy(&filterControl.dateTo,&currDate,sizeof(dateType));

 strcpy(filterControl.category,"None");

 /* initialize sort array */
 DataSortInit();
 memset(&sortStates,0,sizeof(sortState));

 /* initialize account */
 memset(&usrAccount,0,sizeof(accntState));
 usrAccount.checknumber = amntState.currCheckNumber;
 memcpy(&usrAccount.openDate,&currDate,sizeof(dateType));
 memcpy(&usrAccount.closeDate,&currDate,sizeof(dateType));
 usrAccount.decimal = 2;

 /* initalize balance */
 DataInitBal(DEPOSITTYPE,&defamount,&amntState.currAmnt);
 DataInitBal(DEPOSITTYPE,&defamount,&amntState.stateAmnt);
 DataInitBal(DEPOSITTYPE,&defamount,&filtAmnt);

 /* initialize search control */
 memset(&searchControl,0,sizeof(searchState));
 memcpy(&searchControl.searchdate,&currDate,sizeof(dateType));
 searchControl.lastrow = -1;
 searchControl.checknum = 1;

 memset(&reportControl,0,sizeof(reportSetting));
 reportControl.rows = 66;
 reportControl.cols = 80;


 for (x=0;x<MAXMACROS;x++) {
	memset(arexxMacros[x].macroname,0,20+1);
	sprintf(arexxMacros[x].macroname,"<unassigned %d>",x+1);
	arexxMacros[x].macrofile[0]=0;
 }

 amiChangedItems = FALSE;
 return (TRUE);
}

/***************************************************
* DataNode2Ord
*
* 	Returns ordinal entry in list by name
*
****************************************************/
LONG DataNode2Ord (struct List *list, char *name)
{
 struct Node *result;
 struct Node *temp;
 LONG ord = 0;

 result = DataFindName(list,name);

 if (result == NULL) 
	return (0);

 temp = list->lh_Head;
 while (temp != result) {
	ord++;
	temp = temp->ln_Succ;
 }

 return (ord);
}

/***************************************************
* DataOrd2Node
*
* 	Returns Node ptr from ordinal entry
****************************************************/
struct Node *DataOrd2Node(struct List *list,LONG ord)
{
 struct Node *temp = list->lh_Head;
 LONG myord = 0;

 while (temp && myord != ord) {
	myord++;
	temp = temp->ln_Succ;
 }
 
 return (temp);
}

/***************************************************
* DataAddCat()
*
*	Adds a category
****************************************************/
BOOL DataAddCat(struct List *categories, categoryNode *info )
{
 categoryNode *temp;

	if ((temp = AllocVec(sizeof(categoryNode),MEMF_CLEAR))){

		memcpy((void *)temp,(void *)info,sizeof(categoryNode));
		temp->node.ln_Type = NT_USER;
		temp->node.ln_Pri = 0;
		temp->node.ln_Name = temp->catname;

		AddTail(categories,(struct Node *)temp);
	}
	else return (FALSE);

 return (TRUE);
}

/***************************************************
* DataTestFilter()
*
*	Determine if node belongs to filter set
****************************************************/
BOOL DataTestFilter(entryNode *en, filterSetting *set)
{
 BOOL retval = TRUE;

 /* type check - returns quickly if it can */
 switch (en->type) {
	case CHECKTYPE:
		if (set->checks != TRUE) return (FALSE);
		break;

	case WITHDRAWALTYPE:
		if (set->withdrawals != TRUE) return(FALSE);
		break;

	case DEPOSITTYPE:
		if (set->deposits != TRUE) return(FALSE);
		break;
 }

 /* check range check */
 if (en->type == CHECKTYPE) {
 	if (set->checkrange == FILTERIN) {
		if (en->check < set->checkfrom || en->check > set->checkto)
		return (FALSE);
		
 	}
 	else  if (set->checkrange == FILTEROUT) {
		if (en->check >= set->checkfrom && en->check <= set->checkto)
		return (FALSE);
	}
 }
 
 /* amount range check */
 if (set->amntrange == FILTERIN) {
	if (en->amount < set->amountFrom || en->amount > set->amountTo) 
		return (FALSE);
 }
 else if (set->amntrange == FILTEROUT) {
	if (en->amount >= set->amountFrom && en->amount <= set->amountTo)
		return (FALSE);
 }

 /* date range check */
 if (set->daterange == FILTERIN && DataDateRange(en,set) == FALSE)
	return (FALSE);

 else if (set->daterange == FILTEROUT && DataDateRange(en,set) == TRUE) 
	return (FALSE);


 /* category check */
 if (set->usecategory == FILTERIN) {
	if (strcmpi(set->category, en->category) != 0)
		return (FALSE);
 }
 else if (set->usecategory == FILTEROUT) {
	if (strcmpi(set->category, en->category) == 0)
		return (FALSE);
 }
	

 /* set operations */
 if ( (en->flags & set->andflag) != set->andflag)
	return (FALSE);

 if ( (en->flags & set->notflag) != 0)
	return (FALSE);

 /* payee check  */
 if (set->usepayee == FILTERIN ) {
	if ( !MatchPatternNoCase(set->payeetoken, en->name) )
		return (FALSE);
 }
 else if (set->usepayee == FILTEROUT) {
	if (MatchPatternNoCase(set->payeetoken,en->name) )
		return (FALSE);
 }

 /* memo check */
 if (set->usememo == FILTERIN) {
	if (!MatchPatternNoCase(set->memotoken, en->memo) )
		return (FALSE);
 }
 else if (set->usememo == FILTEROUT) {
	if (MatchPatternNoCase(set->memotoken,en->memo) )
		return (FALSE);
 }

  return (retval);
}

/*************************************************
* DataDateRange()
*
*	Range for dates
**************************************************/
BOOL DataDateRange(entryNode *en, filterSetting *set)
{
/* check years */
if( en->date.year < set->dateFrom.year || en->date.year > set->dateTo.year)
	return (FALSE);

/* check months */
if (en->date.year == set->dateFrom.year && en->date.month < set->dateFrom.month)
	return (FALSE);

if (en->date.year == set->dateTo.year && en->date.month > set->dateTo.month)
	return (FALSE);

/* check days */
if (en->date.year == set->dateFrom.year && en->date.month == set->dateFrom.month &&
    en->date.day < set->dateFrom.day)
	return (FALSE);

if (en->date.year == set->dateTo.year && en->date.month == set->dateTo.month &&
    en->date.day > set->dateTo.day)
	return (FALSE);

return (TRUE);
}

/***************************************************
* DataRemoveEntry()
*
*	removes an entry from the list
****************************************************/
void DataRemoveEntry(entryNode *info, filterNode *filt)
{
 DataRemoveFilter(filt);
 Remove((struct Node *)info);
 FreeVec(info);
}

/***************************************************
* DataRemoveFilter()
*
* Removes supplied filter 
***************************************************/
void DataRemoveFilter(filterNode *filt)
{
 if (filt == NULL)
	return;

 entryCount--;
 Remove((struct Node *)filt);
 FreeVec(filt);
}

/***************************************************
* DataRemoveEntryFilter()
*
*	Removes a filter based on the node 
****************************************************/
void DataRemoveEntryFilter(entryNode *node)
{
 filterNode *filt = (filterNode *)filtered.mlh_Head;

 /* is this an empty list? */
 if (filtered.mlh_TailPred == &filtered)
	return;

 /* locate filter */
 while (filt->node.mln_Succ != NULL) {
	if (filt->entry == node) {
		DataRemoveFilter(filt);
		break;
	}

	filt = (filterNode *)filt->node.mln_Succ;
 }

}

/***************************************************
* DataInsertFilter()
*
*	Add a filter node
****************************************************/
BOOL DataInsertFilter(filterNode *fparent,filterNode **ret, 
			entryNode *join, struct MinList *filtered)
{
 int x;
 filterNode *temp;

 *ret = NULL;

	if ((temp = AllocVec(sizeof(filterNode),MEMF_CLEAR))){

		temp->entry = join;
	
		for (x=0;x<4;x++)
			temp->printChars[x]= -1;	

		Insert((struct List *)filtered,(struct Node *)temp, (struct Node *)fparent);		
	}
	else return (FALSE);

 *ret = temp;
 return (TRUE);
}

/***************************************************
* DataInsertEntry()
*
*	Add a transaction to proper place
****************************************************/
BOOL DataInsertEntry(filterNode *fparent, entryNode *eparent,
	entryNode *info, struct MinList *entrylist, filterNode **filt, 
	entryNode **entry)
{
  entryNode *temp;

  *filt = NULL;
  *entry = NULL;

	if ((temp = AllocVec(sizeof(entryNode),MEMF_CLEAR))){

		memcpy((void *)temp,(void *)info,sizeof(entryNode));
		Insert((struct List *)entrylist,(struct Node *)temp, 
			(struct Node *)eparent);
		*entry = temp;
	}
	else return (FALSE);

 /* filter it */
 if (DataTestFilter(temp, &filterControl) == TRUE)
	DataInsertFilter(fparent,filt,temp,&filtered);

 return (TRUE);
}


/***************************************************
* DataAddEntry()
*
*	Add a transaction
****************************************************/
BOOL DataAddEntry(entryNode *info, struct MinList *entrylist, filterNode **filt)
{
  entryNode *temp;

 *filt = NULL;

 if ((temp = AllocVec(sizeof(entryNode),MEMF_CLEAR))){

	memcpy((void *)temp,(void *)info,sizeof(entryNode));
	AddTail((struct List *)entrylist,(struct Node *)temp);		
 }
 else return (FALSE);

 /* filter it */
 if (DataTestFilter(temp, &filterControl) == TRUE)
	DataAddFilter(filt,temp,&filtered);

 return (TRUE);

}

/***************************************************
* DataAddTemp()
*
*	Adds a template
****************************************************/
BOOL DataAddTemp(struct List *templist, templateNode *info)
{
 templateNode *temp;

	if ((temp = AllocVec(sizeof(templateNode),MEMF_CLEAR))){

		memcpy((void *)temp,(void *)info,sizeof(templateNode));
		temp->node.ln_Type = NT_USER;
		temp->node.ln_Pri = 0;
		temp->node.ln_Name = temp->tempname;
		AddTail(templist,(struct Node *)temp);		
	}
	else return (FALSE);

 return (TRUE);
}

/***************************************************
* DataAddFilter()
*
*	Add a filter node
****************************************************/
BOOL DataAddFilter(filterNode **ret, entryNode *join, struct MinList *filtered)
{
 int x;
 filterNode *temp;

 *ret = NULL;

	if ((temp = AllocVec(sizeof(filterNode),MEMF_CLEAR))){

		temp->entry = join;
	
		for (x=0;x<4;x++)
			temp->printChars[x]= -1;	

		AddTail((struct List *)filtered,(struct Node *)temp);		
	}
	else return (FALSE);

 *ret = temp;
 return (TRUE);

}

/*****************************************************
* DataFreeAll()
*
*	Free all database allocations
*****************************************************/
void DataFreeAll(void)
{
  DataFreeList((struct List *)&entries);
  DataFreeList((struct List *)&templates);
  DataFreeList((struct List *)&filtered);
  DataFreeList((struct List *)&categories);
  DataFreeGroupCats((struct List *)&grouplist);
  DataFreeList((struct List *)&grouplist);
  DataFreeList((struct List *)&datelist);
  DataFreeList((struct List *)&memolist);
  DataFreeList((struct List *)&namelist);
  DataFreeList((struct List *)&amntlist);
}

/****************************************************
* DataStoreAmnt()
*
*	Takes the user string and stores it in a template
*****************************************************/
void DataStoreAmnt(amountType *amount, char *str)
{
 ULONG d=0,c=0;

 if (*str == NULL)
	*amount = 0;
 else {
 	/* later we will add "DataExtractAmount" */
	if (usrAccount.decimal)
 		sscanf(str,"%u.%u",&d,&c);
	else	sscanf(str,"%u",&d);

 	*amount = (d*decimalModulos[usrAccount.decimal]) + c;
 }
}

/***************************************************
* DataStoreDate()
*
*	Stores date into string
****************************************************/
void DataStoreDate (dateType *date, char *str)
{
 DataExtractDate(date,str);
}

/****************************************************
* DataBuildRegDate()
*
*	Builds the reduced date for the register
*****************************************************/
void DataBuildRegDate(dateType *date, char *str)
{
 DataCreateDate(date,str,TRUE);
}

/****************************************************
* DataBuildDate()
*
*	Builds string from node of date
*****************************************************/
void DataBuildDate (dateType *date, char *str)
{ 
 DataCreateDate(date,str,FALSE);
}

/****************************************************
* DataBuildAmnt()
*
*	Routine to build a string from amnt
*****************************************************/
void DataBuildAmnt(BOOL zero, amountType *amount, char *str)
{
 if (zero != TRUE && *amount == 0)
	str[0]=0;
 else {
	sprintf(str,
		decimalFormat[usrAccount.decimal],
		(*amount/decimalModulos[usrAccount.decimal]),
		(*amount%decimalModulos[usrAccount.decimal]) 
		);
 }
}

/****************************************************
* DataBuildBal()
*
*	Routine to build a string from balance
*****************************************************/
void DataBuildBal(UBYTE decimal, balanceType *bal, char *str)
{
char negval = ' ';

 if (bal->neg) 
	negval = '-';
	
 sprintf(str,"%c",negval);
 str++;
	
 /* again, eventually we will do a createamount */
 sprintf(str,decimalFormat[decimal],bal->dollar,bal->cent);
}

/*******************************************************
* DataMoveList()
*
*	Accept new list as new original list.
********************************************************/
void DataMoveList(struct List *src, struct List *dest)
{
 DataFreeList(dest);
 memcpy(dest,src,sizeof(struct List));
 
 dest->lh_Head->ln_Pred = (struct Node *)&(dest->lh_Head);
 dest->lh_TailPred->ln_Succ = (struct Node *)&(dest->lh_Tail);

 /* zap out original */
 NewList(src);
}

/*******************************************************
* DataIsUnique()
*
*	Takes any list and replies if an entry is
*	unique by name.  Boolean option opens
*	requestor notifying user with custon strings.
********************************************************/
BOOL DataIsUnique(GUIFront *gui, struct List *lst, char *name, BOOL req,
		char *reqstr)
{
 struct Window *win = NULL;
 struct EasyStruct es;

 if (strlen(name) > 0 && DataFindName(lst,name) == NULL)
	return (TRUE);

 /* oh no, notify user */
 if (req == TRUE) {
	GF_GetGUIAttr(gui,
		GUI_Window,&win,
		TAG_DONE);

	DisplayBeep(win->WScreen);
	if (win != NULL) {
		es.es_StructSize = sizeof(struct EasyStruct);
		es.es_Flags = 0;
		es.es_Title = "amiCheck NOTICE:";
		es.es_TextFormat = reqstr;
		es.es_GadgetFormat = "OK";
		GF_LockGUI(gui);
		GF_EasyRequest(gui,win,&es,NULL);
		GF_UnlockGUI(gui);
	}
 }
 
 return (FALSE);
}

/*****************************************************************
* DataVerifyAmnt()
*
* 	Force valid entry of amount strings
******************************************************************/
BOOL DataVerifyAmnt(char *instr,char *newstr)
{
amountType amnt = 0;
char *dot1, *dot2, *str, *skip;
ULONG x=0,y=0,temp;
char amnthold[AMNTSIZE+1];

 *newstr = NULL;
 amnthold[AMNTSIZE]=NULL;
 strcpy(amnthold,instr);
 str = amnthold;

 /* something odd in there? */
 if (str == NULL)
	return (FALSE);

 /* clip spaces off front and end */
 while (*str == ' ') str++;
 skip = &amnthold[strlen(amnthold) - 1];
 while (*skip == ' ' && skip != str) {
	*skip = NULL;
	skip--;
 }

 if (DataStrSet(str,"0123456789. ") == FALSE) {
		return (FALSE);
 }

 /* grab decimal point if one */
 dot1 = strchr(str,'.');
 if (dot1 != NULL) {

	/* more than two decimal places? */
	dot2 = dot1+1;
	temp = strlen(dot2);
	if (temp > usrAccount.decimal) 
		return (FALSE);
	
	/* pull out data into integers */
	if (dot1 == str) 
	     sscanf(str,".%u",&y);
	else sscanf(str,"%u.%u",&x,&y);

	/* adjust for ".5 -> .50" */
	if (temp < usrAccount.decimal)
		y *= decimalModulos[usrAccount.decimal-temp];

	/* another period? */
	dot2 = strchr(dot2,'.');
	if (dot2 != NULL) {
		return (FALSE);
	}
 }
 else sscanf(str,"%u",&x);

 /* build new string */
 amnt = (x*decimalModulos[usrAccount.decimal]) + y;
 DataBuildAmnt(FALSE,&amnt,newstr);
 
 return (TRUE);
}

/****************************************************************
* DataVerifyDate()
*
*	Verifies and builds date string
****************************************************************/
BOOL DataVerifyDate(char *instr, char *newstr)
{
BOOL retval;

switch (dateFormat) {
	case DATE_MMDDYY:
		retval = DataVerifyDateMMDDYY(instr,newstr);
		break;
	case DATE_DDMMYY:
		retval = DataVerifyDateDDMMYY(instr,newstr);
		break;
	case DATE_DEC_12:
		retval = DataVerifyDateDec12(instr,newstr);
		break;
	case DATE_12_DEC:
		retval = DataVerifyDate12Dec(instr,newstr);
		break;
}

return (retval);
}

/********************************************************************
* DataLeapYear()
*
* 	TRUE if year is leap year
*********************************************************************/
BOOL DataLeapYear(LONG year)
{

 /* quick /4 check for now */
 if ( year%4 || !(year%100) )
	return (FALSE);

 return (TRUE);
}


/*******************************************************************
* DataAnnounce()
*
*	Announce message to user
*******************************************************************/
void DataAnnounce(GUIFront *gui, char *str)
{
 struct Window *win = NULL;
 struct EasyStruct es;

 if (str == NULL)
	return;

 /* oh no, notify user */
 GF_GetGUIAttr(gui,
	GUI_Window,&win,
	TAG_DONE);

 if (win != NULL) {
	es.es_StructSize = sizeof(struct EasyStruct);
	es.es_Flags = 0;
	es.es_Title = "amiCheck NOTICE:";
	es.es_TextFormat = str;
	es.es_GadgetFormat = "OK";
	GF_LockGUI(gui);
	GF_EasyRequest(gui,win,&es,NULL);
	GF_UnlockGUI(gui);
 }
}

/*******************************************************************
* DataQuery()
*
*	Query from  user
*******************************************************************/
int DataQuery(GUIFront *gui, char *str, char *query)
{
 struct Window *win = NULL;
 struct EasyStruct es;
 int val = 0;

 if (str == NULL)
	return (val);

 /* oh no, notify user */
 GF_GetGUIAttr(gui,
	GUI_Window,&win,
	TAG_DONE);

 if (win != NULL) {
	es.es_StructSize = sizeof(struct EasyStruct);
	es.es_Flags = 0;
	es.es_Title = "amiCheck NOTICE:";
	es.es_TextFormat = str;
	es.es_GadgetFormat = query;
	GF_LockGUI(gui);
	val = GF_EasyRequest(gui,win,&es,NULL);
	GF_UnlockGUI(gui);
 }

 return (val);
}

/**************************************************************
* DataStrSet ()
*
*	SAS/C strcspn replacement
**************************************************************/
BOOL DataStrSet(char *src, char *input)
{
 WORD a,b;
 BOOL found;
 
 for (a=0;a<strlen(src);a++) {
	found = FALSE;
	for (b=0;b<strlen(input);b++) {
		if (src[a] == input[b]) {
			found = TRUE;
			break;
		}
	}
	if (found == FALSE)
		return (FALSE);
 }

 return (TRUE);
}

/********************************************************
* DataUpdateFilters()
*
*	Updates the filtered items to the filter criteria
*	(not all transactions, for SPEED :)
********************************************************/
void DataUpdateFilters(struct MinList *filtered)
{
 filterNode *work, *next;

 if ((struct Node *)(filtered->mlh_TailPred) == (struct Node *)filtered)
	return;

 work = (filterNode *)(filtered->mlh_Head);
 while (next = (filterNode *)(work->node.mln_Succ)) {
	if (!DataTestFilter(work->entry,&filterControl)) 
			DataRemoveFilter(work);
	
	work = next;
 } 
}

/********************************************************
* DataBalConvert()
*
*	Converts a balance from one decimal format to
*	another (some data loss possible)
*********************************************************/
void DataBalConvert(balanceType *bal, UBYTE from, UBYTE to)
{
 UWORD diff;

 if (from == to)
	return;

 if (from > to) {
	diff = decimalModulos[from - to];
	bal->cent /= diff;
 }
 else	{
	diff = decimalModulos[to-from];
	bal->cent *= diff;
 }
}

/********************************************************
* DataBalToAmnt()
*
*	Converts a balance to standard amount type
*********************************************************/
void DataBalToAmnt(balanceType *bal, UBYTE *type, amountType *amount)
{
 if (bal->neg)
	*type = WITHDRAWALTYPE;
 else	*type = DEPOSITTYPE;

 *amount = (bal->dollar * decimalModulos[usrAccount.decimal]) +
		bal->cent;
}

/********************************************************
* DataUpdateBal()
*
*	Updates a balance, supply string. Decimal assumed
*	from account setting
*********************************************************/
void DataUpdateBal(UBYTE type, amountType *amount, balanceType *bal, char *str)
{
 DataAddBal(type, amount, bal);
 DataBuildBal(usrAccount.decimal,bal,str);
}

/********************************************************
* DataAddBal()
*
*	Updates a balance. Decimal assumed
*	from account setting
*********************************************************/
void DataAddBal(UBYTE type, amountType *amount, balanceType *bal)
{
 balanceType amntBal;

 DataInitBal(type, amount, &amntBal);
 DataBalAddBal(usrAccount.decimal,bal,&amntBal,bal);
}

/********************************************************
* DataBalAddBal()
*
*	Adds two balances.
*********************************************************/
void DataBalAddBal(UBYTE decimal,balanceType *dest, 
	balanceType *bal1, balanceType *bal2)
{
 balanceType b1, b2;
 memcpy(&b1,bal1,sizeof(balanceType));
 memcpy(&b2,bal2, sizeof(balanceType));

 /* now we need to look at the two balances efficiently */

 /* - + + */
 if (b2.neg && !b1.neg) {
	/* bal is greater or equal to */
	if ( (b1.dollar > b2.dollar) ||
	     (b1.dollar == b2.dollar && b1.cent >= b2.cent)) {
			
		/* borrow if needed */
		if (b1.cent < b2.cent) {
			b1.dollar--;
			b1.cent += decimalModulos[decimal];
		}
		b1.cent -= b2.cent;
		b1.dollar -= b2.dollar;
	} 
	/* bal just shrunk! */
	else {
		b1.neg = TRUE;

		/* borrow if needed */
		if (b2.cent < b1.cent) {
			b2.dollar--;
			b2.cent += decimalModulos[decimal];
		}
		b1.cent = b2.cent - b1.cent;
		b1.dollar = b2.dollar - b1.dollar;
	}
 }

 /* + + - */
 else if (b1.neg && !b2.neg) {
	/* bal is breater or equal to */
	if ( (b1.dollar > b2.dollar) ||
	     (b1.dollar == b2.dollar && b1.cent >= b2.cent)) {
			
		/* borrow if needed */
		if (b1.cent < b2.cent) {
			b1.dollar--;
			b1.cent += decimalModulos[decimal];
		}
		b1.cent -= b2.cent;
		b1.dollar -= b2.dollar;
	} 
	/* bal just shrunk! */
	else {
		b1.neg = FALSE;

		/* borrow if needed */
		if (b2.cent < b1.cent) {
			b2.dollar--;
			b2.cent += decimalModulos[decimal];
		}
		b1.cent = b2.cent - b1.cent;
		b1.dollar = b2.dollar - b1.dollar;
	}
 }

 /* + + +  or - + - */
 else {
	b1.dollar += b2.dollar;
	b1.cent += b2.cent;
	if (b1.cent >= decimalModulos[decimal]) {
		b1.dollar++;
		b1.cent -= decimalModulos[decimal];
	}
 } 

 memcpy(dest,&b1,sizeof(balanceType));
 if (dest->dollar == 0 && dest->cent == 0)
	dest->neg = FALSE;
}

/********************************************************
* DataCmpAmntBal()
*
*	Compares an amount and a balance. -1 if amnt is 
*	smaller, 0 if equal and 1 if larger.  The negative
*	aspect is currently ignored.
*********************************************************/
int DataCmpAmntBal(amountType *amnt, balanceType *bal)
{
 balanceType store;

 DataInitBal(DEPOSITTYPE,amnt,&store);
 return (DataCmpBal(FALSE,&store,bal));
}

/********************************************************
* DataCmpBal()
*
*	Compares two balances. -1 if b1 is smaller, 0
*	if equal and 1 if larger.
*********************************************************/
int DataCmpBal(BOOL neg, balanceType *b1,  balanceType *b2)
{

 /* do we consider the negative aspect? */
 if (neg) {
	if (b1->neg && !b2->neg)
		return -1;
	else if (!b1->neg && b2->neg)
		return 1;
	else if (b1->dollar == b2->dollar && b1->cent == b2->cent)
		return 0;
	else if ( (b1->dollar > b2->dollar) || 
	     (b1->dollar == b2->dollar && b1->cent > b2->cent))
		return -1;
	else return 1;
	
 }
 else {
	if (b1->dollar == b2->dollar && b1->cent == b2->cent)
		return 0;
	else if ( (b1->dollar > b2->dollar) || 
	     (b1->dollar == b2->dollar && b1->cent > b2->cent))
		return 1;
	else return -1;
 }
}

/********************************************************
* DataInitBal()
*
*	Inits a balance with an amount.  The decimal
*	is assumed to be the current account's
*********************************************************/
void DataInitBal(UBYTE type, amountType *amount, balanceType *bal)
{

 bal->dollar = *amount / decimalModulos[usrAccount.decimal];
 bal->cent = *amount % decimalModulos[usrAccount.decimal];

 /* now make negative if necessary */
 if (type != DEPOSITTYPE)
	bal->neg = TRUE;
 else	bal->neg = FALSE;
}

/************************************************************
* DataRebuildFilters()
*
*	We have changed something and need to free up and
*	test _ALL_ transactions again.
*************************************************************/
void DataRebuildFilters(void)
{
 entryNode *work, *next;
 filterNode *filt;

 DataFreeList((struct List *)&filtered);

 entryCount = 0;
 if (entries.mlh_TailPred == (struct Node *)&entries)
	return;

 work = (entryNode *)(entries.mlh_Head);
 while (next = (entryNode *)(work->node.mln_Succ)) {
	if (DataTestFilter(work,&filterControl) == TRUE) {
		entryCount++;
		DataAddFilter(&filt, work, &filtered);
	}
	work = next;
 }
}

/**************************************************************
* DataSitNSpin()
*
*	Wait for window activation
***************************************************************/
void DataSitNSpin(struct Gadget *ga, struct Window *win)
{
 BOOL done = FALSE;
 struct IntuiMessage *imsg = NULL;

 if (simpleRefresh == TRUE)
	return;

 do {
	while (imsg == NULL)
		imsg = GF_GetIMsg(guiapp);
	if (imsg->Class == IDCMP_ACTIVEWINDOW)
		done = TRUE;
	GF_ReplyIMsg(imsg);

 } while (!done);

 if (ga != NULL && win != NULL)
	ActivateGadget(ga,win,NULL);
	
}

/***************************************************************
* DataSortInit()
*
*	Initializes the sort field array
****************************************************************/
void DataSortInit(void)
{
 strcpy(sortEntries[SORTCHECK].field,"Check Number");
 sortEntries[SORTCHECK].node.ln_Name = sortEntries[SORTCHECK].field;
 sortEntries[SORTCHECK].order = 0;
 sortEntries[SORTCHECK].index = SORTCHECK;

 strcpy(sortEntries[SORTDATE].field,"Transaction Date");
 sortEntries[SORTDATE].node.ln_Name = sortEntries[SORTDATE].field;
 sortEntries[SORTDATE].order = 0;
 sortEntries[SORTDATE].index = SORTDATE;

 strcpy(sortEntries[SORTAMNT].field,"Amount"); 
 sortEntries[SORTAMNT].node.ln_Name = sortEntries[SORTAMNT].field;
 sortEntries[SORTAMNT].order = 0;
 sortEntries[SORTAMNT].index = SORTAMNT;

 strcpy(sortEntries[SORTNAME].field,"Name Field");
 sortEntries[SORTNAME].node.ln_Name = sortEntries[SORTNAME].field;
 sortEntries[SORTNAME].order = 0;
 sortEntries[SORTNAME].index = SORTNAME;

 strcpy(sortEntries[SORTMEMO].field,"Memo Field");
 sortEntries[SORTMEMO].node.ln_Name = sortEntries[SORTMEMO].field;
 sortEntries[SORTMEMO].order = 0;
 sortEntries[SORTMEMO].index = SORTMEMO;

 strcpy(sortEntries[SORTCATEGORY].field,"Category");
 sortEntries[SORTCATEGORY].node.ln_Name = sortEntries[SORTCATEGORY].field;
 sortEntries[SORTCATEGORY].order = 0;
 sortEntries[SORTCATEGORY].index = SORTCATEGORY;
}

/*******************************************************
* DataMoveSort()
*
*	move a sort list from one to another
********************************************************/
void DataMoveSort(struct List *to, sortNode *to_nodes, 
	struct List *from, sortNode *from_nodes)
{
 sortNode *node = (sortNode *)from->lh_Head;

 NewList(to);

 if (IsListEmpty(from))
	return;

 while (node->node.ln_Succ != NULL) {
	AddTail(to, &to_nodes[node->index]);
	node = (sortNode *)node->node.ln_Succ;
 }
 
}

/**********************************************************
* DataReconcile()
*
*	Reconciles the current account
***********************************************************/
balanceType DataReconcile(BOOL mark)
{
 entryNode *next, *node = (entryNode *)entries.mlh_Head;
 balanceType curr;

 DataInitBal(DEPOSITTYPE,&usrAccount.beginAmount,&curr);

 if (entries.mlh_TailPred == &entries)
	return (curr);

 while ( (next = (entryNode *)node->node.mln_Succ)) {
	if ( (node->flags & CLEARED) &&  !(node->flags & VOIDED)) {
		DataAddBal(node->type, &node->amount, &curr);
		if (mark == TRUE) node->flags |= RECONCILED;
	}

	node = next;
 }

 if (mark) amntState.stateAmnt = curr;
 return (curr);
}

/**************************************************************
* DataSortFilter()
*
*	Sorts filter entries (recursive). Refilter before?
****************************************************************/
void DataSortFilter(filterNode *top, filterNode *bottom, sortNode *node)
{
 BOOL swap = TRUE,done = FALSE;
 filterNode *newtop, *newbottom;
 filterNode *temptop, *temp, *tempswap;
 sortNode *next = (sortNode *)node->node.ln_Succ;

 /* move through each item and sort... not fast but _rhobust_ and easy */
 temptop = top;
 temp = (filterNode *)temptop->node.mln_Succ;
 while (!done) {

	/* place temptop node: */
	swap = FALSE;
	while (!swap && temp != NULL && temp->node.mln_Succ != NULL) {
		
		switch (node->index) {
			case SORTCHECK:
				if (node->order == SORTASCEND) {
					if (temptop->entry->check > temp->entry->check) 
						swap = TRUE;
				}
				else {
					if (temptop->entry->check < temp->entry->check) 
						swap = TRUE;
				}
				break;

			case SORTDATE:
				if (node->order == SORTASCEND) {
					if (temptop->entry->date.year > temp->entry->date.year)
						swap = TRUE;
					else if (temptop->entry->date.year == temp->entry->date.year &&
						 temptop->entry->date.month > temp->entry->date.month)
						swap = TRUE;
					else if (temptop->entry->date.year == temp->entry->date.year &&
						 temptop->entry->date.month == temp->entry->date.month &&
						 temptop->entry->date.day > temp->entry->date.day)
						swap = TRUE;
				}
				else {
					if (temptop->entry->date.year < temp->entry->date.year)
						swap = TRUE;
					else if (temptop->entry->date.year == temp->entry->date.year &&
						 temptop->entry->date.month < temp->entry->date.month)
						swap = TRUE;
					else if (temptop->entry->date.year == temp->entry->date.year &&
						 temptop->entry->date.month == temp->entry->date.month &&
						 temptop->entry->date.day < temp->entry->date.day)
						swap = TRUE;
				}
				break;

			case SORTAMNT:
				if (node->order == SORTASCEND) {
					if (temptop->entry->amount > temp->entry->amount)
						swap = TRUE;
				}
				else {
					if (temptop->entry->amount < temp->entry->amount)
						swap = TRUE;
				}
				break;

			case SORTNAME:
				if (node->order == SORTASCEND) {
					if (strcmpi(temptop->entry->name,temp->entry->name) > 0)
						swap = TRUE;
				}
				else {
					if (strcmpi(temptop->entry->name,temp->entry->name) < 0)
						swap = TRUE;
				}
				break;

			case SORTMEMO:
				if (node->order == SORTASCEND) {
					if (strcmpi(temptop->entry->memo,temp->entry->memo) > 0)
						swap = TRUE;
				}
				else {
					if (strcmpi(temptop->entry->memo,temp->entry->memo) < 0)
						swap = TRUE;
				}
				break;

			case SORTCATEGORY:
				if (node->order == SORTASCEND) {
					if (strcmpi(temptop->entry->category,temp->entry->category) > 0)
						swap = TRUE;
				}
				else {
					if (strcmpi(temptop->entry->category,temp->entry->category) < 0)
						swap = TRUE;
				}
				break;
		}

	   /* have we scanned the sector? */
     	   if (temp == bottom || swap == TRUE)
		break;

	   /* nope! move on to the next */
	   temp = (filterNode *)temp->node.mln_Succ;
	}	

 	if (swap) {
		/* we will swap temptop with temp */

		/* locate new parent for temptop */
		tempswap = (filterNode *)temp->node.mln_Pred;
		if (tempswap == temptop) 
			tempswap = temp;

		/* is this the top? */
		if (temptop == top)
			top = temp;

		/* is this the bottom? */
		if (temp == bottom)
			bottom = temptop;

		Remove((struct Node *)temp);
		Insert((struct List *)&filtered,(struct Node *)temp,(struct Node *)temptop); 
		Remove((struct Node *)temptop);
		Insert((struct List *)&filtered,(struct Node *)temptop,(struct Node *)tempswap);

		/* point to temp for next search */
		temptop = temp;
		temp = (filterNode *)temptop->node.mln_Succ;
	}
	else {
		/* the current item needs no movement, next! */
		if (temptop == bottom) 
			done = TRUE;

		else {
			temptop = (filterNode *)temptop->node.mln_Succ;
			
			/* prepare for next round of sorting */
			if (temptop == NULL || temptop == bottom || temptop->node.mln_Succ == NULL) 
				done = TRUE;
			else temp = (filterNode *)temptop->node.mln_Succ;
		}
	}
 }

 /* finished, now jump to next level */
 newtop = newbottom = top;
 if (next != NULL && next->node.ln_Succ != NULL) {

	/* find ALL tops and bottoms of this level and sort them */
	while (	DataSortLevel(bottom,&newtop,&newbottom, node))
		DataSortFilter(newtop,newbottom,next);	
 }
}

/****************************************************************
* DataSortLevel()
*
*	Finds next block of sublevel entries that need sorting
*****************************************************************/
BOOL DataSortLevel(filterNode *bottom, filterNode **stop, filterNode **sbottom, sortNode *sort)
{
 BOOL retval = FALSE;
 BOOL skip = FALSE;
 UWORD skipcount = 0;
 filterNode *temptop = NULL; 
 filterNode *temp;

 /* are we at the top or subsequent search? */
 if (*sbottom == *stop) 
	temptop = *stop;
 else
	temptop = (filterNode *)(*sbottom)->node.mln_Succ;

 /* don't go any farther if this is the bottom */
 if (*sbottom == bottom || temptop == NULL || temptop == bottom || temptop->node.mln_Succ == NULL) 
	return(retval);

 /* scan through while level is equal */
 temp = (filterNode *)temptop->node.mln_Succ;
 while (temp != NULL && temp->node.mln_Succ != NULL) {
	skip = FALSE;

	/* is this node == to temptop? */
	switch (sort->index) {
		case SORTCHECK:
			if (temptop->entry->check == temp->entry->check) 
					skip = TRUE;
			break;

		case SORTDATE:
			if (temptop->entry->date.year == temp->entry->date.year &&
				 temptop->entry->date.month == temp->entry->date.month &&
				 temptop->entry->date.day == temp->entry->date.day)
					skip = TRUE;
			break;

		case SORTAMNT:
			 if (temptop->entry->amount == temp->entry->amount)
					skip = TRUE;
			break;

		case SORTNAME:
			if (strcmpi(temptop->entry->name,temp->entry->name) == 0)
				skip = TRUE;
			break;

		case SORTMEMO:
			if (strcmpi(temptop->entry->memo,temp->entry->memo) == 0)
				skip = TRUE;
			break;

		case SORTCATEGORY:
			if (strcmpi(temptop->entry->category,temp->entry->category) == 0)
				skip = TRUE;
			break;

	}
	
	if (skip == TRUE) {
		/* yes, check next one (find whole block) */
		skipcount++;
		if (temp == bottom) {
			temp = (filterNode *)temp->node.mln_Succ;
			break;
		}
		else temp = (filterNode *)temp->node.mln_Succ;
	}

	else {
		/* have we captured a region? */
		if (skipcount > 0)
			break;

		/* move temptop down.. */
		temptop = temp;
		if (temptop == bottom || temptop == NULL) break;
		temp = (filterNode *)temptop->node.mln_Succ;
	}
 }

 /* ok what did we grab? */
 if (skipcount > 0) {
	temp = (filterNode *)temp->node.mln_Pred;
	retval = TRUE;
	*stop = temptop;
	*sbottom = temp;
 }
 
 return (retval);
}

/*************************************************************
* DataNewEditCat()
*
*	Creates a new editcat node and places it at tail
**************************************************************/
editCatNode *DataNewEditCat(struct List *el, categoryNode *cat)
{
 editCatNode *work;

 work = (editCatNode *)AllocVec(sizeof(editCatNode), MEMF_CLEAR);
 memcpy(&work->new,cat,sizeof(categoryNode));
 work->oldnode = FALSE;
 work->new.node.ln_Name = work->new.catname;
 AddTail((struct List *)el,(struct Node *)work);
 return (work);
}

/*************************************************************
* DataFreeGroupCats()
*
*	Special routine to free up group nodes
**************************************************************/
void DataFreeGroupCats(struct List *gl)
{
 groupNode *work = NULL;

 while ( (work = (groupNode *)DataGetNext(gl,(struct Node *)work)) != NULL) 
	DataFreeList(&work->catlist);
}

/*************************************************************
* DataNewGroup()
*
*	Creates a new group node and places it at tail
**************************************************************/
groupNode *DataNewGroup(struct List *gl, groupNode *grp)
{
 groupNode *work;

 work = (groupNode *)AllocVec(sizeof(groupNode), MEMF_CLEAR);
 memcpy(work,grp,sizeof(groupNode));
 NewList(&work->catlist);
 work->node.ln_Name = work->groupname;
 AddTail((struct List *)gl,(struct Node *)work);
 return (work);
}

/*************************************************************
* DataFreeList()
*
*	Special free routine for a generic list
**************************************************************/
void DataFreeList(struct List *list)
{
 struct Node *work = list->lh_Head;
 struct Node *next;

 if (IsListEmpty(list))
	return;

 while ( (next= work->ln_Succ)) {
	FreeVec(work);
	work = next;
 }

 NewList(list);
}

/**************************************************************
* DataCopyCatList()
*
*	A special copy routine for the edit cat list
**************************************************************/
void DataCopyCatList(struct List *editlist, struct List *catlist) 
{ 
 categoryNode *cat =  (categoryNode *)catlist->lh_Head;
 editCatNode *work;

 NewList((struct List *)editlist); 
 if (IsListEmpty(catlist))
	return;

 while (cat->node.ln_Succ) {
	work = (editCatNode *)AllocVec(sizeof(editCatNode), MEMF_CLEAR);
	memcpy(&work->original,cat,sizeof(categoryNode));
	memcpy(&work->new,cat,sizeof(categoryNode));
	work->oldnode = TRUE;
	work->new.node.ln_Name = work->new.catname;
	AddTail((struct List *)editlist,(struct Node *)work);
	cat = (categoryNode*)cat->node.ln_Succ;
 }
}

/********************************************************
* DataCopyList()
*
*	Copy a simple list from one to another
*********************************************************/
void DataCopyList(struct List *src, struct List *dest, funcptr func)
{
 struct MinNode *work = NULL;

 /* free up dest */
 DataFreeList(dest);

 while ( (work = (struct MinNode *)DataGetNext(src,(struct Node *)work)) != NULL)
	func(dest,work);
}

/****************************************************
* DataCopyGroupList()
*
*	Copies a group list from one list to the other

******************************************************/
void DataCopyGroupList(struct List *src, struct List *dest)
{
 groupNode *new, *work = NULL;

 /* clean up dest */
 DataFreeGroupCats(dest);
 DataFreeList(dest);

 /* copy each node over */
 while ( (work = (groupNode *)DataGetNext(src,(struct Node *)work)) != NULL) {
	new = DataNewGroup(dest,work);
	DataCopyList(&work->catlist, &new->catlist, (funcptr)DataAddCat);
 }

}

/****************************************************
* DataMoveCatList()
*
*	Moves the special edit list into the original.
*	replaces ALL system wide categories with
*	their modified version or NONE if deleted.
******************************************************/
void DataMoveCatList(struct List *edit)
{
 editCatNode *en, *next;
 
 /* replace the database entries */
 DataReplaceEntryCats(edit);
 DataReplaceTempCats(edit);
 /*DataReplaceGroupCats(&grouplist, edit);*/

 /* replace all control structures */
 en = DataLocateCat(edit,filterControl.category);
 strcpy(filterControl.category,en->new.catname);

 en = DataLocateCat(edit,searchControl.catstr);
 /* only replace search if _exact_ match */
 if (strcmpi(en->original.catname,searchControl.catstr) == 0)
 	strcpy(searchControl.catstr,en->new.catname);

/* what the hell was I thinking...
 en = DataLocateCat(edit,sortEntries[SORTCATEGORY].field);
 strcpy(sortEntries[SORTCATEGORY].field,en->new.catname);
*/

 /* free up original list */
 DataFreeList(&categories);

 /* create new cat list */
 en = (editCatNode *)edit->lh_Head;
 while ( (next = (editCatNode *)en->new.node.ln_Succ)) {
	DataAddCat(&categories,(categoryNode *)(&en->new));
	en = next;
 }

  /* free up edit list */
 DataFreeList(edit);
}

/********************************************************
* DataReplaceGroupCats()
*
*	replaces all categories with their new names
*********************************************************/
void DataReplaceGroupCats(struct List *grps, struct List *new)
{
 editCatNode *en;
 groupNode *node = NULL;
 categoryNode *cat;
 struct Node *find;

 while ( (node = (groupNode *)DataGetNext(grps, node)) != NULL) {
	cat = NULL;
	while ( (cat = (categoryNode *)DataGetNext(&node->catlist,cat)) != NULL) {
		/* replace all control structures */
		en = DataLocateCat(new, cat->catname);
		if (strcmpi(en->new.catname,"none") == 0) {
 			find = DataFindName(new,cat->catname);
			if (find == NULL) {
				Remove(cat);
				FreeVec(cat);
				cat = NULL;
			}
 			else 	strcpy(cat->catname,en->new.catname);
		}
		else	strcpy(cat->catname,en->new.catname);
	}
 }
}

/********************************************************
* DataReplaceTempCats()
*
*	replaces all categories with their new names
*********************************************************/
void DataReplaceTempCats(struct List *new)
{
 editCatNode *found;
 templateNode *node, *next;

 if (templates.lh_TailPred == (struct Node *)&templates)
	return;

 node = (templateNode *)templates.lh_Head;
 while ( (next = (templateNode *)node->node.ln_Succ) ){
	found = DataLocateCat(new,node->template.category);
	strcpy(node->template.category,found->new.catname);

	node = next;
 }
}

/********************************************************
* DataReplaceEntryCats()
*
*	replaces all categories with their new names
*********************************************************/
void DataReplaceEntryCats(struct List *new)
{
 editCatNode *found;
 entryNode *node, *next;

 if (entries.mlh_TailPred == &entries)
	return;

 node = (entryNode *)entries.mlh_Head;
 while ( (next = (entryNode *)node->node.mln_Succ) ){
	found = DataLocateCat(new,node->category);
	strcpy(node->category,found->new.catname);

	node = next;
 }
}

/********************************************************
* DataLocateCat()
*
*	Locate an original cat name.. return NONE if deleted
**********************************************************/
editCatNode *DataLocateCat(struct List *new, char *cat)
{
 editCatNode *temp, *next;

 temp = (editCatNode *)new->lh_Head;
 while ( (next = (editCatNode *)temp->new.node.ln_Succ) ){
	if (temp->oldnode == TRUE && strcmpi(cat,temp->original.catname) == 0)
		return (temp);

	temp = next;
 } 
 
 return ( (editCatNode*)(new->lh_Head));
}

/**********************************************************
* DataSearchFilter()
*
*	Searches through a fliter list for search match
***********************************************************/
WORD DataSearchFilter(struct MinList *fl, searchState *search)
{
 WORD entry = 0, firstrow = -1;
 BOOL findfirst;
 filterNode *work = (filterNode *)fl->mlh_Head, *next;

 if (fl->mlh_TailPred == fl)
	return ( -1 );

 /* is there any criteria to begin with? */
 if (!search->check && !search->amnt && !search->date && !search->category &&
	!search->name && !search->memo)
	return (-1);

 /* can we return on the first match? */
 if (search->lastrow == -1 || search->lastrow >= entryCount)
	findfirst = TRUE;
 else findfirst = FALSE;

 while ( (next = (filterNode *)work->node.mln_Succ) ) {

	if (DataSearchMatch(work,search)) {
		if (firstrow == -1) {
			firstrow = entry;
			if (findfirst || entry > search->lastrow)
				return (entry);
		}
		else if (entry > search->lastrow)
			return (entry);
	}

	/* next! */
	entry++;
	work = next;
 }
 
 /* did we match before entry? */
 return (firstrow);
}

/********************************************************************
* DataSearchMatch()
*
*	Match a filter to a search criteria
*********************************************************************/
BOOL DataSearchMatch(filterNode *fn, searchState *search)
{

 if (search->check && fn->entry->type != CHECKTYPE)
	return (FALSE);

 if (search->check && (fn->entry->check != search->checknum))
	return (FALSE);

 if (search->memo && !MatchPatternNoCase(search->memostrtoken, fn->entry->memo))
	return (FALSE);

 if (search->name && !MatchPatternNoCase(search->namestrtoken, fn->entry->name))
	return (FALSE);

 if (search->category && !MatchPatternNoCase(search->catstrtoken, fn->entry->category))
	return (FALSE);

 if (search->amnt && (fn->entry->amount != search->amount))
	return (FALSE);

 if (search->date && (fn->entry->date.year != search->searchdate.year || 
		      fn->entry->date.month != search->searchdate.month ||
		      fn->entry->date.day != search->searchdate.day) )
	return (FALSE);

 return (TRUE);
}

/**********************************************************
* DataFindName()
*
*	Case insensitive "FindName"
***********************************************************/
struct Node *DataFindName(struct List *l, char *name)
{
 struct Node *work = (struct Node *)l->lh_Head, *next;

 if (IsListEmpty(l))
	return (NULL);
 
 while ( (next = (struct Node *)work->ln_Succ)) {
	if (strcmpi(work->ln_Name,name) == 0)
		return (work);
	work = next;
 }

 return (NULL);
}

/**********************************************************
* DataFindNode()
*
*	search for node
***********************************************************/
WORD DataFindNode(struct List *l, struct Node *node)
{
 WORD index = 0;
 struct Node *work = (struct Node *)l->lh_Head, *next;

 if (IsListEmpty(l))
	return (-1);
 
 while ( (next = (struct Node *)work->ln_Succ)) {
	if (work == node)
		return (index);
	work = next;
	index++;
 }

 return (-1);
}

/************************************************************
* DataBalance()
*
*	calculate a balance based on flags. ignores
* 	account default balance
************************************************************/
balanceType DataBalance(UBYTE flags, struct MinList *list)
{
 amountType amnt=0;
 balanceType bal;
 entryNode *entry = (entryNode *)list->mlh_Head, *next;

 DataInitBal(DEPOSITTYPE,&amnt,&bal);

 if (IsListEmpty((struct List *)list))
	return (bal);
 
 while ( (next = (entryNode *)entry->node.mln_Succ)) {
	if ( !(entry->flags & VOIDED) && (entry->flags & flags) == flags) {
		/*if (entry->type == DEPOSITTYPE)*/
			DataAddBal(DEPOSITTYPE,&entry->amount,&bal);
/*
		bal += entry->amount;

		else
			bal -= entry->amount;
*/
	}
	entry = next;
 }

 return (bal);
}

/************************************************************
* DataSaveFile()
*
*	Save an account
*************************************************************/
BOOL DataSaveFile(char *fname, BOOL create)
{
 int fd;
 UWORD magicnum = MAGICNUM;
 UWORD temp;
 struct netInfoVars netInfo;
 categoryNode *cat=NULL;
 templateNode *tmpl=NULL;
 entryNode *entry=NULL;
 sortNode *tempsort = NULL;
 memoNode *memo = NULL;
 nameNode *nm = NULL;
 amntNode *am = NULL;
 groupNode *group = NULL;


 if ( (fd = open(fname,O_WRONLY | ((create)?O_CREAT:0) ,0)) == -1)
	return(FALSE);
 
 /* write magic number */
 if (write (fd,&magicnum,sizeof(UWORD)) == -1) {
	close (fd);
	return (FALSE);
 }

 /* calculate and write tax info */
 netInfo.taxdeductcurr = DataBalance( (UBYTE)TAXDEDUCT,&entries);
 netInfo.taxdeductstate = DataBalance( (UBYTE)(TAXDEDUCT | RECONCILED),&entries);
 netInfo.currAmnt = amntState.currAmnt;
 netInfo.stateAmnt = amntState.stateAmnt;
 netInfo.decimal = usrAccount.decimal;
 if (write (fd, &netInfo, sizeof(struct netInfoVars)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write user macros*/
 if (write (fd, arexxMacros, sizeof(arexxNode)*MAXMACROS) == -1) {
		close (fd);
		return (FALSE);
 }

 /* write user check form */
 if (write (fd,&checkForms.userForm,sizeof(checkForm)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write print prefs */
 if (write (fd,&checkPrint,sizeof(printControl)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write report structure */
 if (write (fd,&reportControl,sizeof(reportSetting)) == -1) {
	close (fd);
	return (FALSE);
 }

 /* write filter structure */
 if (write (fd,&filterControl,sizeof(filterSetting)) == -1) {
	close (fd);
	return (FALSE);
 }

 /* write search structure */
 if (write (fd,&searchControl, sizeof(searchState)) == -1) {
	close (fd);
	return (FALSE);
 }

 /* write num of sorts */
 temp = DataCountNodes(&sortfields);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close (fd);
	return (FALSE);
 }

 /* write sort structure */
 while ( (tempsort = (sortNode *)DataGetNext(&sortfields,tempsort)))
	if (write (fd,tempsort,sizeof(sortNode)) == -1) {
		close(fd);
		return (FALSE);
	}

 /* write sort state */
 if (write (fd, &sortStates, sizeof(sortState)) == -1){
	close(fd);
	return (FALSE);
 }

 /* write account structure */
 if (write (fd, &usrAccount,sizeof(accntState)) == -1) {
	close(fd);
	return (FALSE);
 }
 
 /* write num of categories */
 temp = DataCountNodes(&categories);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write categories */
 while ( (cat = (categoryNode*)DataGetNext(&categories,cat)))
	if (write(fd,cat,sizeof(categoryNode)) == -1) {
		close(fd);
		return (FALSE);
	}

 /* write num groups */
 temp = DataCountNodes(&grouplist);
 if (write(fd,&temp,sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }
 
 /* write groups, one at a time */
 while ( (group = (groupNode *)DataGetNext(&grouplist,group))) {
	if (write(fd,group,sizeof(groupNode)) == -1) {
		close(fd);
		return(FALSE);
	}

	temp = DataCountNodes(&group->catlist);
	if (write (fd, &temp, sizeof(UWORD)) == -1) {
		close(fd);
		return (FALSE);
	}

	cat = NULL;
	while ( (cat = (categoryNode*)DataGetNext(&group->catlist,cat)))
		if (write(fd,cat,sizeof(categoryNode)) == -1) {
			close(fd);
			return (FALSE);
		}
 }

 /* write num of templates */
 temp = DataCountNodes(&templates);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write templates */
 while ( (tmpl = (templateNode*)DataGetNext(&templates,tmpl)))
	if (DataWriteTemplate(fd, tmpl) != 0) {
		close(fd);
		return(FALSE);
	}

 /* write num of entries */
 temp = DataCountNodes((struct List *)&entries);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write entries */
 while ( (entry = (entryNode*)DataGetNext((struct List *)&entries, (struct Node *)entry)))
	if (DataWriteEntry(fd,entry) != 0) {
		close(fd);
		return(FALSE);
	}


 /* checkbook state information */
 if (write (fd,&amntState,sizeof(struct stateVars)) == -1) {
	close(fd);
	return (FALSE);
 }


 /* save the memo list */
 temp = DataCountNodes((struct List *)&memolist);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write memos */
 while ( (memo = (memoNode*)DataGetNext((struct List *)&memolist, (struct Node *)memo)))
	if (write(fd,memo,sizeof(memoNode)) == -1) {
		close(fd);
		return (FALSE);
	}

 /* do the same for the names list and amnts list */
 temp = DataCountNodes((struct List *)&namelist);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write names */
 while ( (nm = (nameNode*)DataGetNext((struct List *)&namelist, (struct Node *)nm)))
	if (write(fd,nm,sizeof(nameNode)) == -1) {
		close(fd);
		return (FALSE);
	}

 temp = DataCountNodes((struct List *)&amntlist);
 if (write (fd, &temp, sizeof(UWORD)) == -1) {
	close(fd);
	return (FALSE);
 }

 /* write names */
 while ( (am = (amntNode*)DataGetNext((struct List *)&amntlist, (struct Node *)am)))
	if (write(fd,am,sizeof(amntNode)) == -1) {
		close(fd);
		return (FALSE);
	}

 close(fd);
 return (TRUE);
}

/************************************************************
* DataLoadFile()
*
*	Load an account
*************************************************************/
BOOL DataLoadFile(char *fname)
{
 int fd;
 int x,y;
 UWORD magicnum;
 UWORD temp,temp3;
 categoryNode cat;
 entryNode entry;
 memoNode memo;
 nameNode nm;
 amntNode am;
 templateNode tmpl;
 sortNode tempsort;
 filterNode *filt;
 groupNode *group, tempgroup;
 struct netInfoVars netInfo;

 if ( (fd = open(fname,O_RDONLY,0)) == -1)
	return (FALSE);

 /* read magic number */
 read (fd,&magicnum,sizeof(UWORD));

 if (magicnum > MAGICNUM || magicnum < MAGICNUMOLD1 ) {
	close(fd);
	return (FALSE);
 }

 /* read tax vars */
 DataReadTaxVars(magicnum,fd, &netInfo);

 usrAccount.decimal = netInfo.decimal;

 /* read in user macros*/
 if (magicnum > MAGICNUMOLD8) {
	read (fd, arexxMacros, sizeof(arexxNode)*MAXMACROS);
 }

 if (magicnum >= MAGICNUMOLD6) {
 	/* user check form */
	if (magicnum == MAGICNUMOLD6)
 		read (fd,&checkForms.userForm,sizeof(checkForm)-(sizeof(ULONG)*3));
	else 	read (fd,&checkForms.userForm,sizeof(checkForm));

 	/* print prefs */
 	read (fd,&checkPrint,sizeof(printControl));
 }

 /* read report structure */
 read (fd,&reportControl,sizeof(reportSetting));

 /* read filter structure */
 DataReadFilterSetting(magicnum,fd,&filterControl);

 /* read search structure */
 DataReadSearchControl(magicnum,fd,&searchControl);

 /* read num of sorts */
 read (fd, &temp, sizeof(UWORD));

 /* read sort structure */
 NewList(&sortfields);
 for (x=0;x<temp;x++) {
	read (fd,&tempsort,sizeof(sortNode));
	memcpy(&sortEntries[tempsort.index],&tempsort,sizeof(sortNode));
	sortEntries[tempsort.index].node.ln_Succ = sortEntries[tempsort.index].node.ln_Pred = 0;
	sortEntries[tempsort.index].node.ln_Name = sortEntries[tempsort.index].field;
	AddTail(&sortfields,&sortEntries[tempsort.index]);
 }

 /* read sort state */
 read (fd, &sortStates, sizeof(sortState));

 /* read account structure */
 DataReadAccountInfo(magicnum,fd,&usrAccount);
 
 /* read num of categories */
 read (fd, &temp, sizeof(UWORD));

 /* read categories */
 DataFreeList(&categories);
 for (x=0;x<temp;x++) {
	read (fd,&cat,sizeof(categoryNode));
	DataAddCat(&categories,&cat);
 }

 /* read in groups */
 if (magicnum > MAGICNUMOLD7) {

	/* read num of groups */
 	read (fd, &temp, sizeof(UWORD));

	DataFreeGroupCats(&grouplist);
	DataFreeList(&grouplist);
	
	/* read in each group */
	for (x=0;x<temp;x++) {
		read(fd,&tempgroup,sizeof(groupNode));
		group = DataNewGroup(&grouplist,&tempgroup);
		
		/* read each group category in */
		read(fd,&temp3,sizeof(UWORD));
		for (y=0;y<temp3;y++) {
			read(fd,&cat,sizeof(categoryNode));
			DataAddCat(&group->catlist,&cat);
		}
	}
 }

 /* read num of templates */
 read (fd, &temp, sizeof(UWORD));

 /* read templates */
 DataFreeList(&templates);
 for (x=0;x<temp;x++) {
	DataReadTemplate(magicnum,fd,&tmpl);
	DataAddTemp(&templates, &tmpl);
 }

 /* read num of entries */
 read (fd, &temp, sizeof(UWORD));

 /* read entries */
 DataFreeList((struct List *)&filtered);
 entryCount = 0;
 DataFreeList((struct List *)&entries);
 for (x=0;x<temp;x++) {
	DataReadEntry(magicnum,fd,&entry);
	DataAddEntry(&entry,&entries,&filt);
	if (filt) entryCount++;
 }

 /* basic state variables */
 DataReadStateVars(magicnum,fd, &amntState);

 /* read in memos */
 if (magicnum >= MAGICNUMOLD4) {
	DataFreeList(&memolist);
	read (fd, &temp, sizeof(UWORD));
	for (x=0;x<temp;x++) {
		read (fd,&memo,sizeof(memoNode));
		MemoNewEdit(&memolist,&memo);
 	}
 }

 if (magicnum > MAGICNUMOLD4) {
	DataFreeList(&namelist);
	read(fd,&temp,sizeof(UWORD));
	for (x=0;x<temp;x++) {
		read (fd,&nm,sizeof(nameNode));
		NameNewEdit(&namelist,&nm);
 	}

	DataFreeList(&amntlist);
	read(fd,&temp,sizeof(UWORD));
	for (x=0;x<temp;x++) {
		read (fd,&am,sizeof(amntNode));
		AmntNewEdit(&amntlist,&am);
 	}
 }

 DataInitBalances();
 
 if (close (fd))
	return (FALSE);
 return (TRUE);
}

/*******************************************************
* DataCountNodes()
*
*	Count # nodes in a list
********************************************************/
UWORD DataCountNodes(struct List *lst)
{
 UWORD ret = 0;
 struct Node *work = lst->lh_Head;
 struct Node *next;

 if (IsListEmpty(lst))
	return (ret);

 while (( next = work->ln_Succ)) {
	ret++;
	work = next;
 }

 return (ret);
}

/*******************************************************
* DataGetNext()
*
*	Get the next node in the list
********************************************************/
struct Node *DataGetNext(struct List *lst, struct Node *parent)
{
 if (IsListEmpty(lst))
	return (NULL);

 if (parent == NULL)
	return (lst->lh_Head);

 if (parent->ln_Succ->ln_Succ)
	return (parent->ln_Succ);

 return (NULL);
}

/*******************************************************
* DataGetFileName()
*
*	Obtain the file name from an ASL requestor
********************************************************/
BOOL DataGetFileName(char *dest, short destsize, char *drawer, char *file)
{
 strncpy(dest, drawer, destsize);
 return (AddPart( dest, file, destsize));
}

/*******************************************************
* DataAttachIcon()
*
*	Attaches icons to files	
********************************************************/
void DataAttachIcon(char *iconpath, char *iconname, char *fname)
{
 struct DiskObject *diskobj;
 char icon[200];

 sprintf(icon,"%s%s",iconpath,iconname);
 if ( (diskobj = GetDiskObjectNew(icon)) != NULL) {
 	PutDiskObject(fname,diskobj);
	FreeDiskObject(diskobj);
 }
}

/********************************************************
* DataRunningBals()
*
*	Calculates running balances for the filters
*********************************************************/
balanceType DataRunningBals(struct MinList *entries, struct MinList *filtered)
{
 amountType zero = 0;
 balanceType scale,bal;
 filterNode *filt;
 entryNode *next, *entry = (entryNode *)entries->mlh_Head;

 DataInitBal(DEPOSITTYPE,&usrAccount.beginAmount,&bal);
 DataInitBal(DEPOSITTYPE,&zero, &scale);

 if (entries->mlh_TailPred == (struct Node *)entries)
	return (bal);

 if (filtered->mlh_TailPred == (struct Node *)filtered)
	return (bal);
 
 while ( (next = (entryNode *)entry->node.mln_Succ)) {
	if (!(entry->flags & VOIDED)) {
		DataAddBal(entry->type,&entry->amount,&bal);
	}

	if (DataTestFilter(entry,&filterControl) == TRUE) {

		if ( DataCmpAmntBal(&entry->amount,&scale) == 1)
			DataInitBal(DEPOSITTYPE,&entry->amount,&scale);
		else if ( DataCmpBal(FALSE, &bal, &scale) == 1)
			memcpy(&scale,&bal,sizeof(balanceType));

		filt = (filterNode *)filtered->mlh_Head;
	
 		/* locate filter */
 		while (filt->node.mln_Succ != NULL) {
			if (filt->entry == entry) {
				if (filt->entry->flags & VOIDED)
					DataInitBal(DEPOSITTYPE,&zero,&filt->runningBal);
				else
					memcpy(&filt->runningBal,&bal,sizeof(balanceType));
				break;
			}

			filt = (filterNode *)filt->node.mln_Succ;
		}
	}	
	entry = next;
 }

 DataAddBal(DEPOSITTYPE,&usrAccount.beginAmount,&scale);
 return (scale);
}

/********************************************************
* DataSummarize()
*
*  	Obtain summary information from account
*********************************************************/
BOOL DataSummarize(char *fname, netNode *net)
{
 int fd;
 unsigned short magicnum;

 if ( (fd = open(fname,O_RDONLY,0)) == -1)
	return (FALSE);

 /* read magic number */
 read (fd,&magicnum,sizeof(UWORD));

 if (magicnum < MAGICNUMOLD3) {
	close(fd);
	return (FALSE);
 }

 DataReadTaxVars(magicnum,fd,&net->balances);

 strcpy(net->acname,fname);
 close(fd);
 return (TRUE);
}

/************************************************************
* DataFilterBalance()
*
*	calculate the filter balance
************************************************************/
void DataFilterBalance(struct MinList *list)
{
 amountType zero = 0;
 balanceType bal;
 filterNode *filter = (filterNode *)list->mlh_Head, *next;

 DataInitBal(DEPOSITTYPE,&zero,&bal);
 DataInitBal(DEPOSITTYPE,&zero,&filtAmnt);

 if (IsListEmpty((struct List *)list))
	return;
 
 while ( (next = (filterNode *)filter->node.mln_Succ)) {
	if ( !(filter->entry->flags & VOIDED) ) {
		DataAddBal(filter->entry->type, &filter->entry->amount,&bal);
	}
	filter = next;
 }

 memcpy(&filtAmnt, &bal, sizeof(balanceType));
 
 return;
}

/*******************************************************
* DataInitBalances()
*
* 	Initializes stmt and curr balances from load.
*	They are read from the account, but don't trust
*	them.
********************************************************/
void DataInitBalances(void)
{
 entryNode *entry = NULL;

 DataInitBal(DEPOSITTYPE,&usrAccount.beginAmount,&amntState.currAmnt);
  
 while ( (entry = (entryNode *)DataGetNext((struct List *)&entries,(struct Node *)entry)) != NULL) {
	if (!(entry->flags & VOIDED)) {
		DataAddBal(entry->type,&entry->amount,&amntState.currAmnt);
	 }
 }
}

/*******************************************************
* DataWriteTemplate()
*
*	Write a template to file 
********************************************************/
int DataWriteTemplate(int fd, templateNode *temp)
{
 if (write(fd,temp->tempname,TEMPNAMESIZE) == -1)
	return -1;

 return (DataWriteEntry(fd,&temp->template));
}

/*******************************************************
* DataWriteEntry()
*
*	Pack entries in file format and write out. 	
********************************************************/
int DataWriteEntry(int fd, entryNode *entry)
{
 struct packit out;
 UBYTE catsize;

 out.type = entry->type;
 out.check = entry->check;
 out.flags = entry->flags;
 out.amount = entry->amount;
 out.date.year = entry->date.year;
 out.date.day = entry->date.day;
 out.date.month = entry->date.month;
 out.namesize = strlen(entry->name);
 out.memosize = strlen(entry->memo);
 
 out.addr1size = out.addr2size = out.addr3size = out.addr4size = 0;
 out.numBudgets = 1;
 
 /* write out each datumn */
 if (write (fd,&out,sizeof(struct packit)) != sizeof(struct packit))
	return (-1);

 /* write out strings */
 if (write(fd,entry->name,out.namesize) != out.namesize)
	return (-1);
 if (write(fd,entry->memo,out.memosize) != out.memosize)
	return (-1);

 /* now for each category available... (currently just one) */ 
 
 catsize = strlen(entry->category);
 if (write(fd,&catsize,sizeof(UBYTE)) != sizeof(UBYTE))
	return (-1);
 if (write(fd,entry->category,catsize) != catsize)
	return (-1);
 if (write(fd,&entry->amount,sizeof(amountType)) != sizeof(amountType))
	return (-1);
 
 return (0);
}

/*************************************************************
* DataCreateDate()
*
*	Create date based on multiple formats 
**************************************************************/
void DataCreateDate(dateType *date, char *out, BOOL year)
{

switch (dateFormat) {
	case DATE_MMDDYY:
		if (year)
			sprintf(out,"%02d/%02d/%2d",date->month,date->day,date->year % 100);
		else	sprintf(out,"%02d/%02d/%4d",date->month,date->day,date->year);
		break;

	case DATE_DDMMYY:
		if (year)
			sprintf(out,"%02d/%02d/%2d",date->day,date->month,date->year % 100);
		else	sprintf(out,"%02d/%02d/%4d",date->day,date->month,date->year);

		break;

	case DATE_DEC_12:
		if (year)
			sprintf(out,"%s-%02d-%2d",easymonths[date->month-1],date->day,date->year % 100);
		else	sprintf(out,"%s-%02d-%4d",easymonths[date->month-1],date->day,date->year);

		break;

	case DATE_12_DEC:
		if (year)
			sprintf(out,"%02d-%s-%2d",date->day,easymonths[date->month-1],date->year % 100);
		else	sprintf(out,"%02d-%s-%4d",date->day,easymonths[date->month-1],date->year);

		break;
}
}

/***************************************************************
* DataExtractDate()
*
*	extract date fields from date strings
****************************************************************/
void DataExtractDate(dateType *date, char *out)
{
 ULONG day,year,month;
 char str[4];
 int x;
 
 str[3]=0;

 switch (dateFormat) {
	case DATE_MMDDYY:
		sscanf(out,"%d/%d/%d",&month,&day,&year);
		break;

	case DATE_DDMMYY:
		sscanf(out,"%d/%d/%d",&day,&month,&year);
		break;

	case DATE_DEC_12:
		sscanf(out,"%c%c%c-%d-%d",&str[0],&str[1],&str[2],&day,&year);
		for (x=0;x<12;x++)
			if (strcmpi(easymonths[x],str) == 0) {
				month = x+1;
				break;
			}
		break;

	case DATE_12_DEC:
		sscanf(out,"%d-%c%c%c-%d",&day,&str[0],&str[1],&str[2],&year);
		for (x=0;x<12;x++)
			if (strcmpi(easymonths[x],str) == 0) {
				month = x+1;
				break;
			}

		break;
 }

 date->day = day;
 date->month = month;
 date->year = year;
}

/****************************************************************
* DataVerifyDateMMDDYY()
*
*	Verifies and builds date string
****************************************************************/
BOOL DataVerifyDateMMDDYY(char *instr, char *newstr)
{
char *section1, *section2, *last, *skip, *str;
ULONG day,year,month;
char strhold[DATESIZE+1];
dateType date;

 if (instr == NULL)
	return (FALSE);

 strcpy(strhold,instr);
 strhold[DATESIZE] = NULL;
 str = strhold;

 /* clip spaces off front and end */
 while (*str == ' ') str++;
 skip = &strhold[strlen(strhold) - 1];
 while (*skip == ' ' && skip != str) {
	*skip = NULL;
	skip--;
 }

 if (DataStrSet(str,"0123456789/ ") == FALSE)
	return (FALSE);

 /* quick check for '/' count, grab sections */
 section1 = strchr(str,'/');
 if (section1 == NULL)
	return (FALSE);
 *section1 = NULL;
 section1++;
 section2 = strchr(section1,'/');
 if (section2 == NULL)
	return (FALSE);
 *section2 = NULL;
 section2++;
 last = strchr(section2,'/');
 if (last != NULL)
	return (FALSE);
 
 /* check for "/" */
 if (strlen(section2) == 1)
	return (FALSE);

 /* now we have str->month, section1->day, section2->year */

 /* grab values, then compare later */
 sscanf(str,"%d",&month);
 sscanf(section1,"%d",&day);
 sscanf(section2,"%d",&year);
 if (month > 12 || month == 0)
	return (FALSE);
 if (day > 31 || day == 0)
	return (FALSE);

 if (year > 9999) return (FALSE);
 if (year < 100) year += currCentury;

 /* check for days in month */
 if (daysinmonth[month-1] < day )
	return (FALSE);

 if (month == 2 && day == daysinmonth[month-1] && !DataLeapYear(year))
	return (FALSE);

 date.day = day;
 date.month = month;
 date.year = year;
 DataCreateDate(&date,newstr, FALSE);
 return (TRUE);
}

/****************************************************************
* DataVerifyDateDDMMYY()
*
*	Verifies and builds date string
****************************************************************/
BOOL DataVerifyDateDDMMYY(char *instr, char *newstr)
{
char *section1, *section2, *last, *skip, *str;
ULONG day,year,month;
char strhold[DATESIZE+1];
dateType date;

 if (instr == NULL)
	return (FALSE);

 strcpy(strhold,instr);
 strhold[DATESIZE] = NULL;
 str = strhold;

 /* clip spaces off front and end */
 while (*str == ' ') str++;
 skip = &strhold[strlen(strhold) - 1];
 while (*skip == ' ' && skip != str) {
	*skip = NULL;
	skip--;
 }

 if (DataStrSet(str,"0123456789/ ") == FALSE)
	return (FALSE);

 /* quick check for '/' count, grab sections */
 section1 = strchr(str,'/');
 if (section1 == NULL)
	return (FALSE);
 *section1 = NULL;
 section1++;
 section2 = strchr(section1,'/');
 if (section2 == NULL)
	return (FALSE);
 *section2 = NULL;
 section2++;
 last = strchr(section2,'/');
 if (last != NULL)
	return (FALSE);
 
 /* check for "/" */
 if (strlen(section2) == 1)
	return (FALSE);

 /* now we have str->month, section1->day, section2->year */

 /* grab values, then compare later */
 sscanf(str,"%d",&day);
 sscanf(section1,"%d",&month);
 sscanf(section2,"%d",&year);
 if (month > 12 || month == 0)
	return (FALSE);
 if (day > 31 || day == 0)
	return (FALSE);

 if (year > 9999) return (FALSE);
 if (year < 100) year += currCentury;

 /* check for days in month */
 if (daysinmonth[month-1] < day )
	return (FALSE);

 if (month == 2 && day == daysinmonth[month-1] && !DataLeapYear(year))
	return (FALSE);

 date.day = day;
 date.month = month;
 date.year = year;
 DataCreateDate(&date,newstr, FALSE);
 return (TRUE);
}

/****************************************************************
* DataVerifyDateDec12()
*
*	Verifies and builds date string
****************************************************************/
BOOL DataVerifyDateDec12(char *instr, char *newstr)
{
char *section1, *section2, *last, *skip, *str;
ULONG day,year,month;
int x;
char strhold[DATESIZE+1];
dateType date;

 if (instr == NULL)
	return (FALSE);

 strcpy(strhold,instr);
 strhold[DATESIZE] = NULL;
 str = strhold;

 /* clip spaces off front and end */
 while (*str == ' ') str++;
 skip = &strhold[strlen(strhold) - 1];
 while (*skip == ' ' && skip != str) {
	*skip = NULL;
	skip--;
 }

 /* find date */
 month = 0;
 for (x=0;x<12;x++) {
	if (strnicmp (easymonths[x],str,3) == 0	) {
		month = x+1;
		break;
	}
 }

 if (month == 0)
	return (FALSE);

 /* quick check for '-' count, grab sections */
 section1 = strchr(str,'-');
 if (section1 == NULL)
	return (FALSE);
 *section1 = NULL;
 section1++;
 section2 = strchr(section1,'-');
 if (section2 == NULL)
	return (FALSE);
 *section2 = NULL;
 section2++;
 last = strchr(section2,'-');
 if (last != NULL)
	return (FALSE);
 
 /* check for "-" */
 if (strlen(section2) == 1)
	return (FALSE);

 /* now we have str->month, section1->day, section2->year */

 /* grab values, then compare later */
 sscanf(section1,"%d",&day);
 sscanf(section2,"%d",&year);
 if (month > 12 || month == 0)
	return (FALSE);
 if (day > 31 || day == 0)
	return (FALSE);

 if (year > 9999) return (FALSE);
 if (year < 100) year += currCentury;

 /* check for days in month */
 if (daysinmonth[month-1] < day )
	return (FALSE);

 if (month == 2 && day == daysinmonth[month-1] && !DataLeapYear(year))
	return (FALSE);

 date.day = day;
 date.month = month;
 date.year = year;
 DataCreateDate(&date,newstr, FALSE);
 
 return (TRUE);
}

/****************************************************************
* DataVerifyDate12Dec()
*
*	Verifies and builds date string
****************************************************************/
BOOL DataVerifyDate12Dec(char *instr, char *newstr)
{
char *section1, *section2, *last, *skip, *str;
ULONG day,year,month;
int x;
char strhold[DATESIZE+1];
dateType date;

 if (instr == NULL)
	return (FALSE);

 strcpy(strhold,instr);
 strhold[DATESIZE] = NULL;
 str = strhold;

 /* clip spaces off front and end */
 while (*str == ' ') str++;
 skip = &strhold[strlen(strhold) - 1];
 while (*skip == ' ' && skip != str) {
	*skip = NULL;
	skip--;
 }

 /* quick check for '-' count, grab sections */
 section1 = strchr(str,'-');
 if (section1 == NULL)
	return (FALSE);
 *section1 = NULL;
 section1++;
 section2 = strchr(section1,'-');
 if (section2 == NULL)
	return (FALSE);
 *section2 = NULL;
 section2++;
 last = strchr(section2,'-');
 if (last != NULL)
	return (FALSE);
 
 /* check for "-" */
 if (strlen(section2) == 1)
	return (FALSE);

 /* now we have str->month, section1->day, section2->year */

 /* grab values, then compare later */

 month = 0;
 for (x=0;x<12;x++) {
	if (strnicmp (easymonths[x],section1,3) == 0	) {
		month = x+1;
		break;
	}
 }

 if (month == 0)
	return (FALSE);

 sscanf(str,"%d",&day);
 sscanf(section2,"%d",&year);
 if (month > 12 || month == 0)
	return (FALSE);
 if (day > 31 || day == 0)
	return (FALSE);

 if (year > 9999) return (FALSE);
 if (year < 100) year += currCentury;

 /* check for days in month */
 if (daysinmonth[month-1] < day )
	return (FALSE);

 if (month == 2 && day == daysinmonth[month-1] && !DataLeapYear(year))
	return (FALSE);

 date.day = day;
 date.month = month;
 date.year = year;
 DataCreateDate(&date,newstr, FALSE);
 
 return (TRUE);
}

/************************************************************
* DataAnnounceDate()
*
*	Announce date for different formats 
*************************************************************/
void DataAnnounceDate(GUIFront *gui)
{
 char *str[4] = {
	"Illegal Date\nFormat: mm/dd/yy",
	"Illegal Date\nFormat: dd/mm/yy",
	"Illegal Date\nFormat: mmm-dd-yy",
	"Illegal Date\nFormat: dd-mmm-yy",
 };

 switch (dateFormat) {
	case DATE_MMDDYY:
 		DataAnnounce(gui,str[0]);
		break;
	case DATE_DDMMYY:
 		DataAnnounce(gui,str[1]);
		break;
	case DATE_DEC_12:
		DataAnnounce(gui,str[2]);
		break;
	case DATE_12_DEC:
		DataAnnounce(gui,str[3]);
		break;
 }
}

/************************************************************
* DataAnnounceAmount()
*
*	Announce illegal amount 
*************************************************************/
void DataAnnounceAmount(GUIFront *gui)
{
 DataAnnounce(gui,"Illegal amount value.");
}

/***********************************************************
*  DataListUp()
*
*	Listview management library: move entry UP
************************************************************/
void DataListUp(GUIFront *gui, struct Gadget *listgad, struct List *list, UWORD *listsel)
{
 struct Node *node1, *node2;

 /* do we have room to move up? */
 if (*listsel == 0)
	return;

 /* detach list */
 GF_SetGadgetAttrs(gui,listgad,
 	GTLV_Labels, ~0,
	TAG_DONE);

 node1 = DataOrd2Node(list,*listsel);
 if (*listsel > 1)
	node2 = DataOrd2Node(list,*listsel-2);
 else	node2 = NULL;

 Remove(node1);
 Insert(list, node1, node2);
		
 *listsel = *listsel - 1;

 /* re-attach list */		
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, list,
	GTLV_Selected,*listsel,
	GTLV_MakeVisible, *listsel,
	TAG_DONE);
}

/***********************************************************
*  DataListDown()
*
*	Listview management library: move entry DOWN
************************************************************/
void DataListDown(GUIFront *gui, struct Gadget *listgad, struct List *list, UWORD *listsel)
{
 struct Node *node1, *node2;

 /* detach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, ~0,
	TAG_DONE);

 node1 = DataOrd2Node(list,*listsel);

 /* is this the last one? */
 if ( node1->ln_Succ != (struct Node *)(&list->lh_Tail)) {
	node2 = DataOrd2Node(list,*listsel+1);
	Remove(node1);
	Insert(list,node1,node2);

	*listsel = *listsel + 1;
 }

 /* re-attach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, list,
	GTLV_Selected,*listsel,
	GTLV_MakeVisible, *listsel,
	TAG_DONE);
}

/***********************************************************
*  DataListBottom()
*
*	Listview management library: move entry to BOTTOM
************************************************************/
void DataListBottom(GUIFront *gui, struct Gadget *listgad, 
		struct List *list, UWORD *listsel, UWORD entries)
{
 struct Node *node1;

 /* detach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, ~0,
	TAG_DONE);

 node1 = DataOrd2Node(list,*listsel);
 Remove(node1);
 AddTail(list,node1);
 *listsel = entries-1;

 /* re-attach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, list,
	GTLV_Selected,*listsel,
	GTLV_MakeVisible, *listsel,
	TAG_DONE);	
}

/***********************************************************
*  DataListTop()
*
*	Listview management library: move entry to TOP
************************************************************/
void DataListTop(GUIFront *gui, struct Gadget *listgad, 
		struct List *list, UWORD *listsel, UWORD topmargin)
{
 struct Node *node1, *node2;

 if (*listsel <= topmargin)
	return;

 /* detach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, ~0,
	TAG_DONE);
	
 node1 = DataOrd2Node(list,*listsel);
 if (topmargin != 0 && *listsel > topmargin)
	node2 = DataOrd2Node(list,topmargin-1);
 else	node2 = NULL;

 Remove(node1);
 Insert(list, node1, node2);

 *listsel = topmargin;

 /* re-attach list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, list,
	GTLV_Selected, *listsel,
	GTLV_MakeVisible, *listsel,
	TAG_DONE);
}

/***********************************************************
*  DataListRemove()
*
*	Listview management library: remove entry
************************************************************/
void DataListRemove(GUIFront *gui, struct Gadget *listgad, 
		struct List *list, UWORD *listsel)
{
 struct Node *node1;

 /* detach and modify list */
 GF_SetGadgetAttrs(gui,listgad,
	GTLV_Labels, ~0,
	TAG_DONE);
			
 node1 = DataOrd2Node(list,*listsel);

 if (node1->ln_Succ == (struct Node *)(&list->lh_Tail)) 
	*listsel = *listsel - 1;

 Remove(node1);
 FreeVec(node1);
	
 /* re-attach list */
 if (IsListEmpty(list)) {
 	GF_SetGadgetAttrs(gui,listgad,
		GTLV_Labels, list,
		GTLV_Selected, ~0,
		TAG_DONE);		
 }
 else {
 	GF_SetGadgetAttrs(gui,listgad,
		GTLV_Labels, list,
		GTLV_Selected, *listsel,
		GTLV_MakeVisible, *listsel,
		TAG_DONE);		
 }
}
