/*
**	$Id: termAux.c,v 1.12 92/05/08 20:50:59 olsen Sta Locker: olsen $
**	$Revision: 1.12 $
**	$Date: 92/05/08 20:50:59 $
**
**	Miscellaneous support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Some handy positioning macros for StatusServer(). */

#define MoveItem(X,Y) Move(RPort,StatusOffset + X * 8,10 + Y * 13)

#define MoveSingle(X) Move(RPort,StatusOffset + X * 8,7)

	/* The following static strings are displayed in the status
	 * window.
	 */

STATIC UBYTE *ConfigFont[2] =
{
	"Topaz",
	"IBM® "
};

STATIC UBYTE *ConfigEmulation[4] =
{
	NULL,
	NULL,
	NULL,
	NULL
};

STATIC UBYTE *ConfigParity[5] =
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

STATIC UBYTE *ConfigStatus[7] =
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

	/* Block window nest count. */

STATIC WORD BlockNestCount;

	/* ExtractString():
	 *
	 *	Extracts a string from a list separated by `|' characters.
	 */

UBYTE * __regargs
ExtractString(UBYTE *String,UBYTE *Destination,BYTE ReturnEnd)
{
	UBYTE *OldString;

	if(ReturnEnd)
		OldString = NULL;
	else
		OldString = String;

	while(*String)
	{
		if(*String == '|')
		{
			*Destination = 0;

			String++;

			if(*String)
				return(String);
			else
				return(OldString);
		}
		else
			*Destination++ = *String++;
	}

	*Destination = 0;

	return(OldString);
}

	/* DeleteInterleavedBitMap():
	 *
	 *	Delete an interleaved bitmap as allocated by
	 *	CreateInterleavedBitMap().
	 */

VOID __regargs
DeleteInterleavedBitMap(struct BitMap *SomeBitMap)
{
	FreeVec(SomeBitMap -> Planes[0]);

	FreeVec(SomeBitMap);
}

	/* CreateInterleavedBitMap():
	 *
	 *	With special thanks to Leo Schwab, this routine will create an
	 *	interleaved BitMap structure suitable for optimized blitter
	 *	access.
	 */

struct BitMap * __regargs
CreateInterleavedBitMap(LONG Width,LONG Height,WORD Depth)
{
		/* A single plane BitMap cannot be interleaved. */

	if(Depth > 1)
	{
		struct BitMap *SomeBitMap;

			/* Allocate space for the bitmap structure. */

		if(SomeBitMap = (struct BitMap *)AllocVec(sizeof(struct BitMap),MEMF_ANY))
		{
				/* Initialize with standard values, so we can check
				 * whether the current system will be able to handle
				 * an interleaved bitmap of the expected size.
				 */

			InitBitMap(SomeBitMap,Depth,Width,Height);

				/* Check for old standard blitter limits. */

			if(Height * Depth > 1024 || SomeBitMap -> BytesPerRow * Depth > 126)
			{
					/* The current space requirements will operate
					 * correctly only on a system equipped with a
					 * Fat Agnus (or successor) chip, let's see
					 * if we can find one.
					 */

				if(!(GfxBase -> ChipRevBits0 & GFXF_BIG_BLITS))
				{
						/* Looks like a Big or old (A1000)
						 * Agnus chip.
						 */

					FreeVec(SomeBitMap);

					return(NULL);
				}
				else
				{
						/* Unlikely, put still not impossible: check for
						 * Fat Agnus size limits...
						 */

					if(Height * Depth > 32768 || SomeBitMap -> BytesPerRow * Depth > 4096)
					{
						FreeVec(SomeBitMap);

						return(NULL);
					}
				}
			}

				/* Initialize to interleaved BitMap values. */

			InitBitMap(SomeBitMap,1,Width,Height * Depth);

				/* Allocate plane data. */

			if(SomeBitMap -> Planes[0] = (PLANEPTR)AllocVec(SomeBitMap -> BytesPerRow * SomeBitMap -> Rows,MEMF_CHIP))
			{
				PLANEPTR	Base;
				WORD		i,
						Size;

					/* Remember previous data. */

				Base = SomeBitMap -> Planes[0];
				Size = SomeBitMap -> BytesPerRow;

					/* Clear the bitmap. */

				BltBitMap(SomeBitMap,0,0,SomeBitMap,0,0,Width,Height,0,~0,NULL);

					/* Reinitialize. */

				InitBitMap(SomeBitMap,Depth,Width,Height);

					/* Modify for interleaved look. */

				SomeBitMap -> BytesPerRow *= Depth;

					/* Initialize the single planes. */

				for(i = 0 ; i < Depth ; i++)
				{
					SomeBitMap -> Planes[i] = Base;

					Base += Size;
				}

					/* Return the ready bitmap. */

				return(SomeBitMap);
			}

				/* Deallocate memory. */

			FreeVec(SomeBitMap);
		}
	}

		/* Return failure. */

	return(NULL);
}

	/* GetListNode(LONG Offset,struct List *List):
	 *
	 *	Return the n-th Node entry in a standard exec list.
	 */

struct Node *
GetListNode(LONG Offset,struct List *List)
{
	struct Node	*Node;
	LONG		 i;

	Node = List -> lh_Head;

	for(i = 0 ; i < Offset ; i++)
	{
		if(!Node -> ln_Succ -> ln_Succ)
			return(NULL);

		Node = Node -> ln_Succ;
	}

	return(Node);
}

	/* GetConUnit():
	 *
	 *	Extract the ConUnit pointer from the current output stream
	 *	console.
	 */

struct ConUnit *
GetConUnit(struct MsgPort *ConsoleTask)
{
	struct InfoData	*InfoData;
	struct ConUnit	*ConUnit = NULL;

	if(!ConsoleTask)
	{
		ConsoleTask = (struct MsgPort *)((struct Process *)SysBase -> ThisTask) -> pr_ConsoleTask;

		if(!TypeOfMem(ConsoleTask))
			return(NULL);
	}

	if(InfoData = (struct InfoData *)AllocVec(sizeof(struct InfoData),MEMF_ANY|MEMF_CLEAR))
	{
			/* Send the inquiry packet to the console task. */

		if(DoPkt1(ConsoleTask,ACTION_DISK_INFO,MKBADDR(InfoData)))
			ConUnit = (struct ConUnit *)((struct IOStdReq *)InfoData -> id_InUse) -> io_Unit;

		FreeVec(InfoData);
	}

	return(ConUnit);
}

	/* AddDownloadObject(UBYTE *Line):
	 *
	 *	Add another downloaded object to the list.
	 */

VOID
AddDownloadObject(UBYTE *Line)
{
	struct Node *SomeNode;

		/* Allocate space for the node itself and the
		 * string to be put into the node name.
		 */

	if(SomeNode = (struct Node *)AllocVec(sizeof(struct Node) + strlen(Line) + 1,MEMF_ANY|MEMF_CLEAR))
	{
			/* Block list modification. */

		ObtainSemaphore(DownloadSemaphore);

			/* Enter the name. */

		SomeNode -> ln_Name = (UBYTE *)(SomeNode + 1);

			/* Copy the line over. */

		strcpy(SomeNode -> ln_Name,Line);

			/* Add it to the list. */

		AddTail(&DownloadList,SomeNode);

			/* Increment number of downloads. */

		DownloadLineCount++;

		ReleaseSemaphore(DownloadSemaphore);
	}
}

	/* ClearDownloadObjects():
	 *
	 *	Clear the list of downloaded objects.
	 */

VOID
ClearDownloadObjects()
{
	struct Node *SomeNode,*NextNode;

	ObtainSemaphore(DownloadSemaphore);

	SomeNode = DownloadList . lh_Head;

	while(SomeNode -> ln_Succ)
	{
		NextNode = SomeNode -> ln_Succ;

		Remove(SomeNode);

		FreeVec(SomeNode);

		SomeNode = NextNode;
	}

	DownloadNode = NULL;

	DownloadLineCount = 0;

	ReleaseSemaphore(DownloadSemaphore);
}

	/* SequenceFilter(UBYTE Char):
	 *
	 *	Yet another byte sequence filter, this time it's the
	 *	ARexx interface to make the call.
	 */

struct ScanNode *
SequenceFilter(UBYTE Char)
{
	struct ScanNode	*SomeNode,*Matching = NULL;
	BYTE		 Matches = 0;

		/* Convert input character to upper case. */

	Char = ToUpper(Char);

		/* Get the first node in the list. */

	SomeNode = (struct ScanNode *)SequenceList . lh_Head;

		/* Scan until the last node is found. */

	while(SomeNode -> Node . mln_Succ)
	{
			/* This sequence had a couple of matches. */

		if(SomeNode -> Count == SequenceCount)
		{
				/* The next character in the sequence
				 * still matches.
				 */

			if(Char == SomeNode -> Sequence[SequenceCount])
			{
					/* Increase number of matching
					 * characters.
					 */

				SomeNode -> Count++;

					/* If there's another character
					 * in the sequence, increase
					 * the match counter.
					 */

				if(SomeNode -> Sequence[SequenceCount + 1])
					Matches++;
				else
				{
						/* We were able to make
						 * a perfect match.
						 */

					Matches = 0;

					Matching = SomeNode;
				}
			}
		}

			/* Skip to the next node. */

		SomeNode = (struct ScanNode *)SomeNode -> Node . mln_Succ;
	}

	if(!Matches)
	{
			/* Clear the list entry counters. */

		if(SequenceCount)
		{
			SomeNode = (struct ScanNode *)SequenceList . lh_Head;

			while(SomeNode -> Node . mln_Succ)
			{
				SomeNode -> Count = 0;

				SomeNode = (struct ScanNode *)SomeNode -> Node . mln_Succ;
			}

			SequenceCount = 0;
		}
	}
	else
		SequenceCount++;

	return(Matching);
}

	/* AddSequenceObject(UBYTE *Sequence):
	 *
	 *	Add another sequence to the list of byte sequences
	 *	the routine above will look at whilst parsing.
	 */

VOID
AddSequenceObject(UBYTE *Sequence)
{
	struct ScanNode *SomeNode;

	if(SomeNode = AllocVec(sizeof(struct ScanNode) + strlen(Sequence) + 1,MEMF_ANY|MEMF_CLEAR))
	{
		WORD i;

		SomeNode -> Sequence = (UBYTE *)(SomeNode + 1);

		for(i = 0 ; i <= strlen(Sequence) ; i++)
			SomeNode -> Sequence[i] = ToUpper(Sequence[i]);

		AddTail(&SequenceList,(struct Node *)SomeNode);
	}
}

	/* ClearSequenceObjects():
	 *
	 *	Clear the list of scan sequences.
	 */

VOID
ClearSequenceObjects()
{
	struct Node *SomeNode,*NextNode;

	SomeNode = SequenceList . lh_Head;

	while(SomeNode -> ln_Succ)
	{
		NextNode = SomeNode -> ln_Succ;

		Remove(SomeNode);

		FreeVec(SomeNode);

		SomeNode = NextNode;
	}
}

	/* LogAction(UBYTE *String,...):
	 *
	 *	Write an action to the default log file.
	 */

VOID __stdargs
LogAction(UBYTE *String,...)
{
	if(Config . LogActions && Config . LogFile[0])
	{
		UBYTE	DummyBuffer[512];
		BPTR	File;

		va_list	VarArgs;

			/* Build a valid string. */

		va_start(VarArgs,String);
		VSPrintf(DummyBuffer,String,VarArgs);
		va_end(VarArgs);

			/* Does the log file already exist? */

		if(GetFileSize(Config . LogFile))
		{
				/* It does, let's append the data. */

			if(File = Open(Config . LogFile,MODE_READWRITE))
			{
				if(Seek(File,0,OFFSET_END) == -1)
				{
					Close(File);

					File = NULL;
				}
			}
		}
		else
		{
				/* Create a new file. */

			if(File = Open(Config . LogFile,MODE_NEWFILE))
				FPrintf(File,LocaleString(MSG_TERMAUX_DATE_TIME_ACTION_TXT));
		}

			/* The file is open, build the date/time string and
			 * write the log action.
			 */

		if(File)
		{
			UBYTE		TimeBuffer[20],DateBuffer[20];
			struct DateTime	DateTime;

				/* Obtain current time. */

			DateStamp(&DateTime . dat_Stamp);

				/* Convert it to human readable form. */

			DateTime . dat_Format	= FORMAT_DOS;
			DateTime . dat_Flags	= 0;
			DateTime . dat_StrDay	= NULL;
			DateTime . dat_StrDate	= DateBuffer;
			DateTime . dat_StrTime	= TimeBuffer;

				/* Conversion succeeded? */

			if(DateToStr(&DateTime))
				FPrintf(File,"%s %s %s\n",DateBuffer,TimeBuffer,DummyBuffer);

				/* Done! */

			Close(File);
		}
	}
}

	/* FlushMsg(struct Window *Window):
	 *
	 *	Cancel all pending messages of a window.
	 */

VOID
FlushMsg(struct Window *Window)
{
	struct IntuiMessage *Massage;

	while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		ReplyMsg(&Massage -> ExecMessage);
}

	/* GetString(UBYTE *Prompt,UBYTE *Buffer):
	 *
	 *	Get a string from the user, very much the same as xpr_gets,
	 *	but also including the `Load File' gadget.
	 */

BYTE
GetString(UBYTE *Prompt,UBYTE *Buffer)
{
	struct Gadget		*GadgetList = NULL;
	struct Gadget		*GadgetArray[4];
	struct Window		*PanelWindow;
	UBYTE			 DummyBuffer[256];
	struct FileRequester	*FileRequest;
	LONG			 Width;
	BYTE			 Success = FALSE;

	if(CreateAllGetsGadgets(TRUE,Buffer,Prompt,&Width,&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1,Screen))
	{
		if(PanelWindow = OpenWindowTags(NULL,
			WA_Width,	Width,
			WA_Height,	58,
			WA_Left,	(Screen -> Width - Width) >> 1,
			WA_Top,		(Screen -> Height - 56) >> 1,
			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_DepthGadget,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_CustomScreen,Screen,
			WA_IDCMP,	IDCMP_GADGETDOWN | IDCMP_ACTIVEWINDOW | IDCMP_CLOSEWINDOW | IDCMP_GADGETUP | IDCMP_VANILLAKEY | BUTTONIDCMP | STRINGIDCMP,
			WA_Title,	LocaleString(MSG_GLOBAL_ENTER_TEXT_TXT),
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			PushWindow(PanelWindow);

			AddGList(PanelWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,PanelWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(PanelWindow,NULL);

			ActiveGadget = NULL;

			while(!Terminated)
			{
				WaitPort(PanelWindow -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(PanelWindow -> UserPort)))
				{
					Class	= Massage -> Class;
					Code	= Massage -> Code;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_VANILLAKEY)
						KeySelect(GadgetArray,3,Code,PanelWindow,&Gadget,&Class,&Code);

					if(Class == IDCMP_GADGETDOWN)
					{
						if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
							ActiveGadget = Gadget;
					}

					if(Class == IDCMP_ACTIVEWINDOW && ActiveGadget)
						ActivateGadget(ActiveGadget,PanelWindow,NULL);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_GADGETUP)
					{
						switch(Gadget -> GadgetID)
						{
							case 0: if(Code != '\t')
								{
									strcpy(Buffer,GT_STRING(GadgetArray[0]));

									Success = TRUE;

									Terminated = TRUE;
								}

								break;

							case 1:	strcpy(Buffer,GT_STRING(GadgetArray[0]));

								Success = TRUE;

								Terminated = TRUE;
								break;

							case 2:	BlockWindow(PanelWindow);

								if(FileRequest = GetFile(LocaleString(MSG_TERMAUX_LOAD_FILE_TXT),"","",DummyBuffer,NULL,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_LOAD_TXT)))
								{
									GT_SetGadgetAttrs(GadgetArray[0],PanelWindow,NULL,
										GTST_String,DummyBuffer,
									TAG_DONE);

									FreeAslRequest(FileRequest);
								}

								ReleaseWindow(PanelWindow);

								break;

							case 3:	Terminated = TRUE;
								break;
						}
					}
				}
			}

			ActiveGadget = NULL;

			RemoveGList(PanelWindow,GadgetList,(UWORD)-1);

			PopWindow();

			CloseWindow(PanelWindow);
		}
	}

	FreeGadgets(GadgetList);

	return(Success);
}

	/* WakeUp(struct Window *Window):
	 *
	 *	Pop a window to the front and alert the user.
	 */

VOID __regargs
WakeUp(struct Window *Window,BYTE Activate)
{
	if(Window)
	{
		if(Window -> WScreen -> LeftEdge > 0)
		{
			if(Window -> WScreen -> TopEdge > 0)
				MoveScreen(Window -> WScreen,-Window -> WScreen -> LeftEdge,-Window -> WScreen -> TopEdge);
			else
				MoveScreen(Window -> WScreen,-Window -> WScreen -> LeftEdge,0);
		}
		else
		{
			if(Window -> WScreen -> TopEdge > 0)
				MoveScreen(Window -> WScreen,0,-Window -> WScreen -> TopEdge);
		}

		ScreenToFront(Window -> WScreen);

		if(Activate)
			ActivateWindow(Window);
	}

	Beep();

	if(Activate)
		WaitTime(2,0);
}

	/* SendAmigaDOSCommand(UBYTE *Name):
	 *
	 *	Let the current default Shell execute an AmigaDOS
	 *	command. Block until the command has returned.
	 */

VOID
SendAmigaDOSCommand(UBYTE *Name)
{
	BYTE DummyBuffer[2];
	BPTR File;

		/* Let the asynchronous Rexx server pick up and process
		 * all incoming messages rather than sending them to the
		 * main process (hopefully avoids deadlock situations).
		 */

	BatchMode = TRUE;

		/* Open the console window or whatever the user
		 * wants us to open here.
		 */

	if(File = Open(WindowName,MODE_NEWFILE))
	{
		struct ConUnit	*ConUnit = GetConUnit(((struct FileHandle *)BADDR(File)) -> fh_Type);
		STRPTR		 WindowSpec;

		if(ConUnit)
		{
			if(ConUnit -> cu_Window)
				BumpWindow(ConUnit -> cu_Window);
		}

			/* Make the Shell execute the command. */

		SystemTags(Name,SYS_Output,File,TAG_DONE);

			/* Wait for some impressive user action. */

		if(IsInteractive(File) && (WindowSpec = FilePart(WindowName)))
		{
			BYTE IsAuto = FALSE;
			WORD i;

			for(i = 0 ; i < strlen(WindowSpec) - 3 ; i++)
			{
				if(!Strnicmp(&WindowSpec[i],"WAIT",4))
				{
					IsAuto = TRUE;

					break;
				}
			}

			if(!IsAuto)
			{
				Write(File,LocaleString(MSG_TERMAUX_PRESS_RETURN_TO_CONTINUE_TXT),strlen(LocaleString(MSG_TERMAUX_PRESS_RETURN_TO_CONTINUE_TXT)));

				Read(File,DummyBuffer,1);
			}
		}

		Close(File);
	}

		/* Bring the `term' main screen to the front and
		 * fall back into asynchronous Rexx message processing.
		 */

	BumpWindow(Window);

	BatchMode = FALSE;
}

	/* RexxBackgroundServer():
	 *
	 *	The background process to handle the rexx
	 *	massaging.
	 */

VOID __saveds
RexxBackgroundServer()
{
	BPTR		 OldCOS,OldCIS,NewCOS,NewCIS;
	struct MsgPort	*OldConsoleTask,*ReplyPort;

	BYTE		 DummyBuffer[2];

	struct RexxMsg	*RexxMsg;

	struct Process	*BackgroundProcess;
	struct Message	*SetupMsg;

		/* Look who we are. */

	BackgroundProcess = (struct Process *)SysBase -> ThisTask;

		/* Wait for startup message. */

	WaitPort(&BackgroundProcess -> pr_MsgPort);

		/* Pick the message up. */

	SetupMsg = GetMsg(&BackgroundProcess -> pr_MsgPort);

		/* Create a reply port the Rexx message is to
		 * return to after processing.
		 */

	if(ReplyPort = (struct MsgPort *)CreateMsgPort())
	{
			/* Remember previous I/O streams and
			 * console handler.
			 */

		OldCOS		= BackgroundProcess -> pr_COS;
		OldCIS		= BackgroundProcess -> pr_CIS;

		OldConsoleTask	= BackgroundProcess -> pr_ConsoleTask;

			/* The following lines perform an `almost illegal'
			 * action: new console I/O streams are opened for
			 * the `term' main process. This is due to a rather
			 * helpful Rexx server feature. Errors, messages
			 * and other data are sent to the current output
			 * stream.
			 */

		if(NewCIS = Open(WindowName,MODE_NEWFILE))
		{
			struct FileHandle	*FileHandle = (struct FileHandle *)BADDR(NewCIS);
			struct ConUnit		*ConUnit = GetConUnit(((struct FileHandle *)BADDR(NewCIS)) -> fh_Type);

				/* Lock until we're done with the forgery. */

			Forbid();

			BackgroundProcess -> pr_ConsoleTask	= (APTR)FileHandle -> fh_Type;

			BackgroundProcess -> pr_CIS		= NewCIS;
			BackgroundProcess -> pr_COS		= NewCOS = Open("*",MODE_NEWFILE);

			Permit();

			RexxWindow = NULL;

			if(ConUnit)
			{
				if(RexxWindow = ConUnit -> cu_Window)
					BumpWindow(RexxWindow);
			}

				/* Send the command and wait for a reply. */

			if(SendRexxCommand(ReplyPort,SetupMsg -> mn_Node . ln_Name,".term",TermIDString))
			{
				ULONG SignalSet;

				do
				{
					SignalSet = Wait((1 << ReplyPort -> mp_SigBit) | SIGBREAKF_CTRL_C);

					if(SignalSet & SIGBREAKF_CTRL_C)
						Signal(ThisProcess,SIGBREAKF_CTRL_D);
				}
				while(!(SignalSet & (1 << ReplyPort -> mp_SigBit)));

					/* Pick up the RexxMsg (SendRexxCommand
					 * had allocated it for us) and
					 * examine the return codes.
					 */

				if(RexxMsg = (struct RexxMsg *)GetMsg(ReplyPort))
				{
					if(!ExitQuietly)
					{
						STRPTR WindowSpec;

						/* This doesn't look too
						 * good, does it?
						 */

						if(RexxMsg -> rm_Result1)
							FPrintf(NewCIS,LocaleString(MSG_TERMAUX_COMMAND_HAS_TERMINATED_TXT),SetupMsg -> mn_Node . ln_Name,RexxMsg -> rm_Result1,RexxMsg -> rm_Result2);

						/* Show our hand and return
						 * to the usual business.
						 */

						if(IsInteractive(NewCIS) && (WindowSpec = FilePart(WindowName)))
						{
							BYTE IsAuto = FALSE;
							WORD i;

							for(i = 0 ; i < strlen(WindowSpec) - 3 ; i++)
							{
								if(!Strnicmp(&WindowSpec[i],"WAIT",4))
								{
									IsAuto = TRUE;

									break;
								}
							}

							if(!IsAuto)
							{
								Write(NewCIS,LocaleString(MSG_TERMAUX_PRESS_RETURN_TO_CONTINUE_TXT),strlen(LocaleString(MSG_TERMAUX_PRESS_RETURN_TO_CONTINUE_TXT)));

								Read(NewCIS,DummyBuffer,1);
							}
						}

						Term2Front();
					}
					else
						ExitQuietly = FALSE;

						/* Release the message. */

					FreeRexxCommand(RexxMsg);
				}
			}

			Forbid();

			RexxWindow = NULL;

			Permit();

			BumpWindow(Window);

				/* Close our fake I/O streams. */

			Forbid();

			Close(NewCIS);
			Close(NewCOS);

				/* And install the previous pointers. */

			BackgroundProcess -> pr_ConsoleTask	= (APTR)OldConsoleTask;

			BackgroundProcess -> pr_CIS		= OldCIS;
			BackgroundProcess -> pr_COS		= OldCOS;

			Permit();
		}

			/* Remove the reply port. */

		DeleteMsgPort(ReplyPort);
	}

		/* We are done, lock and reply the message causing the
		 * main process to return to the input loop.
		 */

	Forbid();

	ReplyMsg(SetupMsg);
}

	/* SendARexxCommand(UBYTE *Name):
	 *
	 *	Let the ARexx server execute a command (or a script
	 *	file if necessary) and block until the command
	 *	has returned.
	 */

VOID
SendARexxCommand(UBYTE *Name)
{
	struct Process	*BackgroundProcess;
	struct Message	*SetupMsg;
	struct MsgPort	*ReplyPort;

	ULONG		 SignalSet;

		/* Create a reply port for the info message. */

	if(ReplyPort = CreateMsgPort())
	{
			/* Allocate the message body. */

		if(SetupMsg = (struct Message *)AllocVec(sizeof(struct Message),MEMF_ANY|MEMF_CLEAR))
		{
				/* Set up the message itself. */

			SetupMsg -> mn_Node . ln_Name	= Name;
			SetupMsg -> mn_ReplyPort	= ReplyPort;
			SetupMsg -> mn_Length		= sizeof(struct Message);

				/* Create the background process which will
				 * handle all the messy rexx message sending
				 * for us.
				 */

			if(BackgroundProcess = (struct Process *)CreateNewProcTags(
				NP_Entry,	RexxBackgroundServer,
				NP_Name,	"term ARexx background process",
				NP_StackSize,	16384,
			TAG_END))
			{
				SetSignal(0,SIGBREAKF_CTRL_D);

					/* Send the startup message. */

				PutMsg(&BackgroundProcess -> pr_MsgPort,SetupMsg);

					/* Go into loop and wait for the
					 * background process to return.
					 */

				FOREVER
				{
					SignalSet = Wait(SIG_REXX | (1 << ReplyPort -> mp_SigBit));

						/* Yet another rexx message. */

					if(SignalSet & SIG_REXX)
						HandleRexx();

						/* The background server has
						 * finished.
						 */

					if(SignalSet & (1 << ReplyPort -> mp_SigBit))
						break;
				}

				SetSignal(0,SIGBREAKF_CTRL_D);
			}

				/* Free the message. */

			FreeVec(SetupMsg);
		}

			/* Delete the reply port. */

		DeleteMsgPort(ReplyPort);
	}
}

	/* ahtoi(UBYTE *String):
	 *
	 *	Turn a hexadecimal string into an integer (borrowed from
	 *	Matt Dillon's dmouse.c).
	 */

LONG
ahtoi(UBYTE *String)
{
	LONG Value = 0;
	UBYTE c;

	while(c = *String)
	{
		Value <<= 4;

		if(c >= '0' && c <= '9')
			Value |= (c & 15);
		else
			Value |= (c & 15) + 9;

		++String;
	}

	return(Value);
}

	/* BlockWindow(struct Window *Window):
	 *
	 *	Block a window (show a wait mouse pointer, block
	 *	windows, custom and system gadgets) by adding
	 *	an invisible requester to it.
	 */

VOID __regargs
BlockWindow(struct Window *Window)
{
	if(Window)
	{
		struct Requester	*InvisibleRequest = NULL;
		WORD			 i;

		Forbid();

			/* Is this window already on the list? */

		for(i = 0 ; i < 10 ; i++)
		{
			if(RequesterList[i] . RWindow == Window)
			{
				Permit();

				return;
			}
		}

			/* Look for an empty slot. */

		for(i = 0 ; i < 10 ; i++)
		{
			if(!RequesterList[i] . RWindow)
			{
				InvisibleRequest = &RequesterList[i];

				InvisibleRequest -> RWindow = (struct Window *)~0;

				break;
			}
		}

		Permit();

			/* Install the wait mouse pointer. */

		SetWait(Window);

			/* Install the requester if possible. */

		if(InvisibleRequest)
			Request(InvisibleRequest,Window);
	}
}

	/* ReleaseWindow(struct Window *Window):
	 *
	 *	Release a window blocked by `BlockWindow()'.
	 */

VOID __regargs
ReleaseWindow(struct Window *Window)
{
	if(Window)
	{
		struct Requester	*InvisibleRequest = NULL;
		WORD			 i;

			/* Search all slots for the requester to block this window. */

		for(i = 0 ; i < 10 ; i++)
		{
			if(RequesterList[i] . RWindow == Window)
			{
				InvisibleRequest = &RequesterList[i];

				break;
			}
		}

			/* Restore the mouse pointer. */

		ClearPointer(Window);

			/* If disabled, restore the window. */

		if(InvisibleRequest)
		{
				/* End the requester activity. */

			EndRequest(InvisibleRequest,Window);

				/* Reset it to zeroes. */

			memset(InvisibleRequest,0,sizeof(struct Requester));
		}
	}
}

	/* BlockWindows():
	 *
	 *	Block the main window and the status window (i.e. disable
	 *	the menu and attach a wait pointer).
	 */

VOID
BlockWindows()
{
	if(!(BlockNestCount++))
	{
		BlockWindow(Window);
		BlockWindow(StatusWindow);
		BlockWindow(PacketWindow);
		BlockWindow(FastWindow);
		BlockWindow(ReviewWindow);

		WeAreBlocking = TRUE;
	}
}

	/* ReleaseWindows():
	 *
	 *	Reenable the menus and clear the wait pointer.
	 */

VOID
ReleaseWindows()
{
	if(!(--BlockNestCount))
	{
		ReleaseWindow(Window);
		ReleaseWindow(StatusWindow);
		ReleaseWindow(PacketWindow);
		ReleaseWindow(FastWindow);
		ReleaseWindow(ReviewWindow);

		WeAreBlocking = FALSE;
	}
}

	/* LineRead(BPTR File,UBYTE *Buffer,LONG MaxChars):
	 *
	 *	Read a few bytes from a file (à la gets).
	 */

LONG
LineRead(BPTR File,UBYTE *Buffer,LONG MaxChars)
{
	STATIC	UBYTE	Data[1024];

	STATIC	LONG	RIdx = 0,
			RLen = 0;

		LONG	BytesRead = 0,
			i;

	if(File)
	{
		for(i = 0 ; i < MaxChars ; i++)
		{
			if(RIdx >= RLen)
			{
				RLen = Read(File,Data,1024);

				RIdx = 0;

				if(RLen <= 0)
				{
					Buffer[i] = 0;

					return(BytesRead);
				}
			}

			BytesRead++;

			if((Buffer[i] = Data[RIdx++]) == '\n')
			{
				Buffer[i + 1] = 0;

				return(BytesRead);
			}
		}

		return(BytesRead);
	}
	else
		RIdx = RLen = 0;
}

	/* FlowInit():
	 *
	 *	Set up the data flow parser. The parser scans the serial
	 *	output data for more or less interesting modem output
	 *	(carrier lost, connect, etc.).
	 */

VOID
FlowInit()
{
	FlowInfo . Changed = FALSE;

	FlowInfo . NoCarrier = FlowInfo . ZModemUpload = FlowInfo . Connect = FlowInfo . Voice = FlowInfo . Ring = FlowInfo . Busy = FlowInfo . NoDialTone = FALSE;

	if(Config . NoCarrier[0])
		SPrintf(AttentionBuffers[0],"%s\r",Config . NoCarrier);
	else
		AttentionBuffers[0][0] = 0;

	if(Config . Ring[0])
		SPrintf(AttentionBuffers[1],"%s\r",Config . Ring);
	else
		AttentionBuffers[1][0] = 0;

	if(Config . Voice[0])
		SPrintf(AttentionBuffers[2],"%s\r",Config . Voice);
	else
		AttentionBuffers[2][0] = 0;

	if(Config . Busy[0])
		SPrintf(AttentionBuffers[6],"%s\r",Config . Busy);
	else
		AttentionBuffers[6][0] = 0;

	if(Config . NoDialTone[0])
		SPrintf(AttentionBuffers[7],"%s\r",Config . NoDialTone);
	else
		AttentionBuffers[7][0] = 0;

	strcpy(AttentionBuffers[3],Config . Connect);

	strcpy(AttentionBuffers[4],"*\030B01");
	strcpy(AttentionBuffers[5],"**\030B01");

	FlowCount = 0;

	memset(&AttentionCount[0],0,8);

	BaudPending = FALSE;

	FullCheck = FALSE;
}

	/* FlowFilter(UBYTE Char):
	 *
	 *	Send a character through the data flow parser and look
	 *	if it's part of a modem message.
	 */

VOID
FlowFilter(UBYTE Char)
{
	if(Char)
	{
		STATIC	BYTE IgnoreChar;
			BYTE i,Matches = 0,Start,End,WasOnline = Online;

			/* Full data check is a lot slower than looking for
			 * just a single sequence (such as the `CONNECT'
			 * below). This mode is reserved for the dial panel.
			 */

		if(FullCheck)
		{
			Start	= 0;
			End	= 8;
		}
		else
		{
			Start	= 0;

			if(UsesZModem)
				End = 6;
			else
				End = 4;
		}

			/* We already got a `CONNECT' and the
			 * `connect auto-baud' feature is enabled.
			 * Continue scanning the serial output
			 * data for the actual baud rate.
			 */

		if(BaudPending)
		{
			if(Char >= '0' && Char <= '9')
			{
				BaudBuffer[BaudCount++] = Char;

				if(IgnoreChar)
					IgnoreChar = 0;
			}
			else
			{
					/* The scanner has found a
					 * non-numerical character.
					 * This is either a blank
					 * space or something else.
					 * The latter tells us
					 * that the baud rate has
					 * been identified and is
					 * ready to be used.
					 */

				if(Char != IgnoreChar)
				{
					BaudBuffer[BaudCount] = 0;

					BaudPending = FALSE;
				}
			}
		}

		if(!BaudPending)
		{
				/* Scan all ID strings for matches. */

			for(i = Start ; i < End ; i++)
			{
					/* This sequence is a probable
					 * match.
					 */

				if(AttentionCount[i] == FlowCount)
				{
						/* Does the character
						 * fit into the sequence?
						 */

					if(Char == AttentionBuffers[i][FlowCount])
					{
							/* Increment the
							 * number of matching
							 * characters in this
							 * sequence.
							 */

						AttentionCount[i]++;

							/* Did we hit the
							 * last character
							 * in the sequence?
							 */

						if(AttentionBuffers[i][FlowCount + 1])
							Matches++;
						else
						{
							Matches = 0;

								/* We've got a valid
								 * sequence, now look
								 * which flags to change.
								 */

							switch(i)
							{
									/* We got a `no carrier' message. */

								case 0:	if(Config . CheckCarrier)
									{
										WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

										DoIO(WriteRequest);

											/* Are we to take a look at the carrier bit
											 * and if so, is the carrier still
											 * present?
											 */

										if(!(WriteRequest -> io_Status & (1 << 5)))
											break;
									}

									if(!FlowInfo . NoCarrier)
									{
										FlowInfo . NoCarrier	= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									Online = FALSE;

										/* Clear the password. */

									Password[0] = 0;
									UserName[0] = 0;

									if(WasOnline)
									{
										if(CurrentPay && ChosenEntry)
											LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_COST_TXT),CreateSum(CurrentPay,TRUE));
										else
											LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

										Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));
									}

									break;

									/* Got another call. */

								case 1:	if(!FlowInfo . Ring)
									{
										FlowInfo . Ring		= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									break;

									/* Got a voice call. */

								case 2:	if(!FlowInfo . Voice)
									{
										FlowInfo . Voice	= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									break;

									/* Got a connect message. */

								case 3:	if(!FlowInfo . Connect)
									{
										FlowInfo . Connect	= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									if(Config . ConnectAutoBaud)
									{
										BaudBuffer[0]	= 0;

										BaudPending	= TRUE;
										BaudCount	= 0;

										IgnoreChar	= ' ';
									}

									break;

									/* Got the ZModem inquiry sequence. */

								case 4:
								case 5:	if(!FlowInfo . ZModemUpload)
									{
										FlowInfo . ZModemUpload	= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									break;

									/* Line is busy. */

								case 6:	if(!FlowInfo . Busy)
									{
										FlowInfo . Busy		= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									break;

								case 7:	if(!FlowInfo . NoDialTone)
									{
										FlowInfo . NoDialTone	= TRUE;
										FlowInfo . Changed	= TRUE;
									}

									break;
							}
						}
					}
				}
			}

				/* We've got a good match (recognized
				 * a sequence, so reset the data flow
				 * scanner.
				 */

			if(!Matches)
			{
				if(FlowCount)
				{
					FlowCount = 0;

					memset(&AttentionCount[0],0,8);
				}
			}
			else
				FlowCount++;
		}
		else
		{
				/* This checks for just a single sequence:
				 * the notorious `NO CARRIER'.
				 */

			if(AttentionCount[0] == FlowCount)
			{
				if(Char == AttentionBuffers[0][FlowCount])
				{
					AttentionCount[0]++;

					if(AttentionBuffers[0][FlowCount + 1])
						Matches++;
					else
					{
						Matches = 0;

						if(!FlowInfo . NoCarrier)
						{
							FlowInfo . NoCarrier	= TRUE;
							FlowInfo . Changed	= TRUE;
						}

						Online = FALSE;

							/* Clear the password. */

						Password[0] = 0;
						UserName[0] = 0;

						if(WasOnline)
						{
							if(CurrentPay)
								LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_COST_TXT),CreateSum(CurrentPay,TRUE));
							else
								LogAction(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));

							Say(LocaleString(MSG_TERMAUX_CARRIER_LOST_TXT));
						}
					}
				}
			}

			if(!Matches)
			{
				if(FlowCount)
				{
					FlowCount = 0;

					memset(&AttentionCount[0],0,8);
				}
			}
			else
				FlowCount++;
		}
	}
}

	/* LoadMacros(UBYTE *Name,struct MacroKeys *Keys):
	 *
	 *	Load the keyboard macros from a file.
	 */

BYTE
LoadMacros(UBYTE *Name,struct MacroKeys *Keys)
{
	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))
			{
				/* Collect version number ID if
				 * available.
				 */

				if(!PropChunks(Handle,&VersionProps[0],1))
				{
					/* The following line tells iffparse to stop at the
					 * very beginning of a `Type' chunk contained in a
					 * `TERM' FORM chunk.
					 */

					if(!StopChunk(Handle,'TERM','KEYS'))
					{
						/* Parse the file... */

						if(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
							/* Did we get a version ID? */

							if(Prop = FindProp(Handle,'TERM','VERS'))
							{
								TermInfo = (struct TermInfo *)Prop -> sp_Data;

								/* Is it the file format we are able
								 * to read?
								 */

								if((TermInfo -> Version > TermVersion) || (TermInfo -> Version == TermVersion && TermInfo -> Revision > TermRevision) || (TermInfo -> Version == 1 && TermInfo -> Revision < 6))
								{
										/* Probably an older revision. */

									if(TermInfo -> Version == 1 && TermInfo -> Revision < 6)
									{
										memset(Keys,0,sizeof(struct MacroKeys));

										if(ReadChunkBytes(Handle,Keys,10 * 256) == 10 * 256)
											Success = TRUE;
									}
								}
								else
								{
									/* The file read pointer is positioned
									 * just in front of the first data
									 * to be read, so let's don't disappoint
									 * iffparse and read it.
									 */

									if(ReadChunkBytes(Handle,Keys,sizeof(struct MacroKeys)) == sizeof(struct MacroKeys))
										Success = TRUE;
								}
							}
							else
							{
									/* File was created by WriteIFFData previous
									 * to revision 1.4.
									 */

								memset(Keys,0,sizeof(struct MacroKeys));

								if(ReadChunkBytes(Handle,Keys,10 * 256) == 10 * 256)
									Success = TRUE;
							}
						}
					}
				}

				CloseIFF(Handle);
			}

			Close(Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	return(Success);
}

	/* FindThisItem(ULONG MenuID):
	 *
	 *	Scan the main menu for a menuitem associated with a
	 *	menu ID.
	 */

struct MenuItem *
FindThisItem(ULONG MenuID)
{
	struct Menu	*FirstMenu;
	struct MenuItem	*FirstItem;

	FirstMenu = Menu;

	while(FirstMenu)
	{
		FirstItem = FirstMenu -> FirstItem;

		while(FirstItem)
		{
			if((ULONG)GTMENUITEM_USERDATA(FirstItem) == MenuID)
				return(FirstItem);
			else
				FirstItem = FirstItem -> NextItem;
		}

		FirstMenu = FirstMenu -> NextMenu;
	}

	return(NULL);
}

	/* GetFileSize(UBYTE *Name):
	 *
	 *	Simple routine to return the size of a file in
	 *	bytes.
	 */

LONG
GetFileSize(UBYTE *Name)
{
	struct FileInfoBlock __aligned	FileInfo;
	BPTR				FileLock;
	LONG				FileSize = 0;

	if(FileLock = Lock(Name,ACCESS_READ))
	{
		if(Examine(FileLock,&FileInfo))
			FileSize = FileInfo . fib_Size;

		UnLock(FileLock);
	}

	return(FileSize);
}

	/* GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag,BYTE MultiSelect):
	 *
	 *	Call the asl.library file requester to select a single or
	 *	a couple of files.
	 */

struct FileRequester *
GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag,BYTE MultiSelect,BYTE DirsOnly,UBYTE *OKText)
{
	struct FileRequester	*AslFileRequest;
	BYTE			 Result		= FALSE,
				 DefaultPattern	= FALSE;
	LONG			 Flags,ExtFlags = 0;
	WORD			 i,j;

	STATIC UBYTE		 DirBuffer[256],
				 PatternBuffer[256],
				 OtherTitle[40];

		/* We use this tag array to remember the size and
		 * position of the asl requester window.
		 */

	STATIC struct {
		ULONG Tag1,	LeftEdge;
		ULONG Tag2,	TopEdge;
		ULONG Tag3,	Width;
		ULONG Tag4,	Height;
		ULONG Tag5;
	} Dims = {
		ASL_LeftEdge,	-1,
		ASL_TopEdge,	-1,
		ASL_Width,	-1,
		ASL_Height,	-1,

		TAG_DONE
	};

	for(i = j = 0 ; i < strlen(Title) ; i++)
	{
		if(Title[i] != '_')
			OtherTitle[j++] = Title[i];
	}

	OtherTitle[j] = 0;

	Title = OtherTitle;

	if(DirsOnly)
	{
		ExtFlags |= FIL1F_NOFILES;

		if(Pattern)
			ExtFlags |= FIL1F_MATCHDIRS;
	}

		/* Empty directory string? Revert to the last directory
		 * name.
		 */

	if(!Directory[0])
	{
		if(!DirBuffer[0])
		{
			if(!GetCurrentDirName(DirBuffer,256))
				DirBuffer[0] = 0;
		}

		Directory = DirBuffer;
	}

		/* If a wildcard pattern is required, add a gadget
		 * to display it.
		 */

	if(Pattern)
	{
		Flags = FILF_PATGAD;

		if(!Pattern[0])
		{
			DefaultPattern = TRUE;

			if(PatternBuffer[0])
				Pattern = PatternBuffer;
			else
				Pattern = "#?";
		}
		else
		{
			if(!strcmp(Pattern,"#?") || !Stricmp(Pattern,PatternBuffer))
			{
				DefaultPattern = TRUE;

				Pattern = PatternBuffer;
			}
		}
	}
	else
	{
		Flags = 0;

		Pattern = "#?";
	}

		/* Set the save flag if we are about to save something. */

	if(SaveFlag)
		Flags |= FILF_SAVE;

		/* Set the multiselect bit if multiple files are
		 * to be selected (e.g. for batch file upload).
		 */

	if(MultiSelect)
		Flags |= FILF_MULTISELECT;

		/* Provide a standard `Ok' text if none
		 * specified.
		 */

	if(!OKText)
		OKText = (SaveFlag ? LocaleString(MSG_GLOBAL_SAVE_TXT) : LocaleString(MSG_GLOBAL_OPEN_TXT));

		/* Allocate the asl.library directory requester
		 * and display it.
		 */

	if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
		ASL_Window,	Window,
		ASL_File,	Name,
		ASL_Dir,	Directory,
		ASL_Hail,	Title,
		ASL_FuncFlags,	Flags | FILF_NEWIDCMP,
		ASL_Pattern,	Pattern,
		ASL_OKText,	OKText,
		ASL_ExtFlags1,	ExtFlags,
#ifdef ASLSM_FilterFunc
		ASLFR_TextAttr,	&DefaultFont,
#endif	/* ASLSM_FilterFunc */
		(Dims . LeftEdge == -1) ? TAG_DONE : TAG_MORE,&Dims))
	{
		if(AslRequestTags(AslFileRequest,TAG_DONE))
		{
			Dims . LeftEdge	= AslFileRequest -> rf_LeftEdge;
			Dims . TopEdge	= AslFileRequest -> rf_TopEdge;
			Dims . Width	= AslFileRequest -> rf_Width;
			Dims . Height	= AslFileRequest -> rf_Height;

			if(!DirsOnly)
			{
					/* Do we have a valid file name? */

				if(AslFileRequest -> rf_File[0])
				{
						/* Build a legal path/file string. */

					strcpy(Buffer,AslFileRequest -> rf_Dir);

					AddPart((UBYTE *)Buffer,(UBYTE *)AslFileRequest -> rf_File,256);

					Result = TRUE;

					strcpy(DirBuffer,AslFileRequest -> rf_Dir);
				}
			}
			else
			{
				if(AslFileRequest -> rf_Dir[0])
				{
					strcpy(Buffer,AslFileRequest -> rf_Dir);

					Result = TRUE;

					strcpy(DirBuffer,AslFileRequest -> rf_Dir);
				}
			}
		}
	}

		/* We didn't get a file, no need to keep the
		 * file requester.
		 */

	if(!Result && AslFileRequest)
	{
		FreeAslRequest(AslFileRequest);

		return(NULL);
	}
	else
	{
		if(DefaultPattern)
			strcpy(PatternBuffer,AslFileRequest -> rf_Pat);

		return(AslFileRequest);
	}
}

	/* MyEasyRequest(struct Window *Window,UBYTE *Text,UBYTE *Gadgets,...):
	 *
	 *	Really quite simple varargs version of Intuition's
	 *	EasyRequest requester.
	 */

WORD __stdargs
MyEasyRequest(struct Window *Window,UBYTE *Text,UBYTE *Gadgets,...)
{
	struct EasyStruct	Easy;
	WORD			Result;
	ULONG			IDCMP = NULL;
	va_list	 		VarArgs;

		/* Standard data. */

	Easy . es_StructSize	= sizeof(struct EasyStruct);
	Easy . es_Flags		= NULL;
	Easy . es_Title		= (UBYTE *)LocaleString(MSG_TERMAUX_TERM_REQUEST_TXT);
	Easy . es_TextFormat	= (UBYTE *)Text;
	Easy . es_GadgetFormat	= (UBYTE *)Gadgets;

		/* Use the argument array to build the
		 * requester and display it.
		 */

	va_start(VarArgs,Gadgets);
	Result = EasyRequestArgs(Window,&Easy,&IDCMP,VarArgs);
	va_end(VarArgs);

	return(Result);
}

	/* SelectTime():
	 *
	 *	Searches for the correct unit/pay conversion values.
	 */

VOID
SelectTime(struct PhoneEntry *SomeEntry)
{
	struct TimeDateNode		*TimeDateNode;
	struct DateStamp __aligned	 Date;
	struct ClockData		 ClockData;
	BYTE				 FoundOne = FALSE;

		/* Obtain current time and date. */

	DateStamp(&Date);

		/* Convert into a more suitable form (note: seconds are
		 * required as an input value).
		 */

	Amiga2Date(Date . ds_Days * 86400 + Date . ds_Minute * 60 + Date . ds_Tick / TICKS_PER_SECOND,&ClockData);

		/* Apparently, in the US the week starts with Sunday, we'll
		 * wrap the week around to let it start with Monday.
		 */

	if(ClockData . wday)
		ClockData . wday--;
	else
		ClockData . wday = 6;

		/* Change the month, too... */

	ClockData . month--;

		/* First step: search for current day. */

	TimeDateNode = (struct TimeDateNode *)SomeEntry -> TimeDateList . mlh_Head;

		/* Skip first entry. */

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

	while(TimeDateNode -> VanillaNode . ln_Succ)
	{
			/* Does it match a specific date? */

		if(TimeDateNode -> TimeDate . Month == ClockData . month && TimeDateNode -> TimeDate . Day == ClockData . mday)
		{
			FoundOne = TRUE;

			break;
		}

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

		/* Second step: search for week day settings. */

	if(!FoundOne)
	{
		TimeDateNode = (struct TimeDateNode *)SomeEntry -> TimeDateList . mlh_Head;

			/* Skip first entry. */

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

		while(TimeDateNode -> VanillaNode . ln_Succ)
		{
				/* Does it match a specific day? */

			if(TimeDateNode -> TimeDate . Month == -1 && (TimeDateNode -> TimeDate . Day & (1 << ClockData . wday)))
			{
				FoundOne = TRUE;

				break;
			}

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

		/* Third step: use default settings. */

	if(!FoundOne)
		TimeDateNode = (struct TimeDateNode *)SomeEntry -> TimeDateList . mlh_Head;

		/* Fill in the remaining data. */

	PayPerUnit[0]	= TimeDateNode -> TimeDate . PayPerUnit[0];
	PayPerUnit[1]	= TimeDateNode -> TimeDate . PayPerUnit[1];

	SecPerUnit[0]	= TimeDateNode -> TimeDate . SecPerUnit[0];
	SecPerUnit[1]	= TimeDateNode -> TimeDate . SecPerUnit[1];

	TimeOfDay[0]	= TimeDateNode -> TimeDate . TimeOfDay[0];
	TimeOfDay[1]	= TimeDateNode -> TimeDate . TimeOfDay[1];
}

	/* Raise(UWORD Colour):
	 *
	 *	Make an RGB value brighter.
	 */

STATIC UWORD __regargs
Raise(UWORD Colour)
{
	WORD r,g,b;

	r =  (Colour >> 8)        + 4;
	g = ((Colour >> 4) & 0xF) + 4;
	b = ((Colour     ) & 0xF) + 4;

	if(r > 15)
		r = 15;

	if(g > 15)
		g = 15;

	if(b > 15)
		b = 15;

	return(r << 8 | g << 4 | b);
}

	/* VisualBeep():
	 *
	 *	Handle the visual part of the display beep.
	 */

STATIC BYTE
VisualBeep()
{
	struct UCopList	*UserCopperList;

		/* Create a user copper list. */

	if(UserCopperList = (struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_ANY|MEMF_CLEAR))
	{
			/* Initialize for 35 commands. */

		if(UCopperListInit(UserCopperList,1 + 16 + 1 + 16 + 1))
		{
			WORD i;

				/* Wait until first line of window. */

			CWAIT(UserCopperList,Window -> TopEdge,0);

				/* Set the light colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],Raise(GetRGB4(Screen -> ViewPort . ColorMap,i)));

				/* Wait until bottom of window. */

			CWAIT(UserCopperList,Window -> TopEdge + Window -> Height - 1,0);

				/* Set the standard colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],GetRGB4(Screen -> ViewPort . ColorMap,i));

				/* Finish list. */

			CEND(UserCopperList);

				/* Install user copper list... */

			Screen -> ViewPort . UCopIns = UserCopperList;

				/* ...and display it. */

			RethinkDisplay();

			return(TRUE);
		}
		else
			FreeMem(UserCopperList,sizeof(struct UCopList));
	}

	return(FALSE);
}

	/* PrintStatusLine(struct RastPort *RPort,STRPTR String):
	 *
	 *	Print a string into the status line,
	 *	checks first to see if the Text() routine is
	 *	likely to wait.
	 */

STATIC VOID __regargs
PrintStatusLine(struct RastPort *RPort,STRPTR String)
{
	if(AttemptLockLayerRom(RPort -> Layer))
	{
		Text(RPort,String,strlen(String));

		UnlockLayerRom(RPort -> Layer);
	}
}

	/* StatusServer():
	 *
	 *	Asynchronous process to continuosly display the current
	 *	terminal settings.
	 */

VOID __saveds
StatusServer()
{
	STATIC struct timeval	 OnlineTime;
	STATIC UBYTE		 OnlineBuffer[20];
	STATIC BYTE		 GotOnline		= FALSE,
				 WasOnline		= FALSE,
				 ShowPay		= FALSE,
				 FlagBit		= FALSE;
	STATIC LONG		 SecCount		= 0,
				 BeepCount		= 0;

	struct RastPort		*RPort;
	UBYTE			 Buffer[80];

	struct timerequest	*TimeRequest;
	struct MsgPort		*TimePort;

	BYTE			 Background		= FALSE,
				 FlashIt		= FALSE,
				 SetColours		= FALSE,
				 StandardColours	= TRUE,
				 KeepGoing		= TRUE,
				 Beeping		= FALSE;

	BYTE			 LastProtocol[40],
				 ProtocolBuffer[20],
				 i;

	BYTE			 LastFont		= -1,
				 LastEmulation		= -1,
				 LastBitsPerChar	= -1,
				 LastParity		= -1,
				 LastStopBits		= -1,
				 LastStatus		= -1;

	LONG			 LastBaud		= -1;

	LONG			 ThisHour,ThisMinute,TestTime;

	LastProtocol[0] = 0;

	LocalizeString(ConfigEmulation,MSG_TERMAUX_ANSI_VT102_TXT,MSG_TERMAUX_EXTERNAL_TXT);
	LocalizeString(ConfigParity,MSG_TERMAUX_NONE_TXT,MSG_TERMAUX_SPACE_TXT);
	LocalizeString(ConfigStatus,MSG_TERMAUX_READY_TXT,MSG_TERMAUX_HANG_UP_TXT);

		/* Create a timer device request. */

	if(TimePort = (struct MsgPort *)CreateMsgPort())
	{
		if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
		{
			if(!OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,0))
			{
					/* Signal our father process
					 * that we're running.
					 */

				Signal(ThisProcess,SIGBREAKF_CTRL_C);

				if(StatusWindow)
				{
					RPort = StatusWindow -> RPort;

					if(Config . StatusLine == STATUSLINE_COMPRESSED)
					{
						MoveSingle(0);

						Text(RPort,"        ·            ·            ·         ·       ·          · 00:00:00",73);
					}
					else
					{
						MoveItem(64,1);

						Text(RPort,"00:00:00",8);
					}
				}
				else
					RPort = NULL;

					/* Keep on displaying. */

				while(KeepGoing)
				{
						/* Are we to quit? */

					if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
					{
						KeepGoing = FALSE;

						SetSignal(0,SIGBREAKF_CTRL_C);
					}

						/* Get the current time. */

					TimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

					DoIO(TimeRequest);

						/* A connection has just
						 * been established.
						 */

					if(Online && !GotOnline)
					{
						OnlineTime = TimeRequest -> tr_time;

						GotOnline = TRUE;

						SecCount = 0;

						FlagBit = FALSE;
					}

						/* Print the current time. */

					ThisHour	= (TimeRequest -> tr_time . tv_secs % 86400) / 3600;
					ThisMinute	= (TimeRequest -> tr_time . tv_secs % 3600) / 60;

					if(RPort)
					{
						SPrintf(Buffer,"%02ld:%02ld:%02ld",ThisHour,ThisMinute,TimeRequest -> tr_time . tv_secs % 60);

						if(Config . StatusLine == STATUSLINE_COMPRESSED)
							MoveSingle(54);
						else
							MoveItem(64,0);

						PrintStatusLine(RPort,Buffer);
					}

					if(Online)
					{
						STATIC struct timeval	LastTime;
						struct timeval		TempTime;

						WasOnline = TRUE;

						if(ChosenEntry)
						{
							if(!(ThisMinute % 10) && !(TimeRequest -> tr_time . tv_secs % 60))
							{
								ChosenInUse = TRUE;

								SelectTime(ChosenEntry);

								ChosenInUse = FALSE;
							}

							TestTime = ThisHour * 6 + ThisMinute / 10;

							if(TestTime >= TimeOfDay[0] && TestTime <= TimeOfDay[1])
								PreferredTime = 0;
							else
							{
								if(TestTime >= TimeOfDay[1] && TestTime <= TimeOfDay[0])
									PreferredTime = 1;
								else
								{
									if(TestTime >= TimeOfDay[0] && TestTime >= TimeOfDay[1])
									{
										if(TimeOfDay[0] > TimeOfDay[1])
											PreferredTime = 0;
										else
											PreferredTime = 1;
									}
									else
									{
										if(TestTime <= TimeOfDay[0] && TestTime <= TimeOfDay[1])
										{
											if(TimeOfDay[0] > TimeOfDay[1])
												PreferredTime = 0;
											else
												PreferredTime = 1;
										}
									}
								}
							}

							if(!CurrentPay)
								CurrentPay = PayPerUnit[PreferredTime];

							FlagBit ^= TRUE;

							if(!FlagBit)
							{
								if(SecPerUnit[PreferredTime])
								{
									if(SecCount == SecPerUnit[PreferredTime])
									{
										SecCount = 0;

										CurrentPay += PayPerUnit[PreferredTime];
									}
								}

								SecCount++;
							}
						}

							/* Show the time
							 * we have been online
							 * yet.
							 */

						TempTime = TimeRequest -> tr_time;

						if(TempTime . tv_secs != LastTime . tv_secs)
						{
							LastTime = TempTime;

							SubTime(&TempTime,&OnlineTime);

							if(!(TempTime . tv_secs % 5) && TempTime . tv_secs)
								ShowPay ^= TRUE;

							SPrintf(OnlineBuffer,"%02ld:%02ld:%02ld",(TempTime . tv_secs % 86400) / 3600,(TempTime . tv_secs % 3600) / 60,TempTime . tv_secs % 60);

							if(ShowPay && CurrentPay && ChosenEntry)
							{
								SPrintf(Buffer,"%s          ",CreateSum(CurrentPay,FALSE));

								Buffer[8] = 0;
							}
							else
								strcpy(Buffer,OnlineBuffer);

							if(RPort)
							{
								if(Config . StatusLine == STATUSLINE_COMPRESSED)
									MoveSingle(65);
								else
									MoveItem(64,1);

								PrintStatusLine(RPort,Buffer);
							}
						}
					}
					else
					{
						if(WasOnline)
						{
							WasOnline = FALSE;

							if(RPort)
							{
								if(Config . StatusLine == STATUSLINE_COMPRESSED)
									MoveSingle(65);
								else
									MoveItem(64,1);

								PrintStatusLine(RPort,OnlineBuffer);
							}
						}

						if(GotOnline)
							GotOnline = FALSE;
					}

						/* Take care of the visual beep
						 * if enabled.
						 */

					if(Beeping)
					{
						if(!(BeepCount--))
						{
							Beeping = FALSE;

								/* Remove the copper list. */

							FreeVPortCopLists(&Screen -> ViewPort);

								/* Really remove it. */

							RemakeDisplay();

								/* Clear the signal bit. */

							SetSignal(0,SIGBREAKF_CTRL_D);
						}
					}

						/* Are we to show a visual beep? */

					if(SetSignal(0,0) & SIGBREAKF_CTRL_D)
					{
						if(Config . SystemBeep)
						{
							DisplayBeep(Screen);

							SetSignal(0,SIGBREAKF_CTRL_D);
						}
						else
						{
							if(!Beeping)
							{
								if(VisualBeep())
								{
									Beeping = TRUE;

									BeepCount = 1;

									SetSignal(0,SIGBREAKF_CTRL_D);
								}
							}
						}
					}

						/* Display the current terminal
						 * status.
						 */

					if(LastStatus != Status)
					{
						LastStatus = Status;

						if(RPort)
						{
							if(Config . StatusLine == STATUSLINE_COMPRESSED)
								MoveSingle(0);
							else
								MoveItem(7,0);

							PrintStatusLine(RPort,ConfigStatus[LastStatus]);
						}
					}

						/* Show the current transfer
						 * protocol.
						 */

					if(strcmp(LastProtocol,FilePart(Config . Protocol)))
					{
						strcpy(LastProtocol,FilePart(Config . Protocol));

						strcpy(ProtocolBuffer,"        ");

						for(i = 0 ; i < 8 ; i++)
						{
							if(!LastProtocol[i + 3] || LastProtocol[i + 3] == '.')
								break;
							else
								ProtocolBuffer[i] = LastProtocol[i + 3];
						}

						if(RPort)
						{
							if(Config . StatusLine == STATUSLINE_COMPRESSED)
								MoveSingle(23);
							else
								MoveItem(25,0);

							PrintStatusLine(RPort,ProtocolBuffer);
						}
					}

						/* Show the current baud
						 * rate.
						 */

					if(LastBaud != Config . BaudRate)
					{
						LastBaud = Config . BaudRate;

						if(RPort)
						{
							extern APTR LocaleBase;

							if(LocaleBase)
								SPrintf(Buffer,"%lD        ",LastBaud);
							else
								SPrintf(Buffer,"%ld        ",LastBaud);

							Buffer[7] = 0;

							if(Config . StatusLine == STATUSLINE_COMPRESSED)
								MoveSingle(36);
							else
								MoveItem(47,0);

							PrintStatusLine(RPort,Buffer);
						}
					}

						/* Show the current
						 * terminal font.
						 */

					if(LastFont != Config . Font)
					{
						LastFont = Config . Font;

						if(RPort)
						{
							if(Config . StatusLine != STATUSLINE_COMPRESSED)
							{
								MoveItem(7,1);

								PrintStatusLine(RPort,ConfigFont[LastFont]);
							}
						}
					}

						/* Show the current terminal
						 * emulation.
						 */

					if(Config . Emulation == 3)
					{
						LastEmulation = Config . Emulation;

						if(RPort)
						{
							SPrintf(Buffer,"%s         ",EmulationName);

							Buffer[10] = 0;

							if(Config . StatusLine == STATUSLINE_COMPRESSED)
								MoveSingle(10);
							else
								MoveItem(25,1);

							PrintStatusLine(RPort,Buffer);
						}
					}
					else
					{
						if(LastEmulation != Config . Emulation)
						{
							LastEmulation = Config . Emulation;

							if(RPort)
							{
								if(Config . StatusLine == STATUSLINE_COMPRESSED)
									MoveSingle(10);
								else
									MoveItem(25,1);

								PrintStatusLine(RPort,ConfigEmulation[LastEmulation]);
							}
						}
					}

						/* Show the current serial
						 * parameters (parity, etc).
						 */

					if(LastBitsPerChar != Config . BitsPerChar || LastParity != Config . Parity || LastStopBits != Config . StopBits)
					{
						LastBitsPerChar	= Config . BitsPerChar;
						LastParity	= Config . Parity;
						LastStopBits	= Config . StopBits;

						if(RPort)
						{
							if(Config . StatusLine == STATUSLINE_COMPRESSED)
							{
								STATIC UBYTE Parities[] =
								{
									'N','E','O','M','S'
								};

								MoveSingle(46);

								SPrintf(Buffer,"%ld-%lc-%ld",LastBitsPerChar,Parities[LastParity],LastStopBits);
							}
							else
							{
								SPrintf(Buffer,"%ld-%s-%ld   ",LastBitsPerChar,ConfigParity[LastParity],LastStopBits);

								Buffer[9] = 0;

								MoveItem(47,1);
							}

							PrintStatusLine(RPort,Buffer);
						}
					}

						/* Wait another half a second. */

					if(KeepGoing)
					{
						ULONG Mask;

						TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
						TimeRequest -> tr_time . tv_secs	= 0;
						TimeRequest -> tr_time . tv_micro	= MILLION / 2;

						SendIO(TimeRequest);

						FOREVER
						{
							Mask = Wait(SIGBREAKF_CTRL_D | (1 << TimePort -> mp_SigBit));

							if(Mask & SIGBREAKF_CTRL_D)
							{
								if(Config . SystemBeep)
								{
									DisplayBeep(Screen);

									SetSignal(0,SIGBREAKF_CTRL_D);
								}
								else
								{
									if(!Beeping)
									{
										if(VisualBeep())
										{
											Beeping = TRUE;

											BeepCount = 1;
										}
									}
								}
							}

							if(Mask & (1 << TimePort -> mp_SigBit))
							{
								WaitIO(TimeRequest);

								break;
							}
						}
					}

						/* Make the colours blink. */

					if(Screen == IntuitionBase -> FirstScreen)
					{
							/* No main screen window active? */

						if(StatusWindow)
						{
							if(!(Window -> Flags & WFLG_WINDOWACTIVE) && !(StatusWindow -> Flags & WFLG_WINDOWACTIVE))
								StandardColours = TRUE;
						}
						else
						{
							if(!(Window -> Flags & WFLG_WINDOWACTIVE))
								StandardColours = TRUE;
						}

							/* Menu button pressed or window disabled? */

						if(Window -> Flags & (WFLG_MENUSTATE|WFLG_INREQUEST))
							StandardColours = TRUE;

						Background = FALSE;
					}
					else
					{
						if(!Background)
							StandardColours = TRUE;

						Background = TRUE;
					}

					if(StandardColours)
					{
						if(!SetColours)
						{
							LoadRGB4(VPort,Config . Colours,16);

							SetColours = TRUE;
						}

						StandardColours = FlashIt = FALSE;
					}
					else
					{
							/* Are we to flash the display? */

						if(!(Config . DisableBlinking & ~TERMINAL_FASTER))
						{
							if(Screen == IntuitionBase -> FirstScreen)
							{
								if(FlashIt)
								{
									LoadRGB4(VPort,BlinkColours,16);

									SetColours = FALSE;
								}
								else
								{
									LoadRGB4(VPort,Config . Colours,16);

									SetColours = TRUE;
								}
							}

							FlashIt ^= TRUE;
						}
					}
				}

				CloseDevice(TimeRequest);
			}

			DeleteIORequest(TimeRequest);
		}

		DeleteMsgPort(TimePort);
	}

		/* Signal the father process that we're done
		 * and quietly remove ourselves.
		 */

	Forbid();

	Signal(ThisProcess,SIGBREAKF_CTRL_C);

	StatusProcess = NULL;
}

	/* CloseWindowSafely(struct Window *Window):
	 *
	 *	Close a window freeing all messages pending at
	 *	its user port (taken from example source code
	 *	published once upon a time in Amiga Mail).
	 */

VOID
CloseWindowSafely(struct Window *Window)
{
	struct IntuiMessage	*IntuiMessage;
	struct Node		*Successor;

	Forbid();

	IntuiMessage = (struct IntuiMessage *)Window -> UserPort -> mp_MsgList . lh_Head;

	while(Successor = IntuiMessage -> ExecMessage . mn_Node . ln_Succ)
	{
		if(IntuiMessage -> IDCMPWindow == Window)
		{
			Remove(IntuiMessage);

			ReplyMsg((struct Message *)IntuiMessage);
		}

		IntuiMessage = (struct IntuiMessage *)Successor;
	}

	Window -> UserPort = NULL;

	ModifyIDCMP(Window,NULL);

	Permit();

	CloseWindow(Window);
}

	/* WaitTime(LONG Secs,LONG Micros):
	 *
	 *	Wait a given period of time.
	 */

VOID
WaitTime(LONG Secs,LONG Micros)
{
	TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
	TimeRequest -> tr_time . tv_secs	= Secs;
	TimeRequest -> tr_time . tv_micro	= Micros;

	DoIO(TimeRequest);
}

	/* GetEnvDOS(UBYTE *Name,UBYTE *Buffer):
	 *
	 *	Get the contents of a vanilla AmigaDOS environment
	 *	variable.
	 */

UBYTE *
GetEnvDOS(UBYTE *Name,UBYTE *Buffer)
{
	LONG	Size;
	BPTR	File,SomeLock;

	Buffer[0] = 0;

		/* Is ENV: present? */

	if(SomeLock = Lock("Env:",ACCESS_READ))
	{
		UBYTE SomeBuffer[80];

		UnLock(SomeLock);

		strcpy(SomeBuffer,"Env:");
		strcat(SomeBuffer,Name);

			/* Open the file. */

		if(File = Open(SomeBuffer,MODE_OLDFILE))
		{
				/* Read the contents. */

			Size = Read(File,Buffer,256);

			Close(File);

			if(Size > 0)
			{
				Buffer[Size] = 0;

				return(Buffer);
			}
		}
	}

	return(NULL);
}

	/* SetEnvDOS(UBYTE *Name,UBYTE *Value):
	 *
	 *	Set the contents of a vanilla AmigaDOS environment
	 *	variable.
	 */

BYTE
SetEnvDOS(UBYTE *Name,UBYTE *Value)
{
	UBYTE	Buffer[80],*Destination;
	LONG	Length = 0;
	BPTR	File,FileLock;
	BYTE	Success = FALSE;
	WORD	i;

	for(i = 0 ; i < 2 ; i++)
	{
		if(i)
			Destination = "EnvArc:";
		else
			Destination = "Env:";

			/* Is ENV:/ENVARC: present? */

		if(FileLock = Lock(Destination,ACCESS_READ))
		{
			UnLock(FileLock);

			strcpy(Buffer,Destination);
			strcat(Buffer,Name);

				/* There already is a variable of that
				 * name in the environment storage
				 * directory.
				 */

			if(FileLock = Lock(Buffer,ACCESS_WRITE))
			{
				UnLock(FileLock);

					/* Delete the variable. */

				if(!DeleteFile(Buffer))
				{
					Success = FALSE;
					continue;
				}
			}

				/* Set the new variable. */

			if(Length = strlen(Value))
			{
				if(File = Open(Buffer,MODE_NEWFILE))
				{
					if(Write(File,Value,Length) != Length)
					{
						Close(File);
						DeleteFile(Buffer);

						Success = FALSE;
					}
					else
					{
						Close(File);
						SetProtection(Buffer,FIBF_EXECUTE);

						Success = TRUE;
					}
				}
				else
					Success = FALSE;
			}
			else
				Success = TRUE;
		}
		else
			Success = FALSE;
	}

	return(Success);
}

	/* BumpWindow(struct Window *SomeWindow):
	 *
	 *	Bring a window to the front (and shift the screen
	 *	back to its initial position).
	 */

VOID
BumpWindow(struct Window *SomeWindow)
{
	if(SomeWindow -> WScreen -> LeftEdge > 0)
	{
		if(SomeWindow -> WScreen -> TopEdge > 0)
			MoveScreen(SomeWindow -> WScreen,-SomeWindow -> WScreen -> LeftEdge,-SomeWindow -> WScreen -> TopEdge);
		else
			MoveScreen(SomeWindow -> WScreen,-SomeWindow -> WScreen -> LeftEdge,0);
	}
	else
	{
		if(SomeWindow -> WScreen -> TopEdge > 0)
			MoveScreen(SomeWindow -> WScreen,0,-SomeWindow -> WScreen -> TopEdge);
	}

	ScreenToFront(SomeWindow -> WScreen);

	ActivateWindow(SomeWindow);
}

	/* BumpDefault():
	 *
	 *	Bring the current default screen to the front.
	 */

VOID
BumpDefault()
{
	struct Screen *DefaultScreen;

	if(DefaultScreen = (struct Screen *)LockPubScreen(NULL))
	{
		MoveScreen(DefaultScreen,-DefaultScreen -> LeftEdge,-DefaultScreen -> TopEdge);

		ScreenToFront(DefaultScreen);

		UnlockPubScreen(NULL,DefaultScreen);
	}
}

	/* WriteIFFData(UBYTE *Name,APTR Data,LONG Size,ULONG Type):
	 *
	 *	Write data to an IFF file (via iffparse.library).
	 */

BYTE
WriteIFFData(UBYTE *Name,APTR Data,LONG Size,ULONG Type)
{
	struct IFFHandle	*Handle;
	BYTE			 Success = FALSE;

		/* Allocate a handle. */

	if(Handle = AllocIFF())
	{
			/* Open an output stream. */

		if(Handle -> iff_Stream = Open(Name,MODE_NEWFILE))
		{
				/* Tell iffparse that this is
				 * a DOS handle.
				 */

			InitIFFasDOS(Handle);

				/* Open the handle for writing. */

			if(!OpenIFF(Handle,IFFF_WRITE))
			{
					/* Push outmost chunk onto stack. */

				if(!PushChunk(Handle,'TERM','FORM',IFFSIZE_UNKNOWN))
				{
						/* Add a version identifier. */

					if(!PushChunk(Handle,0,'VERS',IFFSIZE_UNKNOWN))
					{
						struct TermInfo TermInfo;

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

							/* Write the version data. */

						if(WriteChunkBytes(Handle,&TermInfo,sizeof(struct TermInfo)) == sizeof(struct TermInfo))
						{
								/* Pop the version chunk, i.e. write it to the file. */

							if(PopChunk(Handle))
								Success = FALSE;
							else
							{
									/* Push the real data chunk on the stack. */

								if(!PushChunk(Handle,0,Type,IFFSIZE_UNKNOWN))
								{
										/* Write the data. */

									if(WriteChunkBytes(Handle,Data,Size) == Size)
										Success = TRUE;

											/* Pop the data chunk. */

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

						/* Seems that we're done, now try to pop the FORM chunk
						 * and return.
						 */

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

					/* Close the handle (flush any pending data). */

				CloseIFF(Handle);
			}

				/* Close the DOS handle itself. */

			Close(Handle -> iff_Stream);
		}

			/* And free the IFF handle. */

		FreeIFF(Handle);
	}

	if(Success)
		SetProtection(Name,FIBF_EXECUTE);

	return(Success);
}

	/* ReadIFFData(UBYTE *Name,APTR Data,LONG Size,ULONG Type):
	 *
	 *	Read data from a `TERM' FORM chunk contained in an IFF file.
	 */

BYTE
ReadIFFData(UBYTE *Name,APTR Data,LONG Size,ULONG Type)
{
	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))
			{
				/* Collect version number ID if
				 * available.
				 */

				if(!PropChunks(Handle,&VersionProps[0],1))
				{
					/* The following line tells iffparse to stop at the
					 * very beginning of a `Type' chunk contained in a
					 * `TERM' FORM chunk.
					 */

					if(!StopChunk(Handle,'TERM',Type))
					{
						/* Parse the file... */

						if(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
							/* Did we get a version ID? */

							if(Prop = FindProp(Handle,'TERM','VERS'))
							{
								TermInfo = (struct TermInfo *)Prop -> sp_Data;

								/* Is it the file format we are able
								 * to read?
								 */

								if((TermInfo -> Version > TermVersion) || (TermInfo -> Version == TermVersion && TermInfo -> Revision > TermRevision) || (TermInfo -> Version == 1 && TermInfo -> Revision < 6))
									Success = FALSE;
								else
								{
									struct ContextNode *Chunk = CurrentChunk(Handle);

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

									/* The file read pointer is positioned
									 * just in front of the first data
									 * to be read, so let's don't disappoint
									 * iffparse and read it.
									 */

									if(ReadChunkBytes(Handle,Data,Size) == Size)
										Success = TRUE;
								}
							}
						}
					}
				}

				CloseIFF(Handle);
			}

			Close(Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	return(Success);
}

	/* PushWindow(struct Window *Window):
	 *
	 *	Push/PopWindow implement a single lifo window stack
	 *	which always updates the window to activate when
	 *	LSHIFT+RSHIFT+RETURN is pressed. This routine will
	 *	push a window on the stack.
	 */

VOID
PushWindow(struct Window *Window)
{
	if(WindowStackPtr < 5)
	{
		WindowStack[WindowStackPtr++] = Window;

		TopWindow = Window;
	}
}

	/* PopWindow():
	 *
	 *	Remove topmost window from window stack.
	 */

VOID
PopWindow()
{
	if(WindowStackPtr > 0)
	{
		WindowStackPtr--;

		if(WindowStackPtr)
			TopWindow = WindowStack[WindowStackPtr - 1];
		else
			TopWindow = Window;
	}
}
