/*
**	$Id: termXPR.c,v 1.7 92/05/01 12:51:43 olsen Sta Locker: olsen $
**	$Revision: 1.7 $
**	$Date: 92/05/01 12:51:43 $
**
**	External transfer protocol support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

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

#define OPTION_WRAP 16

	/* 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 transfer path' feature is enabled.
	 */

STATIC UBYTE		 RealName[256];

STATIC struct Buffer	*CurrentFile;

	/* Yet another flag, this one determines whether we already told
	 * the user that the file we are currently processing will fit
	 * on the destination device.
	 */

STATIC BYTE		 Alerted;

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

	Alerted = FALSE;

		/* 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(Config . OverridePath)
	{
		if(!Uploading)
		{
			if(!DownloadPath)
			{
				if(!Config . BinaryDownloadPath[0])
				{
					if(!GetCurrentDirName(RealName,256))
						RealName[0] = 0;
				}
				else
					strcpy(RealName,Config . BinaryDownloadPath);
			}
			else
			{
				if(!DownloadPath[0])
				{
					if(!GetCurrentDirName(RealName,256))
						RealName[0] = 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(LocaleString(MSG_TERMXPR_LOGMSG_SEND_FILE_TXT),FileName);
					break;

			case 'w':	LogAction(LocaleString(MSG_TERMXPR_LOGMSG_RECEIVE_FILE_TXT),FileName);
					AddDownloadObject(FileName);
					break;

			case 'a':	LogAction(LocaleString(MSG_TERMXPR_LOGMSG_UPDATE_FILE_TXT),FileName);
					break;
		}

		CurrentFile = File;
	}

	DidTransfer = TRUE;

	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)))
			{
				AddTransferInfo(LocaleString(MSG_TERMXPR_FILE_REMOVED_TXT),FilePart(RealName));

					/* Delete empty file. */

				if(DeleteFile(RealName))
					LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_REMOVED_TXT),RealName);
				else
					LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_TXT),RealName);
			}
			else
			{
				if(ByteMax)
				{
					if(Size == ByteMax)
						AddTransferInfo(LocaleString(MSG_TERMXPR_FILE_RECEIVED_TXT),FilePart(RealName));
					else
						AddTransferInfo(LocaleString(MSG_TERMXPR_INCOMPLETE_FILE_RECEIVED_TXT),FilePart(RealName));
				}
					/* Try to identify the file type. */

				Identify(RealName);

				LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_BYTES_TXT),RealName,Size);
			}
		}
		else
		{
				/* Set the archived bit on files we uploaded? */

			if(Config . SetArchivedBit)
			{
				BPTR FileLock;

					/* Get a lock on it. */

				if(FileLock = Lock(RealName,ACCESS_READ))
				{
					struct FileInfoBlock __aligned FileInfo;

						/* Examine the file. */

					if(Examine(FileLock,&FileInfo))
					{
							/* Remove the lock. */

						UnLock(FileLock);

							/* Set the `archived' bit. */

						SetProtection(RealName,FileInfo . fib_Protection | FIBF_ARCHIVE);
					}
					else
						UnLock(FileLock);
				}
			}

			AddTransferInfo(LocaleString(MSG_TERMXPR_FILE_SENT_TXT),FilePart(RealName));

			LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_TXT),RealName);
		}
	}

	RealName[0] = 0;

	CurrentFile = NULL;

	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)
		{
				/* Are there any bytes in the buffer? */

			if(WriteRequest -> IOSer . io_Actual > 0)
			{
					/* More bytes available than requested? */

				if(Size < WriteRequest -> IOSer . io_Actual)
					ReadRequest -> IOSer . io_Length = Size;
				else
					ReadRequest -> IOSer . io_Length = WriteRequest -> IOSer . io_Actual;

					/* Fill the buffer. */

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

				if(!DoIO(ReadRequest))
					return(ReadRequest -> IOSer . io_Actual);
			}
		}
		else
		{
			if(WriteRequest -> IOSer . io_Actual < Size)
			{
				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,SIG_SERIAL | SIG_TIMER);

					/* Start IO... */

				SendIO(ReadRequest);
				SendIO(TimeRequest);

				FOREVER
				{
						/* Build signal mask. */

					if(TransferWindow)
						SignalSet = SIG_SERIAL | SIG_TIMER | (1 << TransferWindow -> UserPort -> mp_SigBit);
					else
						SignalSet = SIG_SERIAL | SIG_TIMER;

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

					SignalSet = Wait(SignalSet);

						/* Hit by timeout? */

					if(SignalSet & SIG_TIMER)
					{
							/* Abort the read request. */

						AbortIO(ReadRequest);
						WaitIO(ReadRequest);

							/* Remove the timer request. */

						WaitIO(TimeRequest);

							/* Did the driver receive any
							 * data?
							 */

						if(!ReadRequest -> IOSer . io_Actual)
						{
								/* Take a second look and query the number of
								 * bytes ready to be received, there may
								 * still be some bytes in the buffer.
								 * Note: this depends on the way the
								 * driver handles read abort.
								 */

							WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

							DoIO(WriteRequest);

								/* Are there any bytes to transfer? */

							if(WriteRequest -> IOSer . io_Actual > 0)
							{
									/* Don't read more than actually wanted. */

								if(Size < WriteRequest -> IOSer . io_Actual)
									ReadRequest -> IOSer . io_Length = Size;
								else
									ReadRequest -> IOSer . io_Length = WriteRequest -> IOSer . io_Actual;

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

									/* Read the data. */

								if(!DoIO(ReadRequest))
									return(ReadRequest -> IOSer . io_Actual);
								else
									return(0);
							}
							else
								return(0);
						}
						else
							return(ReadRequest -> IOSer . io_Actual);
					}

						/* Receive buffer filled? */

					if(SignalSet & SIG_SERIAL)
					{
						AbortIO(TimeRequest);
						WaitIO(TimeRequest);

						WaitIO(ReadRequest);

						if(!ReadRequest -> IOSer . io_Error)
							return(ReadRequest -> IOSer . io_Actual);
						else
							return(0);
					}

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

								SendAbort = TRUE;

								return(-1);
							}
						}
					}
				}
			}
			else
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= Buffer;
				ReadRequest -> IOSer . io_Length	= Size;

				if(!DoIO(ReadRequest))
					return(Size);
			}
		}
	}

	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)
{
	WORD Len = strlen(FileName);

	if(Len > 48)
	{
		WORD i;

		for(i = Len - 48 ; i < Len ; i++)
		{
			if(i >= Len - 44 && FileName[i] == '/')
			{
				STATIC UBYTE NameBuffer[256];

				strcpy(NameBuffer,".../");

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

				return(NameBuffer);
			}
		}
	}

	return(FileName);
}

	/* CalculateBlocks(LONG Size,LONG BlockSize):
	 *
	 *	Calculate the number of blocks a file will
	 *	occupy if saved to a disk.
	 */

STATIC LONG __regargs
CalculateBlocks(LONG Size,LONG BlockSize)
{
	LONG	Blocks		= 1;		/* One for the file header. */
	BYTE	HasExtension	= FALSE,	/* No extension block yet. */
		Extension	= 0;		/* How many block pointers yet. */

		/* Round to next block. */

	Size = ((Size + BlockSize - 1) / BlockSize) * BlockSize;

	while(Size)
	{
			/* Add another block. */

		Blocks++;

			/* Subtract another block. */

		Size -= BlockSize;

			/* Add another block pointer, if 72 have been
			 * added, add another extension block.
			 */

		if((Extension++) == 72)
		{
				/* If no extension block has been generated
				 * yet, we were running on the block pointers
				 * of the file header itself.
				 */

			if(!HasExtension)
				HasExtension = TRUE;
			else
				Blocks++;

				/* Reset extension block counter. */

			Extension = 0;
		}
	}

	return(Blocks);
}

	/* 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(LocaleString(MSG_TERMXPR_LOGMSG_INITIATE_BINARY_DOWNLOAD_TXT));

		if(!TransferPanel(LocaleString(MSG_TERMXPR_RECEIVE_FILES_TXT)))
		{
			ReleaseWindows();

			return(0);
		}
	}

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

		if((UpdateInfo -> xpru_updatemask & XPRU_PROTOCOL) && UpdateInfo -> xpru_protocol)
		{
			GT_SetGadgetAttrs(TransferGadgetArray[4],TransferWindow,NULL,
				GTTX_Text,	UpdateInfo -> xpru_protocol,
			TAG_DONE);
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_MSG) && UpdateInfo -> xpru_msg)
		{
			GT_SetGadgetAttrs(TransferGadgetArray[5],TransferWindow,NULL,
				GTTX_Text,	UpdateInfo -> xpru_msg,
			TAG_DONE);
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_ERRORMSG) && UpdateInfo -> xpru_errormsg)
			AddTransferInfo(UpdateInfo -> xpru_errormsg);

		if((UpdateInfo -> xpru_updatemask & XPRU_FILENAME) && UpdateInfo -> xpru_filename)
		{
			if(Config . OverridePath && !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(19, 0,"%-48.48s",TruncateName(RealName));
			}
			else
				TransferInfo(19, 0,"%-48.48s",TruncateName(UpdateInfo -> xpru_filename));
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_FILESIZE) && UpdateInfo -> xpru_filesize != -1)
		{
			if(CurrentFile -> DirLock && CurrentFile -> InfoData . id_NumBlocks && CurrentFile -> InfoData . id_BytesPerBlock)
				TransferInfo(19, 1,ConvNumber10,UpdateInfo -> xpru_filesize);
			else
				TransferInfo(19, 1,ConvNumber,UpdateInfo -> xpru_filesize);

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

		if((UpdateInfo -> xpru_updatemask & XPRU_BYTES) && UpdateInfo -> xpru_bytes != -1)
		{
			TransferInfo(19, 4,ConvNumber,ByteVal = UpdateInfo -> xpru_bytes);

			if(ByteMax)
				NewByte = TRUE;

			if(CurrentFile -> DirLock && !Uploading)
			{
				if(CurrentFile -> InfoData . id_NumBlocks && CurrentFile -> InfoData . id_BytesPerBlock)
				{
					if(ByteMax)
					{
						register LONG Blocks = CalculateBlocks(ByteMax,CurrentFile -> InfoData . id_BytesPerBlock),Space = CurrentFile -> InfoData . id_NumBlocks - CurrentFile -> InfoData . id_NumBlocksUsed;

						TransferInfo(19, 2,LocaleString(MSG_TERMXPR_BYTES_FULL_TXT),Space * CurrentFile -> InfoData . id_BytesPerBlock,100 * (CurrentFile -> InfoData . id_NumBlocksUsed) / CurrentFile -> InfoData . id_NumBlocks,(Space < Blocks) ? LocaleString(MSG_TERMXPR_FILE_MAY_NOT_FIT_TXT) : "");

							/* Tell the user that he might be
							 * running into trouble.
							 */

						if(!Alerted && (Space < Blocks))
						{
							Alerted = TRUE;

							BumpWindow(TransferWindow);

							Say(LocaleString(MSG_TERMXPR_SAY_FILE_MAY_NOT_FIT_TXT));
						}
					}
					else
						TransferInfo(19, 2,LocaleString(MSG_TERMXPR_BYTES_FULL_TXT),(CurrentFile -> InfoData . id_NumBlocks - CurrentFile -> InfoData . id_NumBlocksUsed) * CurrentFile -> InfoData . id_BytesPerBlock,100 * (CurrentFile -> InfoData . id_NumBlocksUsed) / CurrentFile -> InfoData . id_NumBlocks);
				}
			}
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKS) && UpdateInfo -> xpru_blocks != -1)
			TransferInfo(19, 5,ConvNumber,UpdateInfo -> xpru_blocks);

		if((UpdateInfo -> xpru_updatemask & XPRU_BLOCKSIZE) && UpdateInfo -> xpru_blocksize != -1)
			TransferInfo(19, 6,ConvNumber,UpdateInfo -> xpru_blocksize);

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

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

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

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

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

			if(TimeMax)
				NewTime = TRUE;
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_DATARATE) && UpdateInfo -> xpru_datarate != -1)
			TransferInfo(54, 4,ConvNumber,UpdateInfo -> xpru_datarate);

		if((UpdateInfo -> xpru_updatemask & XPRU_CHARDELAY) && UpdateInfo -> xpru_chardelay != -1)
			TransferInfo(54, 5,ConvNumber,UpdateInfo -> xpru_chardelay);

		if((UpdateInfo -> xpru_updatemask & XPRU_PACKETDELAY) && UpdateInfo -> xpru_packetdelay != -1)
			TransferInfo(54, 6,ConvNumber,UpdateInfo -> xpru_packetdelay);

		if((UpdateInfo -> xpru_updatemask & XPRU_PACKETTYPE) && UpdateInfo -> xpru_packettype != -1)
		{
			if(UpdateInfo -> xpru_packettype > 31 && UpdateInfo -> xpru_packettype < 256)
			{
				if(ValidTab[UpdateInfo -> xpru_packettype])
					TransferInfo(54, 7,"`%lc'",UpdateInfo -> xpru_packettype);
				else
					TransferInfo(54, 7,ConvNumber,UpdateInfo -> xpru_packettype);
			}
			else
				TransferInfo(54, 7,ConvNumber,UpdateInfo -> xpru_packettype);
		}

		if((UpdateInfo -> xpru_updatemask & XPRU_ERRORS) && UpdateInfo -> xpru_errors != -1)
			TransferInfo(54, 9,ConvNumber,UpdateInfo -> xpru_errors);

		if((UpdateInfo -> xpru_updatemask & XPRU_TIMEOUTS) && UpdateInfo -> xpru_timeouts != -1)
			TransferInfo(54,10,ConvNumber,UpdateInfo -> xpru_timeouts);

		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) % 60,TimeDif % 60);
			}
		}
	}

	return(1);
}

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

LONG __saveds
xpr_chkabort()
{
	LONG Result = 0;

	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(!Result)
			{
				if(Class == IDCMP_VANILLAKEY)
					KeySelect(TransferGadgetArray,1,Code,TransferWindow,&Gadget,&Class,&Code);

				if(Class == IDCMP_NEWSIZE)
				{
					if(!(TransferWindow -> Flags & WFLG_ZOOMED))
						RefreshTransferWindow();
				}

				if(Class == IDCMP_CLOSEWINDOW)
				{
					LogAction(LocaleString(MSG_TERMXPR_LOGMSG_TRANSFER_ABORTED_TXT));

					TransferAborted = TRUE;

					Result = -1;
				}

				if(Class == IDCMP_GADGETUP)
				{
					switch(Gadget -> GadgetID)
					{
						case 0:	LogAction(LocaleString(MSG_TERMXPR_LOGMSG_TRANSFER_ABORTED_TXT));

							TransferAborted = TRUE;

							Result = -1;
							break;

						case 1:	LogAction(LocaleString(MSG_TERMXPR_LOGMSG_FILE_SKIPPED_TXT));

							Result = 1;
							break;

						default:break;
					}
				}
			}
		}
	}

	return(Result);
}

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

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

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

		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,	255,
			GTST_String,	String,
			GTST_EditHook,	&CommandHook,
			GA_Immediate,	TRUE,
		TAG_DONE);

		if(Gadget)
			*WindowWidth = Gadget -> Width + 32;
		else
			*WindowWidth = 200;

		NewGadget . ng_Width		= 88;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= LocaleString(MSG_TERMXPR_OKAY_GAD);
		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		= 88;
			NewGadget . ng_GadgetText	= LocaleString(MSG_TERMXPR_LOAD_FILE_GAD);
			NewGadget . ng_GadgetID		= Counter;
			NewGadget . ng_LeftEdge		= (*WindowWidth - NewGadget . ng_Width) >> 1;

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

		NewGadget . ng_Width		= 88;
		NewGadget . ng_GadgetText	= LocaleString(MSG_GLOBAL_CANCEL_GAD);
		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);
	}

	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;
	LONG		 Width,Success = FALSE;

	if(!Prompt)
		Prompt = LocaleString(MSG_TERMXPR_INPUT_REQUIRED_TXT);

	if(CreateAllGetsGadgets(FALSE,Buffer,Prompt,&Width,&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1,Screen))
	{
		if(PanelWindow = OpenWindowTags(NULL,
			WA_Width,	Width,
			WA_Height,	58,

			WA_Left,	(Screen -> Width - Width) >> 1,
			WA_Top,		(Screen -> Height - 56) >> 1,

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_DepthGadget,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_CustomScreen,Screen,

			WA_IDCMP,	IDCMP_GADGETDOWN | IDCMP_ACTIVEWINDOW | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | BUTTONIDCMP | STRINGIDCMP,

			WA_Title,	LocaleString(MSG_GLOBAL_ENTER_TEXT_TXT),
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			PushWindow(PanelWindow);

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

			ActiveGadget = NULL;

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

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

					GT_ReplyIMsg(Massage);

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

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

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

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

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

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

									Success = TRUE;

									Terminated = TRUE;
									break;
								}

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

								Success = TRUE;

								Terminated = TRUE;
								break;

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

			ActiveGadget = NULL;

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

			PopWindow();

			CloseWindow(PanelWindow);
		}

		FreeGadgets(GadgetList);
	}

	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(Config . OverridePath && !Uploading)
			{
				if(!DownloadPath)
				{
					if(!Config . BinaryDownloadPath[0])
					{
						if(!GetCurrentDirName(RealName,256))
							RealName[0] = 0;
					}
					else
						strcpy(RealName,Config . BinaryDownloadPath);
				}
				else
				{
					if(!DownloadPath[0])
					{
						if(!GetCurrentDirName(RealName,256))
							RealName[0] = 0;
					}
					else
						strcpy(RealName,DownloadPath);
				}

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

			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,
						GA_Immediate,	TRUE,
					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 - 1,
						GTST_EditHook,	&CommandHook,
						GA_Immediate,	TRUE,
					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;
	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(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 | IDCMP_VANILLAKEY | CHECKBOXIDCMP | STRINGIDCMP | INTEGERIDCMP | BUTTONIDCMP,

			WA_Title,	OptionTitle ? OptionTitle : LocaleString(MSG_TERMXPR_TRANSFER_OPTIONS_TXT),
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			PushWindow(PanelWindow);

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

			ActiveGadget = NULL;

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

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

					GT_ReplyIMsg(Massage);

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

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

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

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

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

								Terminated = TRUE;
							}
						}
					}

					if(Terminated)
					{
						if(!OptionTitle)
							NewOptions = FALSE;

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

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

												if(!OptionTitle)
													NewOptions = TRUE;
											}
											break;

								case XPRO_COMMPAR:
								case XPRO_LONG:
								case XPRO_STRING:	if(strcmp(Opts[i] -> xpro_value,GT_STRING(GadgetArray[i])))
											{
												Flags |= (1 << i);

												strcpy(Opts[i] -> xpro_value,GT_STRING(GadgetArray[i]));

												if(!OptionTitle)
													NewOptions = TRUE;
											}
											break;

								default:		break;
							}
						}
					}
				}
			}

			ActiveGadget = NULL;

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

			PopWindow();

			CloseWindow(PanelWindow);
		}
	}

	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(LocaleString(MSG_TERMXPR_LOGMSG_DELETE_FILE_TXT),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],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;
		}
	}

		/* Check if the transfer protocol is a sort of ZModem. */

	UsesZModem = FALSE;

	for(i = 0 ; i <= strlen(NameBuffer) - 6 ; i++)
	{
		if(!Stricmp(&NameBuffer[i],"zmodem"))
			UsesZModem = TRUE;
	}

		/* 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,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LastXprLibrary);

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			LastXprLibrary[0] = 0;

			TransferBits = 0;

			return(FALSE);
		}

		if(TransferBits & XPRS_HOSTNOWAIT)
		{
			if(!HostReadBuffer)
				HostReadBuffer = AllocVec(Config . SerBuffSize,MEMF_ANY);
		}
		else
		{
			if(HostReadBuffer)
			{
				FreeVec(HostReadBuffer);

				HostReadBuffer = NULL;
			}
		}
	}
	else
		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),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,	LocaleString(MSG_TERMXPR_SELECT_TRANSFER_PROTOCOL_TXT),
		ASL_FuncFlags,	FILF_NEWIDCMP,
		ASL_Pattern,	"xpr#?.library",
		ASL_OKText,	LocaleString(MSG_GLOBAL_SELECT_TXT),
	TAG_END))
	{
		if(AslRequestTags(AslFileRequest,TAG_DONE))
		{
			if(AslFileRequest -> rf_File[0])
			{
				if(Stricmp("Libs:",AslFileRequest -> rf_Dir))
				{
					strcpy(SharedBuffer,AslFileRequest -> rf_Dir);

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

				if(SharedBuffer[0] && Stricmp(SharedBuffer,Name))
				{
					strcpy(LastXprLibrary,SharedBuffer);

					UseNewLibrary = TRUE;
				}
			}
		}

		FreeAslRequest(AslFileRequest);
	}

	return(UseNewLibrary);
}
