/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1991 by Olaf 'Olsen' Barthel & MXM
 *
 *	Name .....: TermXPR.c
 *	Created ..: Monday 21-Jan-91 20:12
 *	Revision .: 0
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	21-Jan-91       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

#include "TermGlobal.h"

enum	{	MEN_SKIP=1,MEN_ONLINE,MEN_ABORT,MEN_QUITPANEL };
enum	{	MEN_OKAY=1,MEN_CANCEL,MEN_LOADSOMEFILE };

	/* How many options will xpr_options display in a single column. */

#define OPTION_WRAP 16

STATIC struct NewMenu OptionsMenu[] =
{
	{ NM_TITLE, "Project",		 0 , 0, 0, (APTR)0},
	{  NM_ITEM, "Quit",		"Q", 0, 0, (APTR)MEN_QUITPANEL},
	{ NM_END, 0,			 0 , 0, 0, (APTR)0}
};

struct NewMenu GetsMenu[] =
{
	{ NM_TITLE, "Project",		 0 , 0, 0, (APTR)0},
	{  NM_ITEM, "Okay",		"O", 0, 0, (APTR)MEN_OKAY},
	{  NM_ITEM, "Cancel",		"C", 0, 0, (APTR)MEN_CANCEL},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0, 0, (APTR)0},
	{  NM_ITEM, "Quit",		"Q", 0, 0, (APTR)MEN_QUITPANEL},
	{ NM_END, 0,			 0 , 0, 0, (APTR)0}
};

	/* These variables keep the transferred bytes and transfer
	 * time in seconds.
	 */

STATIC LONG	ByteVal,ByteMax,TimeVal,TimeMax;

	/* The name of the file being transmitted, in case the
	 * `override destination' feature is enabled.
	 */

STATIC UBYTE	RealName[256];

STATIC BYTE	OverrideDestination = TRUE;

	/* xpr_fopen(UBYTE *FileName,UBYTE *AccessMode):
	 *
	 *	Open a file for random access.
	 */

LONG __saveds __asm
xpr_fopen(register __a0 UBYTE *FileName,register __a1 UBYTE *AccessMode)
{
	struct Buffer *File;

		/* Reset transfer counters. */

	ByteVal = ByteMax = TimeVal = TimeMax = 0;

		/* Disable the time and data bars in the transfer window. */

	if(TransferWindow)
	{
		GhostStats(TransferGadgetArray[2]);
		GhostStats(TransferGadgetArray[3]);
	}

		/* Remember file name. */

	strcpy(RealName,FileName);

		/* Build a new filename if neccessary. */

	if(OverrideDestination)
	{
		if(!Uploading)
		{
			if(!DownloadPath)
				strcpy(RealName,&Config . BinaryDownloadPath[0]);
			else
				strcpy(RealName,DownloadPath);

			if(AddPart(RealName,FilePart(FileName),256))
				FileName = RealName;
			else
				strcpy(RealName,FileName);
		}
	}

		/* Determine file transfer mode... */

	if(File = BufferOpen(FileName,AccessMode))
	{
		switch(AccessMode[0])
		{
			case 'r':	LogAction("Send file \"%s\".",FileName);
					break;

			case 'w':	LogAction("Receive file \"%s\".",FileName);
					AddDownloadObject(FileName);
					break;

			case 'a':	LogAction("Update file \"%s\".",FileName);
					break;
		}
	}

	return((LONG)File);
}

	/* xpr_fclose(struct Buffer *File):
	 *
	 *	Close a file opened by xpr_fopen.
	 */

LONG __saveds __asm
xpr_fclose(register __a0 struct Buffer *File)
{
	BYTE WriteAccess = File -> WriteAccess;

		/* Close the file and see what it brings... */

	if(BufferClose(File))
	{
			/* Did we receive or send a file? */

		if(WriteAccess)
		{
			LONG Size;

				/* Did the file remain empty? */

			if(!(Size = GetFileSize(RealName)))
			{
					/* Delete empty file. */

				if(DeleteFile(RealName))
					LogAction("Close file \"%s\" (incomplete file removed).",RealName);
				else
					LogAction("Close file \"%s\".",RealName);
			}
			else
			{
					/* Try to identify the file type. */

				Identify(RealName);

				LogAction("Close file \"%s\", %ld bytes.",RealName,Size);
			}
		}
		else
			LogAction("Close file \"%s\".",RealName);
	}

	RealName[0] = 0;

	return(1);
}

	/* xpr_fread(APTR Buffer,LONG Size,LONG Count,struct Buffer *File):
	 *
	 *	Read a few bytes from a file.
	 */

LONG __saveds __asm
xpr_fread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File)
{
	return(BufferRead(File,Buffer,Size * Count) / Size);
}

	/* xpr_fwrite(APTR Buffer,LONG Size,LONG Count,struct Buffer *File):
	 *
	 *	Write a few bytes to a file.
	 */

LONG __saveds __asm
xpr_fwrite(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File)
{
	return(BufferWrite(File,Buffer,Size * Count) / Size);
}

	/* xpr_fseek(struct Buffer *File,LONG Offset,LONG Origin):
	 *
	 *	Move the read/write pointer in a file.
	 */

LONG __saveds __asm
xpr_fseek(register __a0 struct Buffer *File,register __d0 LONG Offset,register __d1 LONG Origin)
{
	return(BufferSeek(File,Offset,Origin) ? 0 : -1);
}

	/* xpr_sread(UBYTE *Buffer,LONG Size,LONG Timeout):
	 *
	 *	Read a few bytes from the serial port (including
	 *	timeouts).
	 */

ULONG __saveds __asm
xpr_sread(register __a0 UBYTE *Buffer,register __d0 ULONG Size,register __d1 LONG Timeout)
{
		/* Valid parameters? */

	if(WriteRequest && Size)
	{
			/* How many bytes are still in the serial buffer? */

		WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

			/* No timeout specified? Return as many bytes
			 * as are currently within the buffer.
			 */

		if(Timeout < 1)
		{
			register ULONG Length;

				/* Are there any bytes in the buffer? */

			if(Length = WriteRequest -> IOSer . io_Actual)
			{
					/* More bytes available than we
					 * were requested?
					 */

				if(Length > Size)
					Length = Size;

					/* Fill the buffer. */

				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= Buffer;
				ReadRequest -> IOSer . io_Length	= Length;

				DoIO(ReadRequest);
			}

				/* Return number of bytes read. */

			return(Length);
		}
		else
		{
				/* Small enhancement: if the serial buffer holds as many
				 * bytes as we may need, read the data immediately and
				 * return as quickly as possible.
				 */

			if(WriteRequest -> IOSer . io_Actual >= Size)
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= Buffer;
				ReadRequest -> IOSer . io_Length	= Size;

				if(!DoIO(ReadRequest))
					return(Size);
				else
					return(ReadRequest -> IOSer . io_Error ? -1 : ReadRequest -> IOSer . io_Actual);
			}
			else
			{
				register ULONG SignalSet;

					/* Set up the timer. */

				TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
				TimeRequest -> tr_time . tv_secs	= Timeout >= MILLION ? Timeout / MILLION : 0;
				TimeRequest -> tr_time . tv_micro	= Timeout % MILLION;

					/* Set up the read request. */

				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= Buffer;
				ReadRequest -> IOSer . io_Length	= Size;

					/* Prevent early termination. */

				SetSignal(0,(1 << ReadPort -> mp_SigBit) | (1 << TimeRequest -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit));

					/* Start IO... */

				SendIO(ReadRequest);
				SendIO(TimeRequest);

				FOREVER
				{
						/* Build signal mask. */

					if(TransferWindow)
						SignalSet = (1 << ReadPort -> mp_SigBit) | (1 << TimeRequest -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit) | (1 << TransferWindow -> UserPort -> mp_SigBit);
					else
						SignalSet = (1 << ReadPort -> mp_SigBit) | (1 << TimeRequest -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit);

						/* Wait for either of both IORequests to return. */

					SignalSet = Wait(SignalSet);

						/* Hit by timeout? */

					if(SignalSet & (1 << TimeRequest -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit))
					{
						AbortIO(ReadRequest);
						WaitIO(ReadRequest);

						WaitIO(TimeRequest);

						return(ReadRequest -> IOSer . io_Actual);
					}

						/* Receive buffer filled? */

					if(SignalSet & (1 << ReadPort -> mp_SigBit))
					{
						AbortIO(TimeRequest);
						WaitIO(TimeRequest);

						WaitIO(ReadRequest);

						return(ReadRequest -> IOSer . io_Actual);
					}

						/* Check the transfer window for
						 * possible user abort.
						 */

					if(TransferWindow)
					{
						if(SignalSet & (1 << TransferWindow -> UserPort -> mp_SigBit))
						{
							if(xpr_chkabort() == -1)
							{
								AbortIO(ReadRequest);
								AbortIO(TimeRequest);

								WaitIO(ReadRequest);
								WaitIO(TimeRequest);

								return(-1);
							}
						}
					}
				}
			}
		}
	}

	return(0);
}

	/* xpr_swrite(UBYTE *Buffer,LONG Size):
	 *
	 *	Write a few bytes to the serial port.
	 */

LONG __saveds __asm
xpr_swrite(register __a0 UBYTE *Buffer,register __d0 LONG Size)
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command	= CMD_WRITE;
		WriteRequest -> IOSer . io_Data		= Buffer;
		WriteRequest -> IOSer . io_Length	= Size;

		return((LONG)DoIO(WriteRequest));
	}
	else
		return(-1);
}

	/* xpr_sflush():
	 *
	 *	Release the contents of all serial buffers.
	 */

LONG __saveds
xpr_sflush()
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = CMD_CLEAR;

		return((LONG)DoIO(WriteRequest));
	}
	else
		return(-1);
}

	/* GetSeconds(UBYTE *String):
	 *
	 *	Tries to turn a string of the format hh:mm:ss into
	 *	an integer number.
	 */

STATIC LONG __regargs
GetSeconds(UBYTE *String)
{
	UBYTE	Buffer[20];
	LONG	Seconds = 0;

	memset(Buffer,0,20);

	strcpy(Buffer,String);

	Seconds += atol(&Buffer[6]);

	Buffer[5] = 0;

	Seconds += atol(&Buffer[3]) * 60;

	Buffer[2] = 0;

	Seconds += atol(&Buffer[0]) * 3600;

	return(Seconds);
}

	/* TruncateName(UBYTE *FileName):
	 *
	 *	Truncates a file name to a maximum of 48 characters.
	 */

STATIC UBYTE * __regargs
TruncateName(UBYTE *FileName)
{
	STATIC UBYTE	NameBuffer[256];
	WORD		Len = strlen(FileName);

	strcpy(NameBuffer,FileName);

	if(Len > 48)
	{
		WORD i;

		if(Len > 48)
		{
			for(i = Len - 48 ; i < Len ; i++)
			{
				if(i >= Len - 44 && FileName[i] == '/')
				{
					strcpy(NameBuffer,".../");

					strcat(NameBuffer,&FileName[i + 1]);

					Len = strlen(NameBuffer);

					break;
				}
			}
		}
	}

	return(NameBuffer);
}

	/* xpr_update(struct XPR_UPDATE *UpdateInfo):
	 *
	 *	Update the information displayed in the transfer window.
	 */

LONG __saveds __asm
xpr_update(register __a0 struct XPR_UPDATE *UpdateInfo)
{
	if(!TransferWindow)
	{
		BlockWindows();

		LogAction("Initiate binary download.");

		if(!TransferPanel("Receive File(s)"))
		{
			ReleaseWindows();

			return(0);
		}
	}

	if(UpdateInfo)
	{
		BYTE	NewByte = FALSE,
			NewTime = FALSE;

		if((UpdateInfo -> xpru_updatemask & XPRU_PROTOCOL) && UpdateInfo -> xpru_protocol)
			TransferInfo(20, 0,"%-48.48s",UpdateInfo -> xpru_protocol);

		if((UpdateInfo -> xpru_updatemask & XPRU_FILENAME) && UpdateInfo -> xpru_filename)
		{
			if(OverrideDestination && !Uploading)
			{
				if(!RealName[0])
				{
					if(!DownloadPath)
						strcpy(RealName,&Config . BinaryDownloadPath[0]);
					else
						strcpy(RealName,DownloadPath);

					if(!AddPart(RealName,FilePart(UpdateInfo -> xpru_filename),256))
						strcpy(RealName,UpdateInfo -> xpru_filename);
				}

				TransferInfo(20, 3,"%-48.48s",TruncateName(RealName));
			}
			else
				TransferInfo(20, 3,"%-48.48s",TruncateName(UpdateInfo -> xpru_filename));
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_FILESIZE) && UpdateInfo -> xpru_filesize != -1)
		{
			TransferInfo(20, 5,"%ld",UpdateInfo -> xpru_filesize);

			if(ByteMax = UpdateInfo -> xpru_filesize)
				NewByte = TRUE;
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_BYTES) && UpdateInfo -> xpru_bytes != -1)
		{
			TransferInfo(20, 8,"%ld",UpdateInfo -> xpru_bytes);

			ByteVal = UpdateInfo -> xpru_bytes;

			if(ByteMax)
				NewByte = TRUE;
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKS) && UpdateInfo -> xpru_blocks != -1)
			TransferInfo(595, 8,"%ld",UpdateInfo -> xpru_blocks);

		if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKCHECK) && UpdateInfo -> xpru_blockcheck)
			TransferInfo(20,10,"%-12.12s",UpdateInfo -> xpru_blockcheck);

		if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKSIZE) && UpdateInfo -> xpru_blocksize != -1)
			TransferInfo(55,10,"%ld",UpdateInfo -> xpru_blocksize);

		if((UpdateInfo -> xpru_updatemask & XPRU_EXPECTTIME) && UpdateInfo -> xpru_expecttime)
		{
			TransferInfo(20,13,"%-12.12s",UpdateInfo -> xpru_expecttime);

			if(TimeMax = GetSeconds((UBYTE *)UpdateInfo -> xpru_expecttime))
				NewTime = TRUE;
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_ELAPSEDTIME) && UpdateInfo -> xpru_elapsedtime)
		{
			TransferInfo(55,13,"%-12.12s",UpdateInfo -> xpru_elapsedtime);

			TimeVal = GetSeconds((UBYTE *)UpdateInfo -> xpru_elapsedtime);

			if(TimeMax)
				NewTime = TRUE;
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_MSG) && UpdateInfo -> xpru_msg)
			TransferInfo(20,16,"%-48.48s",UpdateInfo -> xpru_msg);

		if((UpdateInfo -> xpru_updatemask & XPRU_ERRORMSG) && UpdateInfo -> xpru_errormsg)
			TransferInfo(20,18,"%-48.48s",UpdateInfo -> xpru_errormsg);

		if((UpdateInfo -> xpru_updatemask & XPRU_ERRORS) && UpdateInfo -> xpru_errors != -1)
			TransferInfo(20,21,"%ld",UpdateInfo -> xpru_errors);

		if((UpdateInfo -> xpru_updatemask & XPRU_TIMEOUTS) && UpdateInfo -> xpru_timeouts != -1)
			TransferInfo(55,21,"%ld",UpdateInfo -> xpru_timeouts);

		if((UpdateInfo -> xpru_updatemask & XPRU_PACKETTYPE) && UpdateInfo -> xpru_packettype != -1)
			TransferInfo(20,24,"%ld/%lc",UpdateInfo -> xpru_packettype,UpdateInfo -> xpru_packettype);

		if((UpdateInfo -> xpru_updatemask & XPRU_PACKETDELAY) && UpdateInfo -> xpru_packetdelay != -1)
			TransferInfo(55,24,"%ld",UpdateInfo -> xpru_packetdelay);

		if((UpdateInfo -> xpru_updatemask & XPRU_DATARATE) && UpdateInfo -> xpru_datarate != -1)
			TransferInfo(20,26,"%ld",UpdateInfo -> xpru_datarate);

		if((UpdateInfo -> xpru_updatemask & XPRU_CHARDELAY) && UpdateInfo -> xpru_chardelay != -1)
			TransferInfo(55,26,"%ld",UpdateInfo -> xpru_chardelay);

		if(TransferWindow)
		{
			if(NewByte)
			{
				ShowStats(TransferGadgetArray[2],ByteVal,ByteMax);

				if(ByteMax)
					ShowString(TransferGadgetArray[2],"%3ld%%",(100 * ByteVal) / ByteMax);
				else
					ShowString(TransferGadgetArray[2],"%3ld%%",0);
			}

			if(NewTime)
			{
				LONG TimeDif = (TimeMax - TimeVal) < 0 ? 0 : TimeMax - TimeVal;

				ShowStats(TransferGadgetArray[3],TimeDif,TimeMax);

				ShowString(TransferGadgetArray[3],"%2ld:%02ld:%02ld",TimeDif / 3600,TimeDif / 60,TimeDif % 60);
			}
		}
	}

	return(1);
}

	/* xpr_chkabort():
	 *
	 *	Check if the user has aborted the transfer.
	 */

LONG __saveds
xpr_chkabort()
{
	if(TransferWindow)
	{
		struct IntuiMessage	*Massage;
		ULONG			 Class,Code;
		struct Gadget		*Gadget;

		while(Massage = (struct IntuiMessage *)GT_GetIMsg(TransferWindow -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;
			Gadget	= (struct Gadget *)Massage -> IAddress;

			GT_ReplyIMsg(Massage);

			if(Class == IDCMP_MENUPICK)
			{
				struct MenuItem *MenuItem;

				while(Code != MENUNULL)
				{
					if(MenuItem = ItemAddress(TransferMenu,Code))
					{
						switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
						{
							case MEN_ABORT:
							case MEN_QUITPANEL:	LogAction("Transfer aborted.");

										return(-1);

										break;

							case MEN_SKIP:		LogAction("File skipped.");

										return(1);

							default:		break;
						}
					}

					Code = MenuItem -> NextSelect;
				}
			}

			if(Class == IDCMP_CLOSEWINDOW)
			{
				LogAction("Transfer aborted.");

				return(-1);
			}

			if(Class == IDCMP_GADGETUP)
			{
				if(Gadget -> GadgetID == 0)
				{
					LogAction("Transfer aborted.");

					return(-1);
				}

				if(Gadget -> GadgetID == 1)
				{
					LogAction("File skipped.");

					return(1);
				}
			}
		}
	}

	return(0);
}

	/* The following subroutine creates the gadgets required by
	 * xpr_gets().
	 */

struct Gadget *
CreateAllGetsGadgets(BYTE LoadGadget,UBYTE *String,UBYTE *Prompt,LONG *WindowWidth,struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge,struct Screen *Screen)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

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

	if(Gadget = CreateContext(GadgetList))
	{
		WORD Width = strlen(Prompt);

		if(Width < 40)
			Width = 40;

		NewGadget . ng_Width		= Width * 8 + 6;
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetText	= Prompt;
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_ABOVE;
		NewGadget . ng_LeftEdge		= 10;
		NewGadget . ng_TopEdge		= 1 + TopEdge + 8 + 8;

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,	256,
			GTST_String,	String,
			GTST_EditHook,	&CommandHook,
		TAG_DONE);

		NewGadget . ng_Width		= 52;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "_Okay";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + NewGadget . ng_Height + 3;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		if(LoadGadget)
		{
			NewGadget . ng_Width		= 76;
			NewGadget . ng_GadgetText	= "_Load File";
			NewGadget . ng_GadgetID		= Counter;
			NewGadget . ng_LeftEdge		= (Width * 8 + 6 - NewGadget . ng_Width) >> 1;

			GadgetArray[Counter] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
				GT_Underscore,	'_',
			TAG_DONE);
		}

		Counter++;

		NewGadget . ng_Width		= 52;
		NewGadget . ng_GadgetText	= "_Cancel";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= GadgetArray[0] -> LeftEdge + GadgetArray[0] -> Width + 6 - NewGadget . ng_Width;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		if(Gadget)
			*WindowWidth = GadgetArray[0] -> Width + 32;
	}

	return(Gadget);
}

	/* xpr_gets(UBYTE *Prompt,UBYTE *Buffer):
	 *
	 *	Prompt the user for string input.
	 */

LONG __saveds __asm
xpr_gets(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer)
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[4];
	struct Window	*PanelWindow;
	struct Menu	*PanelMenu;
	LONG		 Width,Success = FALSE;

	if(!Prompt)
		Prompt = "Input Required";

	if(CreateAllGetsGadgets(FALSE,Buffer,Prompt,&Width,&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1,Screen))
	{
		if(PanelMenu = CreateMenus(GetsMenu,
			GTMN_FrontPen, 0,
		TAG_DONE))
		{
			if(LayoutMenus(PanelMenu,VisualInfo,
				GTMN_TextAttr,&DefaultFont,
			TAG_DONE))
			{
				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_MENUPICK | IDCMP_RAWKEY | BUTTONIDCMP | STRINGIDCMP,

					WA_Title,	"Enter Text",
				TAG_DONE))
				{
					struct IntuiMessage	*Massage;
					ULONG			 Class,Code;
					struct Gadget		*Gadget;
					BYTE			 Terminated = FALSE;

					PushWindow(PanelWindow);

					SetMenuStrip(PanelWindow,PanelMenu);

					PanelWindow -> Flags &= ~WFLG_RMBTRAP;

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

					ActiveGadget = GadgetArray[0];

					ActivateGadget(GadgetArray[0],PanelWindow,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_GADGETDOWN)
							{
								if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
									ActiveGadget = Gadget;
							}

							if(Class == IDCMP_RAWKEY)
							{
								if(Code == IECODE_UP_PREFIX|103 && CommandWindow == PanelWindow)
									ActivateGadget(CommandGadget,PanelWindow,NULL);
							}

							if(Class == IDCMP_ACTIVEWINDOW)
								ActivateGadget(GadgetArray[0],PanelWindow,NULL);

							if(Class == IDCMP_MENUPICK)
							{
								struct MenuItem *MenuItem;

								while(Code != MENUNULL)
								{
									MenuItem = ItemAddress(PanelMenu,Code);

									switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
									{
										case MEN_CANCEL:
										case MEN_QUITPANEL:	Class = IDCMP_CLOSEWINDOW;
													break;

										case MEN_OKAY:		strcpy(Buffer,((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> Buffer);

													Success = TRUE;

													Terminated = TRUE;
													break;
									}

									Code = MenuItem -> NextSelect;
								}

								if(ActiveGadget)
									ActivateGadget(ActiveGadget,PanelWindow,NULL);
							}

							if(Class == IDCMP_CLOSEWINDOW)
								Terminated = TRUE;

							if(Class == IDCMP_GADGETUP)
							{
								if(!DontActivate)
								{
									switch(Gadget -> GadgetID)
									{
										case 0:
										case 1:	strcpy(Buffer,((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> Buffer);

											Success = TRUE;

											Terminated = TRUE;
											break;

										case 3:	Terminated = TRUE;
											break;
									}
								}
								else
									DontActivate = FALSE;
							}
						}
					}

					PanelWindow -> Flags |= WFLG_RMBTRAP;

					ClearMenuStrip(PanelWindow);

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

					PopWindow();

					CloseWindow(PanelWindow);
				}

				FreeGadgets(GadgetList);
			}

			FreeMenus(PanelMenu);
		}
	}

	return(Success);
}

	/* xpr_setserial(LONG Status):
	 *
	 *	Set/read the serial status (parameters).
	 */

LONG __saveds __asm
xpr_setserial(register __d0 LONG Status)
{
	if(WriteRequest)
	{
		STATIC LONG XprBauds[12] =
		{
			   110,
			   300,
			  1200,
			  2400,
			  4800,
			  9600,
			 19200,
			 31250,
			 38400,
			 57600,
			 76800,
			115200
		};

		LONG Return,i;

		WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

		Return = WriteRequest -> io_SerFlags & 0xFF;

		if(WriteRequest -> io_ExtFlags & SEXTF_MSPON)
			Return |= ST_PARTYMARKON;

		if(WriteRequest -> io_ExtFlags & SEXTF_MARK)
			Return |= ST_PARTYMARK;

		if(WriteRequest -> io_StopBits == 2)
			Return |= ST_2BITS;

		if(WriteRequest -> io_ReadLen == 7)
			Return |= ST_READ7;

		if(WriteRequest -> io_WriteLen == 7)
			Return |= ST_WRITE7;

		for(i = 0 ; i < 12 ; i++)
		{
			if(WriteRequest -> io_Baud <= XprBauds[i])
			{
				Return |= (i << 16);

				break;
			}
		}

		if(Status != -1)
		{
			WriteRequest -> IOSer . io_Command	= SDCMD_SETPARAMS;

			WriteRequest -> io_SerFlags		= Status & 0xFF;
			WriteRequest -> io_ExtFlags		= 0;

			if(Status & ST_PARTYMARKON)
				WriteRequest -> io_ExtFlags |= SEXTF_MSPON;

			if(Status & ST_PARTYMARK)
				WriteRequest -> io_ExtFlags |= SEXTF_MARK;

			if(Status & ST_2BITS)
				WriteRequest -> io_StopBits = 2;
			else
				WriteRequest -> io_StopBits = 1;

			if(Status & ST_READ7)
				WriteRequest -> io_ReadLen = 7;
			else
				WriteRequest -> io_ReadLen = 8;

			if(Status & ST_WRITE7)
				WriteRequest -> io_WriteLen = 7;
			else
				WriteRequest -> io_WriteLen = 8;

			DoIO(WriteRequest);

			ReadRequest -> io_SerFlags	= WriteRequest -> io_SerFlags;
			ReadRequest -> io_ExtFlags	= WriteRequest -> io_ExtFlags;

			ReadRequest -> io_StopBits	= WriteRequest -> io_StopBits;
			ReadRequest -> io_ReadLen	= WriteRequest -> io_ReadLen;
			ReadRequest -> io_WriteLen	= WriteRequest -> io_WriteLen;
		}

		return(Return);
	}
	else
		return(-1);
}

	/* xpr_ffirst(UBYTE *Buffer,UBYTE *Pattern):
	 *
	 *	Batch file upload: find the first matching file and return
	 *	its name.
	 */

LONG __saveds __asm
xpr_ffirst(register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern)
{
	if(MultipleFiles)
	{
		FileCount = 0;

		strcpy(Buffer,FileArg[FileCount++] . wa_Name);

		return(1);
	}
	else
	{
		FileMatch = TRUE;

		if(!MatchFirst(Pattern,FileAnchor))
		{
			if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
			{
				strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);

				return(1);
			}
			else
			{
				while(!MatchNext(FileAnchor))
				{
					if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
					{
						strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);

						return(1);
					}
				}
			}
		}
	}

	return(0);
}

	/* xpr_fnext(LONG OldState,UBYTE *Buffer,UBYTE *Pattern):
	 *
	 *	Batch file upload: find the next matching file
	 *	- if any - and return its name.
	 */

LONG __saveds __asm
xpr_fnext(register __d0 LONG OldState,register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern)
{
	if(MultipleFiles)
	{
		if(FileCount < FileCountMax)
		{
			strcpy(Buffer,FileArg[FileCount++] . wa_Name);

			return(1);
		}
	}
	else
	{
		FileMatch = TRUE;

		while(!MatchNext(FileAnchor))
		{
			if(FileAnchor -> ap_Info . fib_DirEntryType < 0)
			{
				strcpy(Buffer,FileAnchor -> ap_Info . fib_FileName);

				return(1);
			}
		}
	}

	return(0);
}

	/* xpr_finfo(UBYTE *FileName,LONG InfoType):
	 *
	 *	Return information on a given file.
	 */

LONG __saveds __asm
xpr_finfo(register __a0 UBYTE *FileName,register __d0 LONG InfoType)
{
	BPTR FileLock;

	switch(InfoType)
	{
		case 1:	if(FileLock = Lock(FileName,ACCESS_READ))
			{
				struct FileInfoBlock __aligned	FileInfo;
				register LONG			Size;

				if(Examine(FileLock,&FileInfo))
					Size = FileInfo . fib_Size;
				else
					Size = 0;

				UnLock(FileLock);

				return(Size);
			}

			break;

		case 2:	return(BinaryTransfer ? 1 : 2);
	}

	return(0);
}

	/* The following routines are to support the xpr_options function. */

STATIC BYTE __regargs
GetOptionMode(struct xpr_option *Option)
{
	if(Option)
	{
		if(!Stricmp(Option -> xpro_value,"OFF"))
			return(FALSE);

		if(!Stricmp(Option -> xpro_value,"FALSE"))
			return(FALSE);

		if(!Stricmp(Option -> xpro_value,"F"))
			return(FALSE);

		if(!Stricmp(Option -> xpro_value,"NO"))
			return(FALSE);

		if(!Stricmp(Option -> xpro_value,"N"))
			return(FALSE);


		if(!Stricmp(Option -> xpro_value,"ON"))
			return(TRUE);

		if(!Stricmp(Option -> xpro_value,"TRUE"))
			return(TRUE);

		if(!Stricmp(Option -> xpro_value,"T"))
			return(TRUE);

		if(!Stricmp(Option -> xpro_value,"YES"))
			return(TRUE);

		if(!Stricmp(Option -> xpro_value,"Y"))
			return(TRUE);
	}

	return(FALSE);
}

STATIC struct Gadget *
CreateAllOptionGadgets(LONG *Count,LONG *Width,LONG *Height,LONG NumOpts,struct xpr_option *Opts[],struct Gadget *GadgetArray[],struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	LONG			 i,MaxLength = 0,MaxLength1 = 0,AddTop = 0,Len,LeftPlus = 0,MaxRight = 0,MaxWidth = 0,NumOptsSmall,Tmp;

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

	*Count = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_TopEdge		= TopEdge + 1;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_Width		= 208;

		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_Flags		= NG_HIGHLABEL;

			/* Precalculate gadget left edge offset. */

		if((NumOptsSmall = NumOpts) > OPTION_WRAP)
			NumOptsSmall = OPTION_WRAP;

		for(i = 0 ; i < NumOptsSmall ; i++)
		{
			if(Opts[i])
			{
				switch(Opts[i] -> xpro_type)
				{
					case XPRO_BOOLEAN:
					case XPRO_LONG:
					case XPRO_STRING:
					case XPRO_COMMPAR:

						if((Len = strlen(Opts[i] -> xpro_description)) > MaxLength)
							MaxLength = Len;

						break;

					case XPRO_COMMAND:

						if(MaxLength < 25)
							MaxLength = 25;

						break;

					default:

						break;
				}
			}
		}

		if(MaxLength < 16)
			MaxLength = 16;

		if(MaxLength < 25)
			MaxLength = (25 - MaxLength) * 8;
		else
			MaxLength = 0;

		for(i = OPTION_WRAP ; i < NumOpts ; i++)
		{
			if(Opts[i])
			{
				switch(Opts[i] -> xpro_type)
				{
					case XPRO_BOOLEAN:
					case XPRO_LONG:
					case XPRO_STRING:
					case XPRO_COMMPAR:

						if((Len = strlen(Opts[i] -> xpro_description)) > MaxLength1)
							MaxLength1 = Len;

						break;

					case XPRO_COMMAND:

						if(MaxLength1 < 25)
							MaxLength1 = 25;

						break;

					default:

						break;
				}
			}
		}

		if(MaxLength1 < 16)
			MaxLength1 = 16;

		if(MaxLength1 < 25)
			MaxLength1 = (25 - MaxLength1) * 8;
		else
			MaxLength1 = 0;

		*Height	= 0;

		for(i = 0 ; i < NumOpts ; i++)
		{
			if(!Opts[i])
				continue;

			switch(Opts[i] -> xpro_type)
			{
				case XPRO_BOOLEAN:

					NewGadget . ng_GadgetText	= Opts[i] -> xpro_description;
					NewGadget . ng_GadgetID		= i;
					NewGadget . ng_Width		= 26;
					NewGadget . ng_Height		= 11;
					NewGadget . ng_LeftEdge		= 218 - MaxLength + LeftPlus;
					NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + AddTop;

					GadgetArray[i] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
						GTCB_Checked,	GetOptionMode(Opts[i]),
					TAG_DONE);

					break;

				case XPRO_LONG:

					NewGadget . ng_GadgetText	= Opts[i] -> xpro_description;
					NewGadget . ng_GadgetID		= i;
					NewGadget . ng_LeftEdge		= 218 - MaxLength + LeftPlus;
					NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + AddTop;
					NewGadget . ng_Height		= 14;

					GadgetArray[i] = Gadget = CreateGadget(INTEGER_KIND,Gadget,&NewGadget,
						GTIN_Number,	atol(Opts[i] -> xpro_value),
						GTST_EditHook,	&CommandHook,
					TAG_DONE);

					break;

				case XPRO_STRING:
				case XPRO_COMMPAR:

					NewGadget . ng_GadgetText	= Opts[i] -> xpro_description;
					NewGadget . ng_GadgetID		= i;
					NewGadget . ng_LeftEdge		= 218 - MaxLength + LeftPlus;
					NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + AddTop;
					NewGadget . ng_Height		= 14;

					GadgetArray[i] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
						GTST_String,	Opts[i] -> xpro_value,
						GTST_MaxChars,	Opts[i] -> xpro_length,
						GTST_EditHook,	&CommandHook,
					TAG_DONE);

					break;

				case XPRO_HEADER:

					NewGadget . ng_GadgetText	= "";
					NewGadget . ng_GadgetID		= i;
					NewGadget . ng_LeftEdge		= 10 + LeftPlus;
					NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + AddTop;
					NewGadget . ng_Flags		= 0;
					NewGadget . ng_Height		= 8;
					NewGadget . ng_Width		= 8 * strlen(Opts[i] -> xpro_description);

					GadgetArray[i] = Gadget = CreateGadget(TEXT_KIND,Gadget,&NewGadget,
						GTTX_Text,	Opts[i] -> xpro_description,
					TAG_DONE);

					break;

				case XPRO_COMMAND:

					NewGadget . ng_GadgetText	= Opts[i] -> xpro_description;
					NewGadget . ng_GadgetID		= i;
					NewGadget . ng_LeftEdge		= 10 + LeftPlus;
					NewGadget . ng_TopEdge		= NewGadget . ng_TopEdge + 13;

					GadgetArray[i] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
						TAG_DONE);

					break;

				default:break;
			}

			if(MaxWidth < (Tmp = NewGadget . ng_LeftEdge + NewGadget .ng_Width + 10))
				MaxWidth = Tmp;

			AddTop = NewGadget . ng_Height + 1;

			if(i < OPTION_WRAP)
			{
				if(NewGadget . ng_LeftEdge + NewGadget . ng_Width > MaxRight)
					MaxRight = NewGadget . ng_LeftEdge + NewGadget . ng_Width;

				if(i == OPTION_WRAP - 1)
				{
					LeftPlus = MaxRight;

					*Height	= NewGadget . ng_TopEdge + NewGadget . ng_Height + 3;

					MaxLength = MaxLength1;

					NewGadget . ng_TopEdge = TopEdge + 1;

					AddTop = 0;
				}
			}

			if(i < NumOpts - 1)
			{
				NewGadget . ng_Height	= 12;
				NewGadget . ng_Width	= 208;
				NewGadget . ng_Flags	= NG_HIGHLABEL;
			}
		}

		if(Gadget)
		{
			if(NewGadget . ng_TopEdge + NewGadget . ng_Height + 3 > *Height)
				*Height = NewGadget . ng_TopEdge + NewGadget . ng_Height + 3;

			*Count	= i;
			*Width	= MaxWidth;
		}
	}

	return(Gadget);
}

	/* xpr_options(LONG NumOpts,struct xpr_option **Opts):
	 *
	 *	Provide a more polished user interface to set the
	 *	transfer protocol options.
	 */

ULONG __saveds __asm
xpr_options(register __d0 LONG NumOpts,register __a0 struct xpr_option **Opts)
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[34];
	struct Window	*PanelWindow;
	struct Menu	*PanelMenu;
	LONG		 Width,Height,i,Count;

	ULONG		 Flags = 0;

	if(CreateAllOptionGadgets(&Count,&Width,&Height,NumOpts,Opts,&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(PanelMenu = CreateMenus(OptionsMenu,
			GTMN_FrontPen, 0,
		TAG_DONE))
		{
			if(LayoutMenus(PanelMenu,VisualInfo,
				GTMN_TextAttr,&DefaultFont,
			TAG_DONE))
			{
				if(PanelWindow = OpenWindowTags(NULL,
					WA_Width,	Width,
					WA_Height,	Height,

					WA_Left,	(Screen -> Width - Width) >> 1,
					WA_Top,		(Screen -> Height - Height) >> 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 | CHECKBOXIDCMP | IDCMP_MENUPICK | IDCMP_RAWKEY,

					WA_Title,	OptionTitle ? OptionTitle : "Transfer Preferences",
				TAG_DONE))
				{
					struct IntuiMessage	*Massage;
					ULONG			 Class,Code;
					struct Gadget		*Gadget;
					BYTE			 Terminated = FALSE;

					PushWindow(PanelWindow);

					SetMenuStrip(PanelWindow,PanelMenu);

					PanelWindow -> Flags &= ~WFLG_RMBTRAP;

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

					for(i = 0 ; i < Count ; i++)
					{
						if((GadgetArray[i] -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
						{
							ActivateGadget(GadgetArray[i],PanelWindow,NULL);

							ActiveGadget = GadgetArray[i];

							break;
						}
					}

					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_GADGETDOWN)
							{
								if((Gadget -> GadgetType & GTYP_GTYPEMASK) == GTYP_STRGADGET)
									ActiveGadget = Gadget;
							}

							if(Class == IDCMP_RAWKEY)
							{
								if(Code == IECODE_UP_PREFIX|103 && CommandWindow == PanelWindow)
									ActivateGadget(CommandGadget,PanelWindow,NULL);
							}

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

							if(Class == IDCMP_MENUPICK)
							{
								struct MenuItem *MenuItem;

								while(Code != MENUNULL)
								{
									MenuItem = ItemAddress(PanelMenu,Code);

									switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
									{
										case MEN_QUITPANEL:	Class = IDCMP_CLOSEWINDOW;
													break;
									}

									Code = MenuItem -> NextSelect;
								}

								if(ActiveGadget)
									ActivateGadget(ActiveGadget,PanelWindow,NULL);
							}

							if(Class == IDCMP_CLOSEWINDOW)
							{
								NewOptions = FALSE;

								for(i = 0 ; i < NumOpts ; i++)
								{
									switch(Opts[i] -> xpro_type)
									{
										case XPRO_BOOLEAN:	if(((GadgetArray[i] -> Flags & GFLG_SELECTED) && !GetOptionMode(Opts[i])) || (!(GadgetArray[i] -> Flags & GFLG_SELECTED) && GetOptionMode(Opts[i])))
													{
														Flags |= (i << i);

														if(GadgetArray[i] -> Flags & GFLG_SELECTED)
															strcpy(Opts[i] -> xpro_value,"yes");
														else
															strcpy(Opts[i] -> xpro_value,"no");

														NewOptions = TRUE;
													}
													break;

										case XPRO_COMMPAR:
										case XPRO_LONG:
										case XPRO_STRING:	if(strcmp(Opts[i] -> xpro_value,((struct StringInfo *)GadgetArray[i] -> SpecialInfo) -> Buffer))
													{
														Flags |= (i << i);

														strcpy(Opts[i] -> xpro_value,((struct StringInfo *)GadgetArray[i] -> SpecialInfo) -> Buffer);

														NewOptions = TRUE;
													}
													break;

										default:		break;
									}
								}

								Terminated = TRUE;
							}

							if(Class == IDCMP_GADGETUP)
							{
								if(Gadget -> GadgetID < NumOpts)
								{
									if(Opts[Gadget -> GadgetID] -> xpro_type == XPRO_COMMAND)
									{
										Flags |= (1 << Gadget -> GadgetID);

										Terminated = TRUE;
									}
								}
							}
						}
					}

					PanelWindow -> Flags |= WFLG_RMBTRAP;

					ClearMenuStrip(PanelWindow);

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

					PopWindow();

					CloseWindow(PanelWindow);
				}
			}

			FreeMenus(PanelMenu);
		}
	}

	FreeGadgets(GadgetList);

	return(Flags);
}

	/* xpr_unlink(UBYTE *FileName):
	 *
	 *	Remove (delete) a given file.
	 */

LONG __saveds __asm
xpr_unlink(register __a0 UBYTE *FileName)
{
	LONG Success = DeleteFile(FileName) ? 0 : -1;

	if(Success)
		LogAction("Delete file \"%s\".",FileName);

	return(Success);
}

	/* xpr_squery():
	 *
	 *	Check how many characters are present in the serial buffer.
	 */

LONG __saveds
xpr_squery()
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

		if(!DoIO(WriteRequest))
			return((LONG)WriteRequest -> IOSer . io_Actual);
	}

	return(-1);
}

	/* xpr_getptr(LONG InfoType):
	 *
	 *	Return a pointer to the term custom screen.
	 */

LONG __saveds __asm
xpr_getptr(register __d0 LONG InfoType)
{
	if(InfoType == 1)
		return((LONG)Screen);
	else
		return(-1);
}

	/* xpr_stealopts(UBYTE *Prompt,UBYTE *Buffer):
	 *
	 *	Steal the contents of the options buffer (replacement
	 *	for xpr_gets).
	 */

LONG __saveds __asm
xpr_stealopts(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer)
{
	if(Buffer)
		strcpy(ProtocolOptsBuffer,Buffer);

	return(1);
}

	/* ProtocolSetup():
	 *
	 *	Set up the library and options for the external protocol.
	 */

BYTE
ProtocolSetup()
{
	UBYTE NameBuffer[40],TestBuffer[40],i;

		/* Close the old library if still open. */

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);
	}

		/* Clear the XPR interface buffer. */

	memset(XprIO,0,sizeof(struct XPR_IO));

		/* Copy the name of the library. */

	strcpy(NameBuffer,FilePart(LastXprLibrary));

		/* Extract the name itself (strip the `.library'). */

	for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
	{
		if(NameBuffer[i] == '.')
		{
			NameBuffer[i] = 0;
			break;
		}
	}

		/* Copy the result to the test buffer and check
		 * if the transfer protocol is a sort of ZModem.
		 */

	strcpy(TestBuffer,&NameBuffer[3]);

	TestBuffer[6] = 0;

	if(!Stricmp(TestBuffer,"zmodem"))
		UsesZModem = TRUE;
	else
		UsesZModem = FALSE;

		/* Obtain the protocol default settings. */

	if(!GetEnvDOS(NameBuffer,ProtocolOptsBuffer))
		ProtocolOptsBuffer[0] = 0;

		/* Initialize the interface structure. */

	XprIO -> xpr_filename	= ProtocolOptsBuffer;
	XprIO -> xpr_fopen	= (APTR)xpr_fopen;
	XprIO -> xpr_fclose	= (APTR)xpr_fclose;
	XprIO -> xpr_fread	= (APTR)xpr_fread;
	XprIO -> xpr_fwrite	= (APTR)xpr_fwrite;
	XprIO -> xpr_sread	= (APTR)xpr_sread;
	XprIO -> xpr_swrite	= (APTR)xpr_swrite;
	XprIO -> xpr_sflush	= (APTR)xpr_sflush;
	XprIO -> xpr_update	= (APTR)xpr_update;
	XprIO -> xpr_chkabort	= (APTR)xpr_chkabort;
	XprIO -> xpr_gets	= (APTR)xpr_gets;
	XprIO -> xpr_setserial	= (APTR)xpr_setserial;
	XprIO -> xpr_ffirst	= (APTR)xpr_ffirst;
	XprIO -> xpr_fnext	= (APTR)xpr_fnext;
	XprIO -> xpr_finfo	= (APTR)xpr_finfo;
	XprIO -> xpr_fseek	= (APTR)xpr_fseek;
	XprIO -> xpr_extension	= 4;
	XprIO -> xpr_options	= (APTR)xpr_options;
	XprIO -> xpr_unlink	= (APTR)xpr_unlink;
	XprIO -> xpr_squery	= (APTR)xpr_squery;
	XprIO -> xpr_getptr	= (APTR)xpr_getptr;

		/* Try to open the library. */

	if(XProtocolBase = (struct Library *)OpenLibrary(LastXprLibrary,0))
	{
			/* Set up the library. */

		TransferBits = XProtocolSetup(XprIO);

			/* Successful initialization? */

		if(!(TransferBits & XPRS_SUCCESS))
		{
			MyEasyRequest(Window,"Failed to set up protocol\n\"%s\"!","Continue",LastXprLibrary);

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			LastXprLibrary[0] = 0;

			TransferBits = 0;

			return(FALSE);
		}
	}
	else
		MyEasyRequest(Window,"Failed to open protocol\n\"%s\"!","Continue",LastXprLibrary);

	return(TRUE);
}

	/* SaveProtocolOpts():
	 *
	 *	Save the current protocol settings to an environment variable.
	 */

VOID
SaveProtocolOpts()
{
		/* It's time to save the altered options. */

	if(NewOptions && XProtocolBase)
	{
		UBYTE NameBuffer[40],i;

			/* Strip the `.library' part. */

		strcpy(NameBuffer,FilePart(LastXprLibrary));

		for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
		{
			if(NameBuffer[i] == '.')
			{
				NameBuffer[i] = 0;
				break;
			}
		}

			/* Cause the xpr.library to prompt for
			 * input. We expect the library to fill
			 * the prompt string with the default
			 * settings. The resulting string is
			 * intercepted by xpr_stealopts, saved
			 * to an environment variable and will
			 * serve as a reinitialization string
			 * later.
			 */

		XprIO -> xpr_filename	= NULL;
		XprIO -> xpr_gets	= (APTR)xpr_stealopts;
		XprIO -> xpr_extension	= 4;
		XprIO -> xpr_options	= (APTR)NULL;

		XProtocolSetup(XprIO);

			/* Save the options in case anything goes
			 * wrong.
			 */

		NewOptions = FALSE;

		SetEnvDOS(NameBuffer,ProtocolOptsBuffer);

			/* Reinitialize the library. */

		XprIO -> xpr_filename	= ProtocolOptsBuffer;
		XprIO -> xpr_gets	= (APTR)xpr_gets;
		XprIO -> xpr_extension	= 4;
		XprIO -> xpr_options	= (APTR)xpr_options;

		XProtocolSetup(XprIO);
	}
}

	/* SelectProtocol(UBYTE *Name,struct Window *ParentWindow):
	 *
	 *	Select a different transfer protocol library using
	 *	the asl.library file requester.
	 */

BYTE
SelectProtocol(UBYTE *Name,struct Window *ParentWindow)
{
	struct FileRequester	*AslFileRequest;
	UBYTE			*File;
	BYTE			 UseNewLibrary = FALSE;

	File = Name;

	if(FilePart(File) == File)
		strcpy(SharedBuffer,"LIBS:");
	else
	{
		UBYTE *Temp;

		strcpy(SharedBuffer,File);

		Temp = PathPart(SharedBuffer);

		Temp[0] = 0;

		File = FilePart(File);
	}

	if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
		ASL_Window,	ParentWindow,
		ASL_File,	File,
		ASL_Dir,	SharedBuffer,
		ASL_Hail,	"Select Transfer Protocol",
		ASL_FuncFlags,	0,
		ASL_Pattern,	"xpr#?.library",
		ASL_OKText,	"Select",
	TAG_END))
	{
		if(AslRequestTags(AslFileRequest,TAG_DONE))
		{
			if(AslFileRequest -> rf_File[0])
			{
				strcpy(SharedBuffer,AslFileRequest -> rf_Dir);

				if(!AddPart(SharedBuffer,AslFileRequest -> rf_File,256))
					strcpy(SharedBuffer,AslFileRequest -> rf_File);

				if(Stricmp(SharedBuffer,Name))
				{
					strcpy(LastXprLibrary,SharedBuffer);

					UseNewLibrary = TRUE;
				}
			}
		}

		FreeAslRequest(AslFileRequest);
	}

	return(UseNewLibrary);
}
