/*
**	termAux.c
**
**	Miscellaneous support routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Block window nest count. */

STATIC WORD BlockNestCount;

	/* This variable stores the current state of the
	 * dialing menu items.
	 */

STATIC BYTE DialItemsAvailable = TRUE;

	/* GetPubScreenName(struct Screen *Screen,STRPTR Name):
	 *
	 *	Get the name of a public screen.
	 */

BYTE __regargs
GetPubScreenName(struct Screen *Screen,STRPTR Name)
{
	struct List		*PubScreenList;
	struct PubScreenNode	*ScreenNode;

	PubScreenList = LockPubScreenList();

	for(ScreenNode = (struct PubScreenNode *)PubScreenList -> lh_Head ; ScreenNode -> psn_Node . ln_Succ ; ScreenNode = (struct PubScreenNode *)ScreenNode -> psn_Node . ln_Succ)
	{
		if(ScreenNode -> psn_Screen == Screen)
		{
			strcpy(Name,ScreenNode -> psn_Node . ln_Name);

			UnlockPubScreenList();

			return(TRUE);
		}
	}

	UnlockPubScreenList();

	return(FALSE);
}

	/* InitSinglePort(struct MsgPort *Port):
	 *
	 *	Initialize a plain MsgPort (as created on the stack) for
	 *	usage. Don't try this at home, kids!
	 */

VOID __regargs
InitSinglePort(struct MsgPort *Port)
{
	memset(Port,0,sizeof(struct MsgPort));

	Port -> mp_Flags	= PA_SIGNAL;
	Port -> mp_SigBit	= SIGB_SINGLE;
	Port -> mp_SigTask	= SysBase -> ThisTask;

	NewList(&Port -> mp_MsgList);
}

	/* GoodStream(BPTR Stream):
	 *
	 *	Check to see whether the current input file
	 *	is an interactive stream.
	 */

BYTE __regargs
GoodStream(BPTR Stream)
{
	if(!Stream)
	{
		struct Process *ThisProcess = (struct Process *)SysBase -> ThisTask;

		Stream = ThisProcess -> pr_CIS;
	}

	if(Stream)
	{
		struct FileHandle *Handle = (struct FileHandle *)BADDR(Stream);

		if(Handle -> fh_Type)
		{
			if(IsInteractive(Stream))
				return(TRUE);
		}
	}

	return(FALSE);
}

	/* GetProgramIcon():
	 *
	 *	Try to find the icon of the program.
	 */

struct DiskObject *
GetProgramIcon()
{
	struct DiskObject *Icon = NULL;

		/* Run from Workbench? */

	if(WBenchMsg)
	{
			/* Sanity check. */

		if(WBenchMsg -> sm_ArgList)
		{
				/* Yet another sanity check. */

			if(WBenchMsg -> sm_ArgList -> wa_Name)
			{
					/* Try to find the icon in the current directory. */

				if(Icon = GetDiskObjectNew(WBenchMsg -> sm_ArgList -> wa_Name))
				{
					if(Icon -> do_Type != WBTOOL)
					{
						FreeDiskObject(Icon);

						Icon = NULL;
					}
				}

				if(!Icon)
				{
					BPTR NewLock;

						/* Move to the directory the
						 * program was run from.
						 */

					if(NewLock = Lock("PROGDIR:",ACCESS_READ))
					{
						BPTR OldLock;

						OldLock = CurrentDir(NewLock);

							/* Try to fetch the icon, use the
							 * default name if necessary.
							 */

						if(Icon = GetDiskObjectNew(WBenchMsg -> sm_ArgList -> wa_Name))
						{
							if(Icon -> do_Type != WBTOOL)
							{
								FreeDiskObject(Icon);

								Icon = NULL;
							}
						}

						if(!Icon)
						{
							if(Icon = GetDiskObjectNew("term"))
							{
								if(Icon -> do_Type != WBTOOL)
								{
									FreeDiskObject(Icon);

									Icon = NULL;
								}
							}
						}

						CurrentDir(OldLock);

						UnLock(NewLock);
					}
				}
			}
		}
	}

		/* Still no success. */

	if(!Icon)
	{
			/* Use the default names. */

		if(Icon = GetDiskObjectNew("term"))
		{
			if(Icon -> do_Type != WBTOOL)
			{
				FreeDiskObject(Icon);

				Icon = NULL;
			}
		}

		if(!Icon)
		{
			if(Icon = GetDiskObjectNew("PROGDIR:term"))
			{
				if(Icon -> do_Type != WBTOOL)
				{
					FreeDiskObject(Icon);

					Icon = NULL;
				}
			}
		}
	}

	return(Icon);
}

	/* GetPenIndex(LONG Pen):
	 *
	 *	Get the table index corresponding to an on-screen
	 *	text rendering pen.
	 */

LONG __regargs
GetPenIndex(LONG Pen)
{
	LONG i;

	for(i = 0 ; i < 16 ; i++)
	{
		if(MappedPens[0][i] == Pen)
			return(i);
	}
}

	/* GetScreenWidth(struct Window *Window):
	 *
	 *	Query the current screen width.
	 */

LONG __regargs
GetScreenWidth(struct Window *Window)
{
	if(Window)
	{
		if(Window -> Width > ScreenWidth || Window -> LeftEdge < -Window -> WScreen -> LeftEdge || Window -> LeftEdge + Window -> WScreen -> LeftEdge + Window -> Width > ScreenWidth)
			return(ScreenWidth);
		else
			return(Window -> Width - (Window -> BorderLeft + Window -> BorderRight));
	}
	else
		return(ScreenWidth);
}

	/* GetScreenHeight(struct Window *Window):
	 *
	 *	Query the current screen height.
	 */

LONG __regargs
GetScreenHeight(struct Window *Window)
{
	if(Window)
	{
		if(Window -> Height > ScreenHeight || Window -> TopEdge < -Window -> WScreen -> TopEdge || Window -> TopEdge + Window -> WScreen -> TopEdge + Window -> Height > ScreenHeight)
			return(ScreenHeight);
		else
			return(Window -> Height - (Window -> BorderTop + Window -> BorderBottom));
	}
	else
		return(ScreenHeight);
}

	/* GetLeft(struct Screen *Screen):
	 *
	 *	Get the screen left edge.
	 */

STATIC LONG __regargs
GetLeft(struct Screen *Screen)
{
	if(Screen -> LeftEdge >= 0)
		return(0);
	else
		return(-Screen -> LeftEdge);
}

	/* GetScreenLeft(struct Window *Window):
	 *
	 *	Query the current screen left edge.
	 */

LONG __regargs
GetScreenLeft(struct Window *Window)
{
	if(Window)
	{
		if(Window -> Width > ScreenWidth || Window -> LeftEdge < -Window -> WScreen -> LeftEdge || Window -> LeftEdge + Window -> WScreen -> LeftEdge + Window -> Width > ScreenWidth)
			return(GetLeft(Window -> WScreen));
		else
			return(Window -> LeftEdge + Window -> BorderLeft);
	}
	else
		return(GetLeft(DefaultPubScreen));
}

	/* GetTop(struct Screen *Screen):
	 *
	 *	Get the screen top edge.
	 */

STATIC LONG __regargs
GetTop(struct Screen *Screen)
{
	if(Screen -> TopEdge >= 0)
		return(0);
	else
		return(-Screen -> TopEdge);
}

	/* GetScreenTop(struct Window *Window):
	 *
	 *	Query the current screen top edge.
	 */

LONG __regargs
GetScreenTop(struct Window *Window)
{
	if(Window)
	{
		if(Window -> Height > ScreenHeight || Window -> TopEdge < -Window -> WScreen -> TopEdge || Window -> TopEdge + Window -> WScreen -> TopEdge + Window -> Height > ScreenHeight)
			return(GetTop(Window -> WScreen));
		else
			return(Window -> TopEdge + Window -> BorderTop);
	}
	else
		return(GetTop(DefaultPubScreen));
}

	/* OldGetAPen(struct RastPort *RPort):
	 *
	 *	Query the current primary rendering colour (old style).
	 */

ULONG __regargs
OldGetAPen(struct RastPort *RPort)
{
	return((ULONG)RPort -> FgPen);
}

	/* OldGetBPen(struct RastPort *RPort):
	 *
	 *	Query the current seconary rendering colour (old style).
	 */

ULONG __regargs
OldGetBPen(struct RastPort *RPort)
{
	return((ULONG)RPort -> BgPen);
}

	/* OldGetDrMd(struct RastPort *RPort):
	 *
	 *	Query the current drawing mode (old style).
	 */

ULONG __regargs
OldGetDrMd(struct RastPort *RPort)
{
	return((ULONG)RPort -> DrawMode);
}

	/* OldSetWrMsk(struct RastPort *RPort,ULONG Mask):
	 *
	 *	Set the rendering plane mask (old style).
	 */

ULONG __regargs
OldSetWrMsk(struct RastPort *RPort,ULONG Mask)
{
	RPort -> Mask = Mask;

	return((ULONG)1);
}

	/* NewGetAPen(struct RastPort *RPort):
	 *
	 *	Query the current primary rendering colour (new style).
	 */

ULONG __regargs
NewGetAPen(struct RastPort *RPort)
{
	return(GetAPen(RPort));
}

	/* NewGetBPen(struct RastPort *RPort):
	 *
	 *	Query the current seconary rendering colour (new style).
	 */

ULONG __regargs
NewGetBPen(struct RastPort *RPort)
{
	return(GetBPen(RPort));
}

	/* NewGetDrMd(struct RastPort *RPort):
	 *
	 *	Query the current drawing mode (new style).
	 */

ULONG __regargs
NewGetDrMd(struct RastPort *RPort)
{
	return(GetDrMd(RPort));
}

	/* NewSetWrMsk(struct RastPort *RPort,ULONG Mask):
	 *
	 *	Set the rendering plane mask (new style).
	 */

ULONG __regargs
NewSetWrMsk(struct RastPort *RPort,ULONG Mask)
{
	return(SetWriteMask(RPort,Mask));
}

	/* SetWait(struct Window *Window):
	 *
	 *	Set the busy wait mouse pointer.
	 */

VOID
SetWait(struct Window *Window)
{
	if(Kick30)
	{
		SetWindowPointer(Window,
			WA_BusyPointer,		TRUE,
			WA_PointerDelay,	TRUE,
		TAG_DONE);
	}
	else
		SetPointer(Window,Stopwatch,16,16,-6,0);
}

	/* ClrWait(struct Window *Window):
	 *
	 *	Remove the busy wait mouse pointer.
	 */

VOID
ClrWait(struct Window *Window)
{
	if(Kick30)
		SetWindowPointer(Window,TAG_DONE);
	else
		ClearPointer(Window);
}

	/* GetModeName(ULONG Mode):
	 *
	 *	Get the name of a display mode.
	 */

STRPTR
GetModeName(ULONG Mode)
{
	STATIC UBYTE	Buffer[DISPLAYNAMELEN + 1];
	struct NameInfo	NameInfo;

	if(GetDisplayInfoData(NULL,(APTR)&NameInfo,sizeof(struct NameInfo),DTAG_NAME,Mode))
		strcpy(Buffer,NameInfo . Name);
	else
	{
		struct DimensionInfo DimensionInfo;

		if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_NAME,Mode))
		{
			STRPTR MonitorName;

			switch(Mode & MONITOR_ID_MASK)
			{
				case NTSC_MONITOR_ID:

					MonitorName = "NTSC: ";
					break;

				case PAL_MONITOR_ID:

					MonitorName = "PAL: ";
					break;

				case VGA_MONITOR_ID:

					MonitorName = "VGA: ";
					break;

				case A2024_MONITOR_ID:

					MonitorName = "A2024: ";
					break;

				default:

					MonitorName = "";
					break;
			}

			SPrintf(Buffer,"%s%ld × %ld",MonitorName,DimensionInfo . TxtOScan . MaxX - DimensionInfo . TxtOScan . MinX + 1,DimensionInfo . TxtOScan . MaxY - DimensionInfo . TxtOScan . MinY + 1);
		}
		else
			strcpy(Buffer,LocaleString(MSG_SCREENPANEL_UNKNOWN_TXT));
	}

	return(Buffer);
}

	/* ModeOkay(ULONG ID):
	 *
	 *	Checks whether a display mode ID will do for deep
	 *	screen bitmaps.
	 */

BYTE __regargs
ModeOkay(ULONG ID)
{
	struct DimensionInfo DimensionInfo;

	if(GetDisplayInfoData(NULL,(APTR)&DimensionInfo,sizeof(struct DimensionInfo),DTAG_DIMS,ID))
	{
		if(DimensionInfo . MaxDepth >= 4)
			return(TRUE);
	}

	return(FALSE);
}

	/* SetRedialMenu():
	 *
	 *	Make the `redial' menu item available or make it
	 *	unavailable.
	 */

VOID
SetRedialMenu()
{
	BYTE Mode;

	if(DialList)
	{
		if(DialList -> lh_Head -> ln_Succ)
			Mode = TRUE;
		else
			Mode = FALSE;
	}
	else
		Mode = FALSE;

	if(Mode && DialItemsAvailable)
		OnItem(MEN_REDIAL);
	else
		OffItem(MEN_REDIAL);
}

	/* SetDialMenu(BYTE Mode):
	 *
	 *	Block or enable the dialing menu.
	 */

VOID
SetDialMenu(BYTE Mode)
{
	if(Window && Menu)
	{
		if(Mode)
		{
			if(DialList)
			{
				if(DialList -> lh_Head -> ln_Succ)
					OnItem(MEN_REDIAL);
				else
					OffItem(MEN_REDIAL);
			}
			else
				OffItem(MEN_REDIAL);

			OnItem(MEN_DIAL_NUMBER);
		}
		else
		{
			OffItem(MEN_REDIAL);
			OffItem(MEN_DIAL_NUMBER);
		}

		DialItemsAvailable = Mode;
	}
}

	/* SetTransferMenu(BYTE Mode):
	 *
	 *	Block or enable the transfer menu.
	 */

VOID
SetTransferMenu(BYTE Mode)
{
	if(Window && Menu)
	{
		if(Mode)
		{
			ULONG i;

			for(i = MEN_UPLOAD_ASCII ; i <= MEN_DOWNLOAD_BINARY ; i++)
				OnItem(i);

			OnItem(MEN_TRANSFER);
		}
		else
		{
			ULONG i;

			for(i = MEN_UPLOAD_ASCII ; i <= MEN_DOWNLOAD_BINARY ; i++)
				OffItem(i);

			OffItem(MEN_TRANSFER);
		}
	}
}

	/* SetRasterMenu(BYTE Mode):
	 *
	 *	Block or enable the menu entries associated with
	 *	functions to access the screen raster.
	 */

VOID
SetRasterMenu(BYTE Mode)
{
	if(Window && Menu)
	{
		if(Mode)
		{
			OnItem(MEN_SAVE_AS_TEXT);
			OnItem(MEN_PRINT_SCREEN);

			OnItem(MEN_COPY);
			OnItem(MEN_CLEAR);
		}
		else
		{
			OffItem(MEN_SAVE_AS_TEXT);
			OffItem(MEN_PRINT_SCREEN);

			OffItem(MEN_COPY);
			OffItem(MEN_CLEAR);
		}
	}
}

	/* PickFont(struct Window *Window,STRPTR Name,WORD *Height,BYTE MonoSpaced):
	 *
	 *	Pick a font using asl.library
	 */

BYTE
PickFont(struct Window *Window,STRPTR Name,WORD *Height,BYTE MonoSpaced)
{
	struct FontRequester	*Requester;
	BYTE			 Result = FALSE;

	if(Requester = (struct FontRequester *)AllocAslRequestTags(ASL_FontRequest,
		ASL_Window,					Window,
		MonoSpaced ? ASL_FuncFlags : TAG_IGNORE,	FONF_FIXEDWIDTH,
	TAG_DONE))
	{
		if(AslRequestTags(Requester,
			ASL_FontName,	Name,
			ASL_FontHeight,	*Height,
		TAG_DONE))
		{
			strcpy(Name,Requester -> fo_Attr . ta_Name);

			*Height = Requester -> fo_Attr . ta_YSize;

			Result = TRUE;
		}

		FreeAslRequest(Requester);
	}

	return(Result);
}

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

STRPTR __regargs
ExtractString(STRPTR String,STRPTR Destination,BYTE ReturnEnd)
{
	STRPTR 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);
}

	/* GetListSize(struct List *List):
	 *
	 *	Determine the number of entries in a list.
	 */

LONG
GetListSize(struct List *List)
{
	struct Node	*Node	= List -> lh_Head;
	LONG		 i	= 0;

	while(Node -> ln_Succ)
	{
		i++;

		Node = Node -> ln_Succ;
	}

	return(i);
}

	/* 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);
}

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

VOID __stdargs
LogAction(STRPTR String,...)
{
	if(Config -> CaptureConfig -> LogActions && Config -> CaptureConfig -> LogFileName[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 -> CaptureConfig -> LogFileName))
		{
				/* It does, let's append the data. */

			if(File = Open(Config -> CaptureConfig -> LogFileName,MODE_READWRITE))
			{
				if(Seek(File,0,OFFSET_END) == -1)
				{
					Close(File);

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

			if(File = Open(Config -> CaptureConfig -> LogFileName,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(STRPTR Prompt,STRPTR Buffer):
	 *
	 *	Get a string from the user, very much the same as xpr_gets,
	 *	but also including the `Load File' gadget.
	 */

BYTE
GetString(BYTE LoadGadget,BYTE Password,WORD MaxChars,STRPTR Prompt,STRPTR Buffer)
{
	struct Gadget		*GadgetList = NULL;
	struct Gadget		*GadgetArray[4];
	struct Window		*PanelWindow;
	UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
	struct FileRequester	*FileRequest;
	BYTE			 Success = FALSE;

	if(CreateAllGetsGadgets(LoadGadget,Password,MaxChars,Buffer,Prompt,GadgetArray,&GadgetList,VisualInfo,Window -> WScreen))
	{
		if(PanelWindow = OpenWindowTags(NULL,
			WA_Left,	GetScreenLeft(Window) + (GetScreenWidth(Window) - SZ_GetWindowWidth()) / 2,
			WA_Top,		GetScreenTop(Window) + (GetScreenHeight(Window) - SZ_GetWindowHeight())	/ 2,
			WA_Width,	SZ_GetWindowWidth(),
			WA_Height,	SZ_GetWindowHeight(),

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_DepthGadget,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_CustomScreen,Window -> WScreen,
			WA_IDCMP,	IDCMP_GADGETDOWN | IDCMP_ACTIVEWINDOW | IDCMP_CLOSEWINDOW | IDCMP_GADGETUP | IDCMP_VANILLAKEY | IDCMP_RAWKEY | BUTTONIDCMP | STRINGIDCMP,
			WA_Title,	LocaleString(MSG_GLOBAL_ENTER_TEXT_TXT),
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 IClass,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;

			ClrSignal(SIG_BREAK);

			while(!Terminated)
			{
				if(Wait(PORTMASK(PanelWindow -> UserPort) | SIG_BREAK) & SIG_BREAK)
					break;

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

					GT_ReplyIMsg(Massage);

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

					if(IClass == IDCMP_RAWKEY && Code == HELP_CODE)
						DisplayBeep(PanelWindow -> WScreen);

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

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

					if(IClass == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(IClass == IDCMP_GADGETUP)
					{
						switch(Gadget -> GadgetID)
						{
							case 0:

								strcpy(Buffer,SZ_GetString(GadgetArray[0]));

								Success = TRUE;

								Terminated = TRUE;

								break;

							case 1:

								BlockWindow(PanelWindow);

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

									FreeAslRequest(FileRequest);
								}

								ReleaseWindow(PanelWindow);

								break;

							case 2:

								strcpy(Buffer,SZ_GetString(GadgetArray[0]));

								Success = TRUE;

								Terminated = TRUE;

								break;

							case 3:

								Terminated = TRUE;
								break;
						}
					}
				}
			}

			ActiveGadget = NULL;

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

			PopWindow();

			CloseWindow(PanelWindow);
		}
	}

	FreeGadgets(GadgetList);

	return(Success);
}

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

VOID __regargs
WakeUp(struct Window *Window,WORD Sound)
{
	if(Window)
	{
		if(Config -> TerminalConfig -> AlertMode == ALERT_SCREEN || Config -> TerminalConfig -> AlertMode == ALERT_BEEP_SCREEN)
		{
			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);
			}

			WindowToFront(Window);

			ScreenToFront(Window -> WScreen);
		}
	}

	if(Sound != SOUND_BELL || Config -> TerminalConfig -> AlertMode == ALERT_BEEP || Config -> TerminalConfig -> AlertMode == ALERT_BEEP_SCREEN)
		SoundPlay(Sound);
}

	/* AmigaDOSBackgroundServer(VOID):
	 *
	 *	Background process to handle tool execution.
	 */

STATIC VOID __saveds
AmigaDOSBackgroundServer(VOID)
{
	struct Process	*ThisProcess = (struct Process *)SysBase -> ThisTask;
	struct Message	*Message;
	BPTR		 OldCOS,
			 NewCOS = NULL;

	WaitPort(&ThisProcess -> pr_MsgPort);

	Message = GetMsg(&ThisProcess -> pr_MsgPort);

		/* Create console output stream, will be closed automagically on exit. */

	if(!ThisProcess -> pr_COS && ThisProcess -> pr_ConsoleTask)
	{
		if(NewCOS = Open("*",MODE_NEWFILE))
		{
			OldCOS = ThisProcess -> pr_COS;

			ThisProcess -> pr_COS = NewCOS;
		}
	}

	SystemTags(Message -> mn_Node . ln_Name,TAG_DONE);

	Forbid();

	if(NewCOS)
	{
		ThisProcess -> pr_COS = OldCOS;

		Close(NewCOS);
	}

	ReplyMsg(Message);
}

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

VOID
SendAmigaDOSCommand(STRPTR Name)
{
	BPTR Stream;

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

	if(Stream = Open(WindowName,MODE_NEWFILE))
	{
		struct MsgPort *ReplyPort;

		if(ReplyPort = CreateMsgPort())
		{
			struct Message __aligned	 Message;
			struct Process			*NewProcess;

			memset(&Message,0,sizeof(struct Message));

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

			if(GoodStream(Stream))
			{
				struct FileHandle *Handle = (struct FileHandle *)BADDR(Stream);

				NewProcess = CreateNewProcTags(
					NP_Entry,	AmigaDOSBackgroundServer,
					NP_Input,	Stream,
					NP_Output,	NULL,
					NP_ConsoleTask,	Handle -> fh_Type,
					NP_StackSize,	8000,
					NP_Cli,		TRUE,
					NP_Name,	"term AmigaDOS background process",
				TAG_DONE);
			}
			else
			{
				NewProcess = CreateNewProcTags(
					NP_Entry,	AmigaDOSBackgroundServer,
					NP_Output,	Stream,
					NP_ConsoleTask,	NULL,
					NP_StackSize,	8000,
					NP_Cli,		TRUE,
					NP_Name,	"term AmigaDOS background process",
				TAG_DONE);
			}

			if(NewProcess)
			{
				ULONG SignalSet;

				PutMsg(&NewProcess -> pr_MsgPort,&Message);

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

				FOREVER
				{
					SignalSet = Wait(SIG_REXX | PORTMASK(ReplyPort));

						/* Yet another rexx message. */

					if(SignalSet & SIG_REXX)
						HandleRexx();

						/* The background server has
						 * finished.
						 */

					if(SignalSet & PORTMASK(ReplyPort))
					{
						GetMsg(ReplyPort);

						break;
					}
				}
			}
			else
				Close(Stream);

			DeleteMsgPort(ReplyPort);
		}
		else
			Close(Stream);
	}

	BumpWindow(Window);
}

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

#ifdef USE_AREXX

STATIC VOID __saveds
RexxBackgroundServer(VOID)
{
	struct MsgPort 	*RexxPort;
	struct Message	*Message;
	BPTR		 OldCOS,
			 NewCOS = NULL;

		/* Look who we are. */

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

		/* Wait for startup message. */

	WaitPort(&RexxBackgroundProcess -> pr_MsgPort);

		/* Pick the message up. */

	Message = GetMsg(&RexxBackgroundProcess -> pr_MsgPort);

		/* Create console output stream, will be closed automagically on exit. */

	if(!RexxBackgroundProcess -> pr_COS && RexxBackgroundProcess -> pr_ConsoleTask)
	{
		if(NewCOS = Open("*",MODE_NEWFILE))
		{
			OldCOS = RexxBackgroundProcess -> pr_COS;

			RexxBackgroundProcess -> pr_COS = NewCOS;
		}
	}

	if(RexxPort = FindPort(RXSDIR))
	{
		struct MsgPort __aligned	 ReplyPort;
		struct RexxMsg			*RexxMsg;

		InitSinglePort(&ReplyPort);

		if(RexxMsg = CreateRexxMsg(&ReplyPort,"term",RexxPortName))
		{
			if(RexxMsg -> rm_Args[0] = CreateArgstring(Message -> mn_Node . ln_Name,strlen(Message -> mn_Node . ln_Name)))
			{
				RexxMsg -> rm_Action = RXCOMM;

				Forbid();

				ClrSignal(SIGF_SINGLE);

				PutMsg(RexxPort,(struct Message *)RexxMsg);

				WaitPort(&ReplyPort);

				GetMsg(&ReplyPort);

				Permit();

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

				if(RexxMsg -> rm_Result1)
					Printf(LocaleString(MSG_TERMAUX_COMMAND_HAS_TERMINATED_TXT),Message -> mn_Node . ln_Name,RexxMsg -> rm_Result1,RexxMsg -> rm_Result2);

				DeleteArgstring(RexxMsg -> rm_Args[0]);
			}

			DeleteRexxMsg(RexxMsg);
		}
	}

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

	Forbid();

	if(NewCOS)
	{
		RexxBackgroundProcess -> pr_COS = OldCOS;

		Close(NewCOS);
	}

	ReplyMsg(Message);

	RexxBackgroundProcess = NULL;
}

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

VOID
SendARexxCommand(STRPTR Name)
{
	struct MsgPort *ReplyPort;

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

	if(ReplyPort = CreateMsgPort())
	{
		struct Process			*NewProcess;
		struct Message __aligned	 Message;
		BPTR				 Stream;

			/* Set up the command name carrier message. */

		memset(&Message,0,sizeof(struct Message));

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

			/* Open the output file. */

		if(WindowName[0])
			Stream = Open(WindowName,MODE_NEWFILE);
		else
			Stream = NULL;

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

		if(Stream)
		{
			if(GoodStream(Stream))
			{
				struct FileHandle *Handle = (struct FileHandle *)BADDR(Stream);

				NewProcess = (struct Process *)CreateNewProcTags(
					NP_Entry,	RexxBackgroundServer,
					NP_Name,	"term ARexx background process",
					NP_Input,	Stream,
					NP_Output,	NULL,
					NP_Cli,		TRUE,
					NP_ConsoleTask,	Handle -> fh_Type,
					NP_StackSize,	16384,
				TAG_END);
			}
			else
			{
				NewProcess = (struct Process *)CreateNewProcTags(
					NP_Entry,	RexxBackgroundServer,
					NP_Name,	"term ARexx background process",
					NP_Output,	Stream,
					NP_Cli,		TRUE,
					NP_ConsoleTask,	NULL,
					NP_StackSize,	16384,
				TAG_END);
			}
		}
		else
		{
			NewProcess = (struct Process *)CreateNewProcTags(
				NP_Entry,	RexxBackgroundServer,
				NP_Name,	"term ARexx background process",
				NP_Cli,		TRUE,
				NP_ConsoleTask,	NULL,
				NP_StackSize,	16384,
			TAG_END);
		}

			/* Did we succeed in creating the process? */

		if(NewProcess)
		{
			ULONG SignalSet;

				/* Send the startup message. */

			PutMsg(&NewProcess -> pr_MsgPort,&Message);

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

			FOREVER
			{
				SignalSet = Wait(SIG_REXX | PORTMASK(ReplyPort));

					/* Yet another rexx message. */

				if(SignalSet & SIG_REXX)
					HandleRexx();

					/* The background server has
					 * finished.
					 */

				if(SignalSet & PORTMASK(ReplyPort))
				{
					GetMsg(ReplyPort);

					break;
				}
			}
		}
		else
		{
			if(Stream)
				Close(Stream);
		}

			/* Delete the reply port. */

		DeleteMsgPort(ReplyPort);
	}

	BumpWindow(Window);
}

#endif	/* USE_AREXX */

	/* BlockWindow(struct Window *SomeWindow):
	 *
	 *	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 *SomeWindow)
{
	if(SomeWindow)
	{
		struct Requester	*InvisibleRequest = NULL;
		WORD			 i;

		if(SomeWindow == Window)
			GhostCursor();

		Forbid();

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

		for(i = 0 ; i < 10 ; i++)
		{
			if(RequesterList[i] . RWindow == SomeWindow)
			{
				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(SomeWindow);

			/* Install the requester if possible. */

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

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

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

		if(SomeWindow == Window && (Window -> Flags & WFLG_WINDOWACTIVE))
			NormalCursor();

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

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

				break;
			}
		}

			/* Restore the mouse pointer. */

		ClrWait(SomeWindow);

			/* If disabled, restore the window. */

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

			EndRequest(InvisibleRequest,SomeWindow);

				/* 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);
		BlockWindow(InfoWindow);

		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);
		ReleaseWindow(InfoWindow);

		WeAreBlocking = FALSE;
	}
}

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

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

	STATIC	LONG	ReadIndex = 0,
			ReadLen = 0;

		LONG	BytesRead = 0,
			i;

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

				ReadIndex = 0;

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

					return(BytesRead);
				}
			}

			BytesRead++;

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

				return(BytesRead);
			}
		}

		return(BytesRead);
	}
	else
		ReadIndex = ReadLen = 0;
}

	/* GetBaudRate(STRPTR Buffer):
	 *
	 *	Calculate the baud rate contained in a connect string.
	 */

LONG
GetBaudRate(STRPTR Buffer)
{
	LONG Rate,i,j;

	for(i = j = 0 ; i < strlen(Buffer) ; i++)
	{
		if(Buffer[i] == ' ')
			continue;
		else
		{
			if(Buffer[i] >= '0' && Buffer[i] <= '9')
				SharedBuffer[j++] = Buffer[i];
			else
				break;
		}
	}

	SharedBuffer[j] = 0;

	if(StrToLong(SharedBuffer,&Rate) > 0)
	{
		if(Rate > 0)
			return(Rate);
	}

	return(0);
}

	/* LookForIt(struct MenuItem *Item,ULONG ID):
	 *
	 *	Auxilary routine for FindThisItem(), scans
	 *	menu item and sub item lists.
	 */

STATIC struct MenuItem * __inline
LookForIt(struct MenuItem *Item,ULONG ID)
{
	while(Item)
	{
		if((ULONG)GTMENUITEM_USERDATA(Item) == ID)
			return(Item);
		else
		{
			if(Item -> SubItem)
			{
				register struct MenuItem *TheItem;

				if(TheItem = LookForIt(Item -> SubItem,ID))
					return(TheItem);
			}

			Item = Item -> NextItem;
		}
	}

	return(NULL);
}

	/* FindThisItem(struct Menu *FirstMenu,ULONG MenuID):
	 *
	 *	Scan a menu for a menuitem associated with a
	 *	menu ID.
	 */

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

	while(FirstMenu)
	{
		if(Item = LookForIt(FirstMenu -> FirstItem,MenuID))
			return(Item);
		else
			FirstMenu = FirstMenu -> NextMenu;
	}

	return(NULL);
}

	/* GetItem(ULONG MenuID):
	 *
	 *	Get the checkmark state of a menu item.
	 */

BYTE __regargs
GetItem(ULONG MenuID)
{
	if(Menu)
	{
		struct MenuItem *Item;

		if(Item = FindThisItem(Menu,MenuID))
		{
			if(Item -> Flags & CHECKED)
				return(TRUE);
		}
	}

	return(FALSE);
}

	/* SetItem(ULONG MenuID,BYTE Mode):
	 *
	 *	Clear or set the checkmark or state of a menu item.
	 */

VOID
SetItem(ULONG MenuID,BYTE Mode)
{
		/* Is the global pull-down menu available? */

	if(Menu)
	{
		struct MenuItem *Item;

			/* Try to find the menu item and change
			 * the state of the checkmark.
			 */

		if(Item = FindThisItem(Menu,MenuID))
		{
				/* Remove the menu from the windows. */

			if(Window)
				ClearMenuStrip(Window);

			if(StatusWindow)
				ClearMenuStrip(StatusWindow);

			if(FastWindow)
				ClearMenuStrip(FastWindow);

			if(InfoWindow)
				ClearMenuStrip(InfoWindow);

			switch(Mode)
			{
				case SETITEM_SETCHECK:

					Item -> Flags |= CHECKED;
					break;

				case SETITEM_CLRCHECK:

					Item -> Flags &= ~CHECKED;
					break;

				case SETITEM_ON:

					Item -> Flags |= ITEMENABLED;
					break;

				case SETITEM_OFF:

					Item -> Flags &= ~ITEMENABLED;
					break;
			}


				/* Reattach the menu to the windows. */

			if(Window)
				ResetMenuStrip(Window,Menu);

			if(StatusWindow)
				ResetMenuStrip(StatusWindow,Menu);

			if(FastWindow)
				ResetMenuStrip(FastWindow,Menu);

			if(InfoWindow)
				ResetMenuStrip(InfoWindow,Menu);
		}
	}
}

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

LONG
GetFileSize(STRPTR Name)
{
	struct FileInfoBlock	*FileInfo;
	LONG			 FileSize = 0;

	if(FileInfo = (struct FileInfoBlock *)AllocDosObject(DOS_FIB,TAG_DONE))
	{
		BPTR FileLock;

		if(FileLock = Lock(Name,ACCESS_READ))
		{
			if(Examine(FileLock,FileInfo))
			{
				if(FileInfo -> fib_DirEntryType < 0)
					FileSize = FileInfo -> fib_Size;
			}

			UnLock(FileLock);
		}

		FreeDosObject(DOS_FIB,FileInfo);
	}

	return(FileSize);
}

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

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

	STATIC UBYTE		 DirBuffer[MAX_FILENAME_LENGTH],
				 PatternBuffer[256],
				 OtherTitle[60];

		/* 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,MAX_FILENAME_LENGTH))
				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(!Stricmp(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,
		Title ? ASL_Hail : TAG_IGNORE,	Title,
		ASL_FuncFlags,	Flags | FILF_NEWIDCMP,
		ASL_Pattern,	Pattern,
		ASL_OKText,	OKText,
		ASL_ExtFlags1,	ExtFlags,
		ASLFR_TextAttr,	&UserFont,
		(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((STRPTR)Buffer,(STRPTR)AslFileRequest -> rf_File,MAX_FILENAME_LENGTH);

					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(SaveFlag && AskWrite)
		{
			if(GetFileSize(Buffer))
			{
				if(!MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FILE_ALREADY_EXISTS_OVERWRITE_TXT),LocaleString(MSG_GLOBAL_REPLACE_CANCEL_TXT),FilePart(Buffer)))
				{
					FreeAslRequest(AslFileRequest);

					return(NULL);
				}
			}
		}

		if(DefaultPattern)
			strcpy(PatternBuffer,AslFileRequest -> rf_Pat);

		return(AslFileRequest);
	}
}

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

WORD __stdargs
MyEasyRequest(struct Window *Window,STRPTR Text,STRPTR 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		= (STRPTR)LocaleString(MSG_TERMAUX_TERM_REQUEST_TXT);
	Easy . es_TextFormat	= (STRPTR)Text;
	Easy . es_GadgetFormat	= (STRPTR)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;
	UBYTE				 Time;
	BYTE				 FoundOne = FALSE;
	LONG				 i,Last;

		/* Obtain current time and date. */

	DateStamp(&Date);

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

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

		/* Apparently, in the US of A 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;

		/* First step: search for date settings. */

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

		if(TimeDateNode -> Header . Month == ClockData . month && TimeDateNode -> Header . 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 -> Header . Month == -1 && (TimeDateNode -> Header . 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;

		/* Convert current time to packed format. */

	Time = DT_GET_TIME(ClockData . hour,ClockData . min);

		/* Start with a blank. */

	Last = -1;

		/* Look for fitting time. */

	for(i = 0 ; i < TimeDateNode -> Table[0] . Count ; i++)
	{
			/* The time we are looking for must be >= the
			 * current time.
			 */

		if(TimeDateNode -> Table[i] . Time > Time)
		{
			if(i == 0)
				break;
			else
			{
				Last = i - 1;
				break;
			}
		}
		else
			continue;
	}

		/* If none is found, take the last one in the list.
		 * Note that this requires the list to be sorted in
		 * ascending order.
		 */

	if(Last == -1)
		Last = TimeDateNode -> Table[0] . Count - 1;

		/* Fill in the remaining data. */

	PayPerUnit[DT_FIRST_UNIT]	= TimeDateNode -> Table[Last] . PayPerUnit[DT_FIRST_UNIT];
	PayPerUnit[DT_NEXT_UNIT]	= TimeDateNode -> Table[Last] . PayPerUnit[DT_NEXT_UNIT];
	SecPerUnit[DT_FIRST_UNIT]	= TimeDateNode -> Table[Last] . SecPerUnit[DT_FIRST_UNIT];
	SecPerUnit[DT_NEXT_UNIT]	= TimeDateNode -> Table[Last] . SecPerUnit[DT_NEXT_UNIT];
}

	/* 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(STRPTR Name,STRPTR Buffer):
	 *
	 *	Get the contents of a vanilla AmigaDOS environment
	 *	variable.
	 */

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

	Buffer[0] = 0;

		/* Is ENV: present? */

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

		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(STRPTR Name,STRPTR Value):
	 *
	 *	Set the contents of a vanilla AmigaDOS environment
	 *	variable.
	 */

BYTE
SetEnvDOS(STRPTR Name,STRPTR Value)
{
	UBYTE	Buffer[MAX_FILENAME_LENGTH],*Destination;
	LONG	Length;
	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);
}

	/* 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;
	}
}

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

BYTE
LoadMacros(STRPTR 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,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,ID_TERM,ID_KEYS))
					{
						/* Parse the file... */

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

							if(Prop = FindProp(Handle,ID_TERM,ID_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);
}

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

BYTE
WriteIFFData(STRPTR 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,ID_TERM,ID_FORM,IFFSIZE_UNKNOWN))
				{
						/* Add a version identifier. */

					if(!PushChunk(Handle,0,ID_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(STRPTR Name,APTR Data,LONG Size,ULONG Type):
	 *
	 *	Read data from a `TERM' FORM chunk contained in an IFF file.
	 */

BYTE
ReadIFFData(STRPTR 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,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,ID_TERM,Type))
					{
						/* Parse the file... */

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

							if(Prop = FindProp(Handle,ID_TERM,ID_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);
}
