/*
**	$Id: termPhone.c,v 1.11 92/08/18 16:12:51 olsen Sta Locker: olsen $
**	$Revision: 1.11 $
**	$Date: 92/08/18 16:12:51 $
**
**	Phonebook support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Size of a phonebook entry in the list. */

#define RAWSIZE (sizeof(struct PhoneEntry) - offsetof(struct PhoneEntry,Name))

#define DATEHEADERSIZE 24

	/* Obsolete TimeDate structure. */

struct TimeDateOld
{
	WORD			PayPerUnit[2],
				SecPerUnit[2],
				TimeOfDay[2];

	BYTE			Month,
				Day;

	UBYTE			Comment[22];
};

	/* Are we using the new includes? */

#ifdef ASLSM_FilterFunc

	/* Undefine the following symbols, the preferences header file
	 * will use the same names and the same values.
	 */

#undef PARITY_NONE
#undef PARITY_EVEN
#undef PARITY_ODD
#undef PARITY_MARK
#undef PARITY_SPACE

	/* Include the preferences header files. */

#include <prefs/prefhdr.h>
#include <prefs/serial.h>

	/* Serial preferences data. */

STATIC BYTE			SerialPrefsRead		= FALSE,
				SerialPrefsReadFailed	= FALSE;
STATIC struct SerialPrefs	SerialPrefs;

	/* ReadSerialPrefs():
	 *
	 *	Reads the default serial preferences settings.
	 */

STATIC BYTE
ReadSerialPrefs()
{
	struct IFFHandle *Handle;

		/* Allocate an IFF handle. */

	if(Handle = AllocIFF())
	{
			/* Open the preferences settings file. */

		if(Handle -> iff_Stream = Open("ENV:sys/serial.prefs",MODE_OLDFILE))
		{
				/* Make it known as a DOS file handle. */

			InitIFFasDOS(Handle);

				/* Open the file for reading. */

			if(!OpenIFF(Handle,IFFF_READ))
			{
					/* Stop at the `body' chunk. */

				if(!StopChunk(Handle,ID_PREF,ID_SERL))
				{
						/* Look for it... */

					if(!ParseIFF(Handle,IFFPARSE_SCAN))
					{
							/* Read the data. */

						if(ReadChunkBytes(Handle,&SerialPrefs,sizeof(struct SerialPrefs)) == sizeof(struct SerialPrefs))
							SerialPrefsRead = TRUE;
					}
				}

					/* Close the handle. */

				CloseIFF(Handle);
			}

				/* Release the handle. */

			Close(Handle -> iff_Stream);
		}

			/* Clean up. */

		FreeIFF(Handle);
	}

		/* Return sucess/failure. */

	return(SerialPrefsRead);
}

#endif	/* ASLSM_FilterFunc */

	/* SetPrefToDefaults():
	 *
	 *	Initialize configuration with default values.
	 */

VOID
SetPrefToDefaults(struct Configuration *Config,UBYTE *PathBuffer)
{
	if(!PathBuffer)
		PathBuffer = "TERM:config";

	strcpy(Config -> DefaultStorage,PathBuffer);

#ifdef ASLSM_FilterFunc

		/* The program will only try to read the preferences
		 * settings once; if the first access failed, no
		 * other accesses will be made.
		 */

	if(!SerialPrefsRead && !SerialPrefsReadFailed)
		SerialPrefsReadFailed = ReadSerialPrefs() ^ TRUE;

		/* Did we succeed in reading the file? */

	if(!SerialPrefsRead)
	{
		Config -> BaudRate		= 2400;
		Config -> BitsPerChar		= 8;
		Config -> Parity		= PARITY_NONE;
		Config -> StopBits		= 1;
		Config -> Handshaking		= 42;
		Config -> HandshakingProtocol	= HANDSHAKING_NONE;
		Config -> xONxOFF		= TRUE;
		Config -> SerBuffSize		= 32768;
	}
	else
	{
			/* Fill in the common data. */

		Config -> BaudRate		= SerialPrefs . sp_BaudRate;
		Config -> SerBuffSize		= SerialPrefs . sp_InputBuffer;
		Config -> BitsPerChar		= SerialPrefs . sp_BitsPerChar;
		Config -> StopBits		= SerialPrefs . sp_StopBits;
		Config -> Handshaking		= 42;

			/* Convert the handshaking mode. */

		switch(SerialPrefs . sp_InputHandshake)
		{
			case HSHAKE_NONE:	Config -> HandshakingProtocol	= HANDSHAKING_NONE;
						Config -> xONxOFF		= FALSE;

						break;

			case HSHAKE_RTS:	Config -> HandshakingProtocol	= HANDSHAKING_RTSCTS;
						Config -> xONxOFF		= FALSE;

						break;

			default:		Config -> HandshakingProtocol	= HANDSHAKING_NONE;
						Config -> xONxOFF		= TRUE;

						break;
		}

			/* Convert the parity settings. */

		if(SerialPrefs . sp_Parity <= PARITY_SPACE)
			Config -> Parity = SerialPrefs . sp_Parity;
		else
			Config -> Parity = PARITY_NONE;
	}

#else

	Config -> BaudRate		= 2400;
	Config -> BitsPerChar		= 8;
	Config -> Parity		= PARITY_NONE;
	Config -> StopBits		= 1;
	Config -> Handshaking		= HANDSHAKING_XONXOFF;
	Config -> SerBuffSize		= 32768;

#endif	/* ASLSM_FilterFunc */

	strcpy(Config -> SerialDevice,SERIALNAME);

	Config -> Duplex		= DUPLEX_FULL;
	Config -> HighSpeed		= FALSE;
	Config -> BreakLength		= 250000;
	Config -> UnitNumber		= 0;

	/* Modem Preferences. */

	Config -> RedialDelay		= 2;
	Config -> DialRetries		= 10;
	Config -> DialTimeout		= 60;
	Config -> ConnectAutoBaud	= FALSE;

	strcpy(Config -> ModemInit,	"ATZ\\r");
	strcpy(Config -> ModemExit,	"");
	strcpy(Config -> ModemHangup,	"~~~~~~~~+++\\r~~~~~~ATH0\\r");
	strcpy(Config -> DialPrefix,	"~~ATDP");
	strcpy(Config -> NoCarrier,	"NO CARRIER");
	strcpy(Config -> Connect,	"CONNECT");
	strcpy(Config -> Voice,		"VOICE");
	strcpy(Config -> Ring,		"RING");
	strcpy(Config -> Busy,		"BUSY");

	/* Transfer Preferences. */

	strcpy(Config -> Protocol,"xprzmodem.library");

	/* Macro Preferences. */

	if(!LastMacros[0])
	{
		strcpy(LastMacros,PathBuffer);

		AddPart(LastMacros,"macros.prefs",256);
	}

	strcpy(Config -> MacroFile,LastMacros);

	/* Screen Preferences. */

	Config -> DisplayMode		= DEFAULT_MONITOR_ID|HIRES_KEY;
	Config -> MakeScreenPublic	= TRUE;

	/* Terminal Preferences. */

	Config -> CaptureFilter		= TRUE;
	Config -> DestructiveBackspace	= FALSE;
	Config -> AudibleBell		= TRUE;
	Config -> VisibleBell		= FALSE;
	Config -> EightyColumns		= FALSE;
	Config -> SendCR		= CR_ASCR;
	Config -> SendLF		= LF_ASLF;
	Config -> ColourMode		= COLOUR_AMIGA;
	Config -> Emulation		= EMULATION_ANSIVT100;
	Config -> Font			= FONT_TOPAZ;

	Config -> SwapBSDelete		= FALSE;
	Config -> StripBit8		= FALSE;

	Config -> RasterEnabled		= TRUE;

	Config -> EmulationName[0]	= 0;

	Config -> StartupMacro[0]	= 0;
	Config -> UploadMacro[0]	= 0;
	Config -> DownloadMacro[0]	= 0;
	Config -> LogoffMacro[0]	= 0;

	if(!LastFastMacros[0])
	{
		strcpy(LastFastMacros,PathBuffer);

		AddPart(LastFastMacros,"fastmacros.prefs",256);
	}

	strcpy(Config -> FastMacroFile,LastFastMacros);

	Config -> Priority		= 0;
	Config -> OverridePath		= TRUE;
	Config -> BackupConfig		= FALSE;
	Config -> AutoUpload		= TRUE;

	strcpy(Config -> NoDialTone,"NO DIALTONE");

	Config -> TitleBar		= TRUE;
	Config -> StatusLine		= TRUE;

	Config -> DropDTR		= FALSE;

	Config -> MaxLogBuffSize	= 0;
	Config -> BufferEnabled		= TRUE;

	Config -> ReceiveCR		= CR_ASCR;
	Config -> ReceiveLF		= LF_ASLF;

	Config -> SetArchivedBit	= FALSE;

	Config -> KeyMapName[0]		= 0;

	Config -> OpenFastMacroPanel	= FALSE;

	Config -> PassThrough		= FALSE;

	Config -> SystemBeep		= FALSE;

	Config -> Alert			= ALERT_BEEP_SCREEN;

	Config -> AnswerBack[0]		= 0;

	Config -> ClipboardUnit		= 0;

	strcpy(Config -> FontName,"topaz.font");
	Config -> FontHeight = 8;

	strcpy(Config -> TextFontName,"topaz.font");
	Config -> TextFontHeight = 8;

	Config -> NCommLog		= FALSE;
	Config -> PrinterEnabled	= TRUE;

	Config -> Pad			= 0;

	strcpy(Config -> DialSuffix,"\\r");

	Config -> LineDelay		= 0;
	Config -> CharDelay		= 0;
}

	/* UpdatePrefs(struct Configuration *Config):
	 *
	 *	Patch existing preferences entry to
	 *	use new flags and definitions.
	 */

VOID
UpdatePrefs(struct Configuration *Config)
{
	enum { OLDHANDSHAKING_XONXOFF,OLDHANDSHAKING_RTSCTS,OLDHANDSHAKING_NONE,OLDHANDSHAKING_RTSCTS_DSR };

	switch(Config -> Handshaking)
	{
		case OLDHANDSHAKING_XONXOFF:	Config -> HandshakingProtocol	= HANDSHAKING_NONE;
						Config -> xONxOFF		= TRUE;
						break;

		case OLDHANDSHAKING_RTSCTS:	Config -> HandshakingProtocol	= HANDSHAKING_RTSCTS;
						Config -> xONxOFF		= FALSE;
						break;

		case OLDHANDSHAKING_NONE:	Config -> HandshakingProtocol	= HANDSHAKING_NONE;
						Config -> xONxOFF		= FALSE;
						break;

		case OLDHANDSHAKING_RTSCTS_DSR:	Config -> HandshakingProtocol	= HANDSHAKING_RTSCTS_DSR;
						Config -> xONxOFF		= FALSE;
						break;
	}

	Config -> Handshaking = 42;

	if(Config -> EmulationName[0])
	{
		WORD i;

		for(i = 0 ; i < strlen(Config -> EmulationName) ; i++)
			Config -> EmulationName[i] = ToLower(Config -> EmulationName[i]);
	}

	if(Config -> HighSpeed & ~SERIAL_SHARED)
	{
		Config -> BitsPerChar	= 8;
		Config -> StopBits	= 1;
		Config -> Parity	= PARITY_NONE;
	}
}

VOID
RemoveDialEntry(LONG Entry)
{
	LONG Count = Phonebook[Entry] -> Count,i;

	for(i = 0 ; i < NumPhoneEntries ; i++)
	{
		if(Phonebook[i] -> Count > Count)
			SPrintf(Phonebook[i] -> Node -> LocalName,"%3ld - %s",Phonebook[i] -> Count--,Phonebook[i] -> Name);
	}

	Phonebook[Entry] -> Count = -1;

	SPrintf(Phonebook[Entry] -> Node -> LocalName,"      %s",Phonebook[Entry] -> Name);
}

VOID
RemoveDialNode(struct PhoneNode *Node)
{
	LONG Count = Node -> Entry -> Count,i;

	for(i = 0 ; i < NumPhoneEntries ; i++)
	{
		if(Phonebook[i] -> Count > Count)
			SPrintf(Phonebook[i] -> Node -> LocalName,"%3ld - %s",Phonebook[i] -> Count--,Phonebook[i] -> Name);
	}

	Node -> Entry -> Count = -1;

	SPrintf(Node -> LocalName,"      %s",Node -> Entry -> Name);
}

VOID
SortToList(struct PhoneNode *PhoneNode)
{
	if(!DialList)
	{
		if(DialList = (struct List *)AllocVec(sizeof(struct List),MEMF_ANY|MEMF_CLEAR))
			NewList(DialList);
	}

	if(DialList)
	{
		struct PhoneNode *Node = (struct PhoneNode *)DialList -> lh_Head,*NewNode;

		if(NewNode = (struct PhoneNode *)AllocVec(sizeof(struct PhoneNode),MEMF_ANY))
		{
			CopyMem(PhoneNode,NewNode,sizeof(struct PhoneNode));

			while(Node -> VanillaNode . ln_Succ)
			{
				if(Node -> Entry -> Count > NewNode -> Entry -> Count)
				{
					if(Node == (struct NewNode *)DialList -> lh_Head)
						AddHead(DialList,&NewNode -> VanillaNode);
					else
						Insert(DialList,&NewNode -> VanillaNode,Node -> VanillaNode . ln_Pred);

					return;
				}

				Node = (struct PhoneNode *)Node -> VanillaNode . ln_Succ;
			}

			AddTail(DialList,&NewNode -> VanillaNode);
		}
	}
}

VOID
FreeDialList()
{
	if(DialList)
	{
		struct PhoneNode *NextNode,*SubNode;

		SubNode = (struct PhoneNode *)DialList -> lh_Head;

		while(SubNode -> VanillaNode . ln_Succ)
		{
			NextNode = (struct PhoneNode *)SubNode -> VanillaNode . ln_Succ;

			FreeVec(SubNode);

			SubNode = NextNode;
		}

		FreeVec(DialList);

		DialList = NULL;
	}
}

	/* CreatePhoneList():
	 *
	 *	Turn the array of pointers to phonebook entries into
	 *	a linked standard Amiga List (gadtools needs this).
	 */

struct List *
CreatePhoneList()
{
	struct List		*PhoneList;
	struct PhoneNode	*PhoneNode;
	LONG			 i;

	if(Phonebook && NumPhoneEntries)
	{
		if(PhoneList = (struct List *)AllocVec(sizeof(struct List) + NumPhoneEntries * sizeof(struct PhoneNode),MEMF_ANY|MEMF_CLEAR))
		{
			NewList(PhoneList);

			PhoneNode = (struct PhoneNode *)(PhoneList + 1);

			for(i = 0 ; i < NumPhoneEntries ; i++)
			{
				if(Phonebook[i] -> Count != -1)
					SPrintf(PhoneNode[i] . LocalName,"%3ld - %s",Phonebook[i] -> Count + 1,Phonebook[i] -> Name);
				else
					SPrintf(PhoneNode[i] . LocalName,"      %s",Phonebook[i] -> Name);

				PhoneNode[i] . VanillaNode . ln_Name = PhoneNode[i] . LocalName;

				Phonebook[i] -> Node = &PhoneNode[i];

				PhoneNode[i] . Entry = Phonebook[i];

				AddTail(PhoneList,&PhoneNode[i]);
			}

			return(PhoneList);
		}
	}
	else
	{
		if(PhoneList = (struct List *)AllocVec(sizeof(struct List),MEMF_ANY|MEMF_CLEAR))
		{
			NewList(PhoneList);

			return(PhoneList);
		}
	}

	return(NULL);
}

	/* DeletePhoneList(struct List *PhoneList):
	 *
	 *	Delete the entries listed in the Amiga List
	 *	created by the routine above.
	 */

VOID
DeletePhoneList(struct List *PhoneList)
{
	if(PhoneList)
	{
		FreeVec(PhoneList);

		PhoneList = NULL;
	}
}

	/* Compare(struct PhoneEntry **A,struct PhoneEntry **B):
	 *
	 *	Comparison subroutine required by the QuickSort
	 *	call below.
	 */

STATIC LONG __stdargs
Compare(struct PhoneEntry **A,struct PhoneEntry **B)
{
		/* Has entry A been selected? */

	if((*A) -> Count == -1)
	{
			/* If entry B isn't selected either, compare the
			 * names lexically, else entry B is supposed
			 * to be `smaller' than entry A.
			 */

		if((*B) -> Count == -1)
			return(Stricmp((*A) -> Name,(*B) -> Name));
		else
			return(1);
	}
	else
	{
			/* If entry B isn't selected, entry A is supposed
			 * to be `smaller' than entry B, else return
			 * the difference between both entries.
			 */

		if((*B) -> Count == -1)
			return(-1);
		else
			return((*A) -> Count - (*B) -> Count);
	}
}

	/* SortPhoneEntries():
	 *
	 *	Sorts the current phone list array in ascending order.
	 */

VOID
SortPhoneEntries()
{
	qsort(Phonebook,NumPhoneEntries,sizeof(struct PhoneEntry *),Compare);
}

	/* FreeTimeDateNode(struct TimeDateNode *Node):
	 *
	 *	Free the memory allocated for a TimeDateNode and
	 *	the associated data table.
	 */

VOID __regargs
FreeTimeDateNode(struct TimeDateNode *Node)
{
	FreeVec(Node -> Table);

	FreeVec(Node);
}

	/* FreeTimeDateList(struct List *List):
	 *
	 *	Free a list of TimeDateNodes.
	 */

VOID
FreeTimeDateList(struct List *List)
{
	struct Node *Node,*NextNode;

	Node = List -> lh_Head;

	while(NextNode = Node -> ln_Succ)
	{
		Remove(Node);

		FreeTimeDateNode((struct TimeDateNode *)Node);

		Node = NextNode;
	}
}

	/* CopyTimeDateList(struct List *From,struct List *To,BYTE SkipFirst):
	 *
	 *	Copies the TimeDateNode list from one location into
	 *	another.
	 */

VOID
CopyTimeDateList(struct List *From,struct List *To,BYTE SkipFirst)
{
	struct TimeDateNode *FromNode,*ToNode;

	FromNode = (struct TimeDateNode *)From -> lh_Head;

	if(SkipFirst)
		FromNode = (struct TimeDateNode *)FromNode -> VanillaNode . ln_Succ;

	while(FromNode -> VanillaNode . ln_Succ)
	{
		if(ToNode = (struct TimeDateNode *)AllocVec(sizeof(struct TimeDateNode),MEMF_ANY))
		{
			CopyMem(FromNode,ToNode,sizeof(struct TimeDateNode));

			ToNode -> VanillaNode . ln_Name = ToNode -> Buffer;

			if(ToNode -> Table = (struct TimeDate *)AllocVec(sizeof(struct TimeDate) * FromNode -> Table[0] . Count,MEMF_ANY))
			{
				CopyMem(FromNode -> Table,ToNode -> Table,sizeof(struct TimeDate) * FromNode -> Table[0] . Count);

				AddTail(To,&ToNode -> VanillaNode);
			}
			else
				FreeVec(ToNode);
		}

		FromNode = (struct TimeDateNode *)FromNode -> VanillaNode . ln_Succ;
	}
}

	/* AdaptTimeDateNode(struct TimeDateNode *Node):
	 *
	 *	Adapt the title and comment of a TimeDateNode.
	 */

VOID
AdaptTimeDateNode(struct TimeDateNode *Node)
{
	UBYTE *Comment = Node -> Comment[0] ? Node -> Comment : LocaleString(MSG_TERMPHONE_NO_COMMENT_TXT);

	if(Node -> Month == -1)
	{
		if(Node -> Day == -1)
			strcpy(Node -> Buffer,LocaleString(MSG_TERMPHONE_STANDARD_SETTINGS_TXT));
		else
			SPrintf(Node -> Buffer,LocaleString(MSG_TERMPHONE_DAYS_TXT),Comment);
	}
	else
		SPrintf(Node -> Buffer,"%2ld %s » %s",Node -> Day,LocaleString(MSG_TERMPHONE_JAN_TXT + Node -> Month),Comment);
}

	/* TimeCompare(struct TimeDate *A,struct TimeDate *B):
	 *
	 *	Comparison routine required by SortTimeTable().
	 */

STATIC LONG __stdargs
TimeCompare(struct TimeDate *A,struct TimeDate *B)
{
	return(((LONG)A -> Time) - ((LONG)B -> Time));
}

	/* SortTimeTable(struct TimeDateNode *Node):
	 *
	 *	Sort the time table associated with a
	 *	TimeDateNode in ascending order.
	 */

VOID
SortTimeTable(struct TimeDateNode *Node)
{
	qsort(Node -> Table,Node -> Table[0] . Count,sizeof(struct TimeDate),TimeCompare);
}

	/* BuildTimeList(struct TimeDateNode *Node):
	 *
	 *	Build a read-to-display time table list from a TimeDateNode.
	 */

struct List *
BuildTimeList(struct TimeDateNode *Node)
{
	struct List *List;

	if(List = (struct List *)AllocVec(sizeof(struct List) + Node -> Table[0] . Count * sizeof(struct TimeNode),MEMF_ANY|MEMF_CLEAR))
	{
		struct TimeNode	*Time = (struct TimeNode *)(List + 1);
		LONG		 i;

		NewList(List);

		for(i = 0 ; i < Node -> Table[0] . Count ; i++)
		{
			Time[i] . VanillaNode . ln_Name	= Time[i] . Name;
			Time[i] . Time			= Node -> Table[i] . Time;

			SPrintf(Time[i] . Name,"%2ld:%ld0",Time[i] . Time / 6,Time[i] . Time % 6);

			AddTail(List,&Time[i]);
		}
	}

	return(List);
}

	/* ResizeTimeDateNode(struct TimeDateNode *Node,LONG Count,UBYTE Time):
	 *
	 *	Resize the time table associated with a TimeDateNode.
	 */

BYTE
ResizeTimeDateNode(struct TimeDateNode *Node,LONG Count,UBYTE Time)
{
	if(Count != Node -> Table[0] . Count)
	{
		struct TimeDate *Table;

		if(Table = (struct TimeDate *)AllocVec(sizeof(struct TimeDate) * Count,MEMF_ANY|MEMF_CLEAR))
		{
			LONG i;

			CopyMem(Node -> Table,Table,sizeof(struct TimeDate) * Count);

			if(Count > Node -> Table[0] . Count)
			{
				for(i = Node -> Table[0] . Count ; i < Count ; i++)
				{
					CopyMem(&Node -> Table[0],&Table[i],sizeof(struct TimeDate));

					Table[i] . Time = Time;
				}
			}

			for(i = 0 ; i < Count ; i++)
				Table[i] . Count = Count;

			FreeVec(Node -> Table);

			Node -> Table = Table;

			return(TRUE);
		}
		else
			return(FALSE);
	}
	else
		return(TRUE);
}

	/* DeleteTimeDateNode(struct TimeDateNode *Node,LONG Index):
	 *
	 *	Delete a single timetable entry from a TimeDateNode.
	 */

BYTE
DeleteTimeDateNode(struct TimeDateNode *Node,LONG Index)
{
	struct TimeDate	*Table;
	LONG		 Count = Node -> Table[0] . Count - 1;

	if(Table = (struct TimeDate *)AllocVec(sizeof(struct TimeDate) * Count,MEMF_ANY|MEMF_CLEAR))
	{
		LONG i,j;

		for(i = j = 0 ; i < Node -> Table[0] . Count ; i++)
		{
			if(i != Index)
			{
				CopyMem(&Node -> Table[i],&Table[j],sizeof(struct TimeDate));

				Table[j++] . Count = Count;
			}
		}

		FreeVec(Node -> Table);

		Node -> Table = Table;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* CreateTimeDateNode(BYTE Month,BYTE Day,UBYTE *Comment,LONG Count):
	 *
	 *	Create a new TimeDateNode including time table entries.
	 */

struct TimeDateNode *
CreateTimeDateNode(BYTE Month,BYTE Day,UBYTE *Comment,LONG Count)
{
	struct TimeDateNode *Node;

	if(Node = (struct TimeDateNode *)AllocVec(sizeof(struct TimeDateNode),MEMF_ANY|MEMF_CLEAR))
	{
		if(Node -> Table = (struct TimeDate *)AllocVec(sizeof(struct TimeDate) * Count,MEMF_ANY|MEMF_CLEAR))
		{
			Node -> VanillaNode . ln_Name			= Node -> Buffer;

			Node -> Month					= Month;
			Node -> Day					= Day;

			Node -> Table[0] . PayPerUnit[DT_FIRST_UNIT]	= 23;
			Node -> Table[0] . SecPerUnit[DT_FIRST_UNIT]	=  6 * 60;
			Node -> Table[0] . PayPerUnit[DT_NEXT_UNIT]	= 23;
			Node -> Table[0] . SecPerUnit[DT_NEXT_UNIT]	=  6 * 60;
			Node -> Table[0] . Time				= DT_GET_TIME( 8,0);
			Node -> Table[0] . Count			= Count;

			if(Count > 1)
			{
				Node -> Table[1] . PayPerUnit[DT_FIRST_UNIT]	= 23;
				Node -> Table[1] . SecPerUnit[DT_FIRST_UNIT]	= 12 * 60;
				Node -> Table[1] . PayPerUnit[DT_NEXT_UNIT]	= 23;
				Node -> Table[1] . SecPerUnit[DT_NEXT_UNIT]	= 12 * 60;
				Node -> Table[1] . Time				= DT_GET_TIME(18,0);
				Node -> Table[1] . Count			= Count;
			}

			strcpy(Node -> Comment,Comment);

			AdaptTimeDateNode(Node);

			return(Node);
		}
		else
			FreeVec(Node);
	}

	return(NULL);
}

	/* RemPhoneEntry(LONG Num):
	 *
	 *	Remove a given entry from the phone book.
	 */

VOID
RemPhoneEntry(LONG Num)
{
	struct PhoneEntry	*Entry;
	LONG			 i;

	Entry = Phonebook[Num];

	for(i = Num ; i < NumPhoneEntries ; i++)
		Phonebook[i] = Phonebook[i + 1];

	Phonebook[NumPhoneEntries--] = NULL;

	FreeTimeDateList((struct List *)&Entry -> TimeDateList);

	FreeVec(Entry);
}

	/* NewPhoneEntry():
	 *
	 *	Create a new phone book entry with default values and
	 *	add it to the array. Expand the phone book if necessary.
	 */

BYTE
NewPhoneEntry()
{
	struct PhoneEntry	**PrivatePhonebook;
	LONG			 PrivatePhoneSize;
	LONG			 i;

		/* The phone book is filled `to the brim', so let's expand
		 * it.
		 */

	if(NumPhoneEntries + 1 > PhoneSize)
	{
			/* Allocate another phone book. */

		if(PrivatePhonebook = CreatePhonebook(PhoneSize + 1,&PrivatePhoneSize,FALSE))
		{
				/* Copy the data. */

			if(Phonebook && PhoneSize)
			{
				for(i = 0 ; i < PhoneSize ; i++)
					PrivatePhonebook[i] = Phonebook[i];

					/* Remove the old phonebook. */

				DeletePhonebook(Phonebook,PhoneSize,FALSE);
			}

				/* Assign the new pointers. */

			Phonebook = PrivatePhonebook;
			PhoneSize = PrivatePhoneSize;
		}
		else
			return(FALSE);
	}

		/* Allocate another entry and add it to the phone book. */

	if(Phonebook[NumPhoneEntries] = (struct PhoneEntry *)AllocVec(sizeof(struct PhoneEntry),MEMF_ANY|MEMF_CLEAR))
	{
		struct TimeDateNode *TimeDateNode;

		strcpy(Phonebook[NumPhoneEntries] -> Name,LocaleString(MSG_TERMPHONE_UNUSED_ENTRY_TXT));

		CopyMem(&Config,&Phonebook[NumPhoneEntries] -> Config,sizeof(struct Configuration));

		Phonebook[NumPhoneEntries] -> Count = -1;

		NewList((struct List *)&Phonebook[NumPhoneEntries] -> TimeDateList);

		if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
			AddTail((struct List *)&Phonebook[NumPhoneEntries] -> TimeDateList,&TimeDateNode -> VanillaNode);

		NumPhoneEntries++;

		return(TRUE);
	}

	return(FALSE);
}

	/* CreatePhonebook(LONG Size,LONG *AllocSize,BYTE CreateEntries):
	 *
	 *	Create a new phone entry array (so-called phone book).
	 */

struct PhoneEntry **
CreatePhonebook(LONG Size,LONG *AllocSize,BYTE CreateEntries)
{
	struct PhoneEntry **PhoneEntry = NULL;

	if(Size)
	{
			/* Round the number of phone entries to a
			 * multiple of eight.
			 */

		*AllocSize = (((Size + 7) >> 3) << 3);

			/* Create the list of pointers. */

		if(PhoneEntry = (struct PhoneEntry **)AllocVec(*AllocSize * sizeof(struct PhoneEntry *),MEMF_ANY|MEMF_CLEAR))
		{
				/* And create some entries if necessary. */

			if(CreateEntries)
			{
				LONG i;

				for(i = 0 ; i < Size ; i++)
				{
					if(!(PhoneEntry[i] = (struct PhoneEntry *)AllocVec(sizeof(struct PhoneEntry),MEMF_ANY|MEMF_CLEAR)))
					{
						LONG j;

						for(j = 0 ; j < i ; j++)
							FreeVec(PhoneEntry[j]);

						FreeVec(PhoneEntry);

						return(NULL);
					}
					else
						NewList((struct List *)&PhoneEntry[i] -> TimeDateList);
				}
			}
		}
	}

	return(PhoneEntry);
}

	/* DeletePhonebook(struct PhoneEntry **PhoneBook,LONG Size,BYTE FreeEntries):
	 *
	 *	Deallocates a given phone book and its entries if necessary.
	 */

VOID
DeletePhonebook(struct PhoneEntry **Phonebook,LONG Size,BYTE FreeEntries)
{
	if(FreeEntries)
	{
		LONG i;

		for(i = 0 ; i < Size ; i++)
		{
			if(Phonebook[i])
			{
				FreeTimeDateList((struct List *)&Phonebook[i] -> TimeDateList);

				FreeVec(Phonebook[i]);
			}
		}
	}

	FreeVec(Phonebook);
}

	/* SavePhonebook(UBYTE *Name):
	 *
	 *	Save the current phone book to a disk file.
	 */

BYTE
SavePhonebook(UBYTE *Name)
{
	struct IFFHandle	*Handle;
	BYTE			 Success = FALSE;

	if(Phonebook && NumPhoneEntries)
	{
		if(Handle = (struct IFFHandle *)AllocIFF())
		{
			if(Handle -> iff_Stream = Open(Name,MODE_NEWFILE))
			{
				InitIFFasDOS(Handle);

				if(!OpenIFF(Handle,IFFF_WRITE))
				{
					if(!PushChunk(Handle,'TERM',ID_CAT,IFFSIZE_UNKNOWN))
					{
						if(!PushChunk(Handle,'TERM',ID_FORM,IFFSIZE_UNKNOWN))
						{
							if(!PushChunk(Handle,0,'VERS',IFFSIZE_UNKNOWN))
							{
								struct TermInfo TermInfo;

								TermInfo . Version	= TermVersion;
								TermInfo . Revision	= TermRevision;

								if(WriteChunkBytes(Handle,&TermInfo,sizeof(struct TermInfo)) == sizeof(struct TermInfo))
								{
									if(PopChunk(Handle))
										Success = FALSE;
									else
									{
										UBYTE *TempBuffer = NULL;

										if(PhonePasswordUsed)
										{
											if(!PushChunk(Handle,0,'PSWD',20))
											{
												Success = TRUE;

												if(WriteChunkBytes(Handle,PhonePassword,20) != 20)
													Success = FALSE;

												if(PopChunk(Handle))
													Success = FALSE;

												if(Success)
												{
													if(!(TempBuffer = AllocVec(RAWSIZE,MEMF_ANY)))
														Success = FALSE;
												}
											}
										}
										else
											Success = TRUE;

										if(Success)
										{
											Success = FALSE;

											if(!PushChunk(Handle,0,'DIAL',IFFSIZE_UNKNOWN))
											{
												if(WriteChunkBytes(Handle,&NumPhoneEntries,sizeof(LONG)) == sizeof(LONG))
												{
													if(PopChunk(Handle))
														Success = FALSE;
													else
													{
														if(PopChunk(Handle))
															Success = FALSE;
														else
														{
															LONG i;

															for(i = 0 ; i < NumPhoneEntries ; i++)
															{
																if(!PushChunk(Handle,'TERM',ID_FORM,IFFSIZE_UNKNOWN))
																{
																	if(!PushChunk(Handle,0,'PREF',IFFSIZE_UNKNOWN))
																	{
																		if(TempBuffer)
																		{
																			Encrypt((UBYTE *)Phonebook[i] -> Name,RAWSIZE,TempBuffer,PhonePassword,20,TRUE);

																			if(WriteChunkBytes(Handle,TempBuffer,RAWSIZE) != RAWSIZE)
																			{
																				Success = FALSE;

																				break;
																			}
																			else
																				Success = TRUE;
																		}
																		else
																		{
																			if(WriteChunkBytes(Handle,Phonebook[i] -> Name,RAWSIZE) != RAWSIZE)
																			{
																				Success = FALSE;

																				break;
																			}
																			else
																				Success = TRUE;
																		}

																		if(PopChunk(Handle))
																		{
																			Success = FALSE;

																			break;
																		}
																	}

																	if(Success)
																	{
																		struct TimeDateNode *TimeDateNode;

																		TimeDateNode = (struct TimeDateNode *)Phonebook[i] -> TimeDateList . mlh_Head;

																		while(TimeDateNode -> VanillaNode . ln_Succ)
																		{
																			if(!PushChunk(Handle,0,'DATE',IFFSIZE_UNKNOWN))
																			{
																				if(WriteChunkBytes(Handle,TimeDateNode -> Comment,DATEHEADERSIZE) != DATEHEADERSIZE)
																				{
																					Success = FALSE;

																					break;
																				}
																				else
																				{
																					if(WriteChunkBytes(Handle,TimeDateNode -> Table,TimeDateNode -> Table[0] . Count * sizeof(struct TimeDate)) != TimeDateNode -> Table[0] . Count * sizeof(struct TimeDate))
																					{
																						Success = FALSE;

																						break;
																					}
																				}

																				if(PopChunk(Handle))
																				{
																					Success = FALSE;

																					break;
																				}

																				TimeDateNode = (struct TimeDateNode *)TimeDateNode -> VanillaNode . ln_Succ;
																			}
																		}
																	}

																	if(PopChunk(Handle))
																	{
																		Success = FALSE;

																		break;
																	}
																}
															}
														}
													}
												}
											}

											if(TempBuffer)
												FreeVec(TempBuffer);
										}
									}
								}
							}
						}

						if(PopChunk(Handle))
							Success = FALSE;
					}

					CloseIFF(Handle);
				}

				Close(Handle -> iff_Stream);
			}

			FreeIFF(Handle);
		}

		if(Success)
			SetProtection(Name,FIBF_EXECUTE);
		else
			DeleteFile(Name);
	}

	return(Success);
}

	/* OldLoadPhonebook(UBYTE *Name):
	 *
	 *	Restore a phone book from a disk file (old version).
	 */

STATIC BYTE __regargs
OldLoadPhonebook(UBYTE *Name)
{
	struct PhoneEntry	**PrivatePhonebook;
	LONG			 PrivatePhoneSize;
	LONG			 Size,i;
	struct IFFHandle	*Handle;
	BYTE			 Success = FALSE;
	struct StoredProperty	*Prop;
	struct TermInfo		*TermInfo;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
		{
			InitIFFasDOS(Handle);

			if(!OpenIFF(Handle,IFFF_READ))
			{
				if(!PropChunks(Handle,&VersionProps[0],1))
				{
					if(!StopChunk(Handle,'TERM','DIAL'))
					{
						if(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
							if(Prop = FindProp(Handle,'TERM','VERS'))
							{
								TermInfo = (struct TermInfo *)Prop -> sp_Data;

								if(TermInfo -> Version == 1 && TermInfo -> Revision >= 6)
								{
									if(ReadChunkRecords(Handle,&Size,sizeof(LONG),1))
									{
										if(Size)
										{
											if(PrivatePhonebook = CreatePhonebook(Size,&PrivatePhoneSize,TRUE))
											{
												struct ContextNode	*Chunk;
												LONG			 ItemSize;
												struct TimeDateNode	*TimeDateNode;

												Chunk = CurrentChunk(Handle);

												ItemSize = Chunk -> cn_Size / Size;

												for(i = 0 ; i < Size ; i++)
												{
													SetPrefToDefaults(&PrivatePhonebook[i] -> Config,NULL);

													if(ReadChunkRecords(Handle,PrivatePhonebook[i] -> Name,ItemSize,1))
													{
														UpdatePrefs(&PrivatePhonebook[i] -> Config);

														PrivatePhonebook[i] -> Count = -1;

														if(strlen(PrivatePhonebook[i] -> Password) > 19)
														{
															PrivatePhonebook[i] -> Password[19] = 0;
															PrivatePhonebook[i] -> UserName[ 0] = 0;
														}

														if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
															AddTail((struct List *)&PrivatePhonebook[i] -> TimeDateList,&TimeDateNode -> VanillaNode);
													}
													else
													{
														if(i)
															Size = i - 1;
														else
															Size = 0;

														break;
													}
												}

												if(Size)
												{
													if(Phonebook)
														DeletePhonebook(Phonebook,PhoneSize,TRUE);

													Phonebook = PrivatePhonebook;
													PhoneSize = PrivatePhoneSize;

													NumPhoneEntries = Size;

													Success = TRUE;
												}
												else
												{
													DeletePhonebook(PrivatePhonebook,PrivatePhoneSize,TRUE);

													Success = FALSE;
												}

												FreeDialList();
											}
										}
									}
								}
							}
						}
					}
				}

				CloseIFF(Handle);
			}

			Close(Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	return(Success);
}

	/* LoadPhonebook(UBYTE *Name):
	 *
	 *	Restore a phone book from a disk file.
	 */

BYTE
LoadPhonebook(UBYTE *Name)
{
	STATIC ULONG Stops[2 * 7] =
	{
		'TERM','VERS',
		'TERM','DIAL',
		'TERM','PREF',
		'TERM','DATE',
		'TERM','TIME',
		'TERM','PASS',
		'TERM','PSWD'
	};

	struct PhoneEntry	**PrivatePhonebook;
	LONG			 PrivatePhoneSize = 0;
	LONG			 Size = 0,i,j,Last = -1;
	struct IFFHandle	*Handle;
	BYTE			 Success = FALSE;
	struct ContextNode	*Chunk;
	BYTE			 UseOld		= FALSE,
				 LastHadTime	= TRUE,
				 HadPassword	= FALSE,
				 NewStyle	= TRUE;
	struct TimeDateNode	*TimeDateNode;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
		{
			InitIFFasDOS(Handle);

			if(!OpenIFF(Handle,IFFF_READ))
			{
				if(!StopChunks(Handle,&Stops[0],7))
				{
					while(!ParseIFF(Handle,IFFPARSE_SCAN))
					{
						Chunk = CurrentChunk(Handle);

						if(Chunk -> cn_ID == 'VERS')
						{
							struct TermInfo TermInfo;

							if(ReadChunkBytes(Handle,&TermInfo,sizeof(struct TermInfo)) == sizeof(struct TermInfo))
							{
								if(TermInfo . Version == 1 && TermInfo . Revision < 9)
								{
									UseOld = TRUE;

									break;
								}

								if((TermInfo . Version > TermVersion) || (TermInfo . Version == TermVersion && TermInfo . Revision > TermRevision) || (TermInfo . Version == 1 && TermInfo . Revision < 6))
									break;
							}
							else
								break;
						}

						if(Chunk -> cn_ID == 'PASS')
						{
							UBYTE DummyBuffer[20];

							HadPassword = TRUE;

							if(ReadChunkBytes(Handle,DummyBuffer,20) == 20)
							{
								if(PhonePasswordUsed)
								{
									if(!memcmp(PhonePassword,DummyBuffer,20))
										continue;
								}

								memset(SharedBuffer,0,20);

								if(CryptPanel(SharedBuffer))
								{
									UBYTE AnotherBuffer[20];

									Encrypt(SharedBuffer,20,AnotherBuffer,SharedBuffer,20,NewStyle = FALSE);

									if(!memcmp(DummyBuffer,AnotherBuffer,20))
									{
										memcpy(PhonePassword,DummyBuffer,20);

										PhonePasswordUsed = TRUE;

										continue;
									}
									else
									{
										MyEasyRequest(Window,LocaleString(MSG_TERMPHONE_WRONG_PASSWORD_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),Name);

										break;
									}
								}
								else
									break;
							}
							else
								break;
						}

						if(Chunk -> cn_ID == 'PSWD')
						{
							UBYTE DummyBuffer[20];

							HadPassword = TRUE;

							if(ReadChunkBytes(Handle,DummyBuffer,20) == 20)
							{
								if(PhonePasswordUsed)
								{
									if(!memcmp(PhonePassword,DummyBuffer,20))
										continue;
								}

								memset(SharedBuffer,0,20);

								if(CryptPanel(SharedBuffer))
								{
									UBYTE AnotherBuffer[20];

									Encrypt(SharedBuffer,20,AnotherBuffer,SharedBuffer,strlen(SharedBuffer),NewStyle = TRUE);

									if(!memcmp(DummyBuffer,AnotherBuffer,20))
									{
										memcpy(PhonePassword,DummyBuffer,20);

										PhonePasswordUsed = TRUE;

										continue;
									}
									else
									{
										MyEasyRequest(Window,LocaleString(MSG_TERMPHONE_WRONG_PASSWORD_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),Name);

										break;
									}
								}
								else
									break;
							}
							else
								break;
						}

						if(Chunk -> cn_ID == 'DIAL')
						{
							if(ReadChunkBytes(Handle,&Size,sizeof(LONG)) == sizeof(LONG))
							{
								i = 0;

								if(!(PrivatePhonebook = CreatePhonebook(Size,&PrivatePhoneSize,TRUE)))
									break;
							}
							else
								break;
						}

						if(Chunk -> cn_ID == 'PREF')
						{
							if(Last != -1)
							{
								if(strlen(PrivatePhonebook[i] -> Password) > 19)
								{
									PrivatePhonebook[i] -> Password[19] = 0;
									PrivatePhonebook[i] -> UserName[ 0] = 0;
								}

								if(!LastHadTime)
								{
									if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
										AddTail((struct List *)&PrivatePhonebook[Last] -> TimeDateList,&TimeDateNode -> VanillaNode);
								}
							}

							if(Size && i < Size)
							{
								WORD LastSize = sizeof(struct PhoneEntry);

								PhonePasswordUsed = HadPassword;

								if(Chunk -> cn_Size < LastSize)
									LastSize = Chunk -> cn_Size;

								SetPrefToDefaults(&PrivatePhonebook[i] -> Config,NULL);

								if(ReadChunkBytes(Handle,PrivatePhonebook[i] -> Name,LastSize) == LastSize)
								{
									UpdatePrefs(&PrivatePhonebook[i] -> Config);

									if(PhonePasswordUsed)
										Decrypt((UBYTE *)PrivatePhonebook[i] -> Name,LastSize,PrivatePhonebook[i] -> Name,PhonePassword,20,NewStyle);

									Last = i;

									PrivatePhonebook[i] -> Count = -1;

									i++;

									LastHadTime = FALSE;
								}
								else
								{
									if(i)
										Size = i - 1;
									else
										Size = 0;

									break;
								}
							}
						}

						if(Chunk -> cn_ID == 'TIME')
						{
							if(Last != -1)
							{
								struct TimeDateOld TimeDateOld;

								for(j = 0 ; j < Chunk -> cn_Size / sizeof(struct TimeDateOld) ; j++)
								{
									if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
									{
										if(ReadChunkBytes(Handle,&TimeDateOld,sizeof(struct TimeDateOld)) == sizeof(struct TimeDateOld))
										{
											if(PhonePasswordUsed)
												Decrypt((UBYTE *)&TimeDateOld,sizeof(struct TimeDateOld),(UBYTE *)&TimeDateOld,PhonePassword,20,NewStyle);

											strcpy(TimeDateNode -> Comment,TimeDateOld . Comment);

											TimeDateNode -> Month	= TimeDateOld . Month;
											TimeDateNode -> Day	= TimeDateOld . Day;

											TimeDateNode -> Table[0] . PayPerUnit[0]	= TimeDateOld . PayPerUnit[0];
											TimeDateNode -> Table[0] . SecPerUnit[0]	= TimeDateOld . SecPerUnit[0];
											TimeDateNode -> Table[0] . PayPerUnit[1]	= TimeDateOld . PayPerUnit[0];
											TimeDateNode -> Table[0] . SecPerUnit[1]	= TimeDateOld . SecPerUnit[0];
											TimeDateNode -> Table[0] . Time			= TimeDateOld . TimeOfDay[0];

											TimeDateNode -> Table[1] . PayPerUnit[0]	= TimeDateOld . PayPerUnit[1];
											TimeDateNode -> Table[1] . SecPerUnit[0]	= TimeDateOld . SecPerUnit[1];
											TimeDateNode -> Table[1] . PayPerUnit[1]	= TimeDateOld . PayPerUnit[1];
											TimeDateNode -> Table[1] . SecPerUnit[1]	= TimeDateOld . SecPerUnit[1];
											TimeDateNode -> Table[1] . Time			= TimeDateOld . TimeOfDay[1];

											AdaptTimeDateNode(TimeDateNode);

											AddTail((struct List *)&PrivatePhonebook[Last] -> TimeDateList,&TimeDateNode -> VanillaNode);

											LastHadTime = TRUE;
										}
									}
								}
							}
						}

						if(Chunk -> cn_ID == 'DATE')
						{
							if(Last != -1)
							{
								WORD Count = (Chunk -> cn_Size - DATEHEADERSIZE) / sizeof(struct TimeDate);

								if(TimeDateNode = CreateTimeDateNode(-1,-1,"",Count))
								{
									if(ReadChunkBytes(Handle,TimeDateNode -> Comment,DATEHEADERSIZE) == DATEHEADERSIZE)
									{
										if(ReadChunkBytes(Handle,TimeDateNode -> Table,sizeof(struct TimeDate) * Count) == sizeof(struct TimeDate) * Count)
										{
											AdaptTimeDateNode(TimeDateNode);

											AddTail((struct List *)&PrivatePhonebook[Last] -> TimeDateList,&TimeDateNode -> VanillaNode);

											LastHadTime = TRUE;
										}
										else
											FreeTimeDateNode(TimeDateNode);
									}
									else
										FreeTimeDateNode(TimeDateNode);
								}
							}
						}
					}

					if(Size)
					{
						if(!LastHadTime && Last != -1)
						{
							if(TimeDateNode = CreateTimeDateNode(-1,-1,"",2))
								AddTail((struct List *)&PrivatePhonebook[Last] -> TimeDateList,&TimeDateNode -> VanillaNode);
						}

						if(Phonebook)
							DeletePhonebook(Phonebook,PhoneSize,TRUE);

						Phonebook = PrivatePhonebook;
						PhoneSize = PrivatePhoneSize;

						NumPhoneEntries = Size;

						Success = TRUE;
					}
					else
					{
						if(PrivatePhoneSize)
						{
							DeletePhonebook(PrivatePhonebook,PrivatePhoneSize,TRUE);

							Success = FALSE;
						}
					}

					FreeDialList();
				}

				CloseIFF(Handle);
			}

			Close(Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	if(UseOld)
		Success = OldLoadPhonebook(Name);

	return(Success);
}
