#include <exec/exec.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <stdio.h>
#include <stdlib.h>
#include <devices/serial.h>
#include <intuition/intuition.h>
#include <dos/dosasl.h>
#include "bbs.h"
#include "xproto.h"
#define MILLION 1000000L

extern struct IOExtSer *WriteSerReq;
extern struct IOExtSer *ReadSerReq;
struct XPR_IO *XprIO;
struct Library *XProtocolBase;
extern struct MsgPort *ReadSerPort;
extern struct MsgPort *WriteSerPort;
extern struct timerequest	*TimerMsg;
extern struct Window		*ZmodemStats;
extern struct Library *IntuitionBase;
LONG __saveds __asm		xpr_fopen(register __a0 UBYTE *FileName,register __a1 UBYTE *AccessMode);
LONG __saveds __asm		xpr_fclose(register __a0 struct Buffer *File);
LONG __saveds __asm		xpr_fread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File);
LONG __saveds __asm		xpr_fwrite(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct Buffer *File);
ULONG __saveds __asm		xpr_sread(register __a0 UBYTE *Buffer,register __d0 ULONG Size,register __d1 LONG Timeout);
LONG __saveds __asm		xpr_swrite(register __a0 UBYTE *Buffer,register __d0 LONG Size);
LONG __saveds			xpr_sflush(VOID);
LONG __saveds __asm		xpr_update(register __a0 struct XPR_UPDATE *UpdateInfo);
LONG __saveds			xpr_chkabort(VOID);
LONG __saveds __asm		xpr_gets(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer);
LONG __saveds __asm		xpr_setserial(register __d0 LONG Status);
LONG __saveds __asm		xpr_ffirst(register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern);
LONG __saveds __asm		xpr_fnext(register __d0 LONG OldState,register __a0 UBYTE *Buffer,register __a1 UBYTE *Pattern);
LONG __saveds __asm		xpr_finfo(register __a0 UBYTE *FileName,register __d0 LONG InfoType);
LONG __saveds __asm		xpr_fseek(register __a0 struct Buffer *File,register __d0 LONG Offset,register __d1 LONG Origin);
ULONG __saveds __asm		xpr_options(register __d0 LONG NumOpts,register __a0 struct xpr_option **Opts);
LONG __saveds __asm		xpr_unlink(register __a0 UBYTE *FileName);
LONG __saveds			xpr_squery(VOID);
LONG __saveds __asm		xpr_getptr(register __d0 LONG InfoType);
LONG __saveds __asm		xpr_stealopts(register __a0 UBYTE *Prompt,register __a1 UBYTE *Buffer);

VOID __stdargs			TransferInfo(WORD X,WORD Y,BYTE *String,...);
VOID __stdargs			ShowString(struct Gadget *Gadget,UBYTE *String,...);
VOID				ShowStats(struct Gadget *Gadget,LONG Value,LONG Max);


BYTE				ProtocolSetup(VOID);
VOID				SaveProtocolOpts(VOID);

STATIC UBYTE		 LineOffsets[] = {  0, 8,16, 0,30,38,46,54, 0,67,75 };

#define WIDTH		564
#define HEIGHT		198

#define ORIGIN_X	10
#define ORIGIN_Y	(14+50)

char LastXprLibrary[]= "xprzmodem.library";
static BOOL UsesZModem;
static BOOL UseNewLibrary;
static BOOL Alerted;
static BOOL DidTransfer;
static BOOL Uploading;
static BOOL SendAbort;
static BOOL MultipleFiles;
static BOOL TransferAborted;
static BOOL BinaryTransfer;
static BYTE FileMatch;
static UBYTE			 SharedBuffer[512];
static char *FileArg[100];
static int FileCount,FileCountMax;
static int TransferBits;
static int ByteVal,ByteMax,TimeVal,TimeMax;
static struct Buffer *CurrentFile;
static UBYTE			 ConvNumber[30],ConvNumber10[30];
static struct Gadget		*TransferGadgetArray[8];
static struct AnchorPath	*FileAnchor;
static BYTE			 ValidTab[256];
static char RealName[200];

ULONG __saveds __asm
xpr_options(register __d0 LONG NumOpts,register __a0 struct xpr_option **Opts)
{
	ULONG		 Flags = 0;

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

	return(Success);
}
LONG __saveds
xpr_squery()
{
	if(WriteSerReq)
	{
		WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;

		if(!DoIO(WriteSerReq))
			return((LONG)WriteSerReq -> 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)
{
     char temp[100];
     gu(temp,RAWSCREEN_ADDRESS);
	if(InfoType == 1)
		return((LONG)atol(temp));
	else
		return(-1);
}
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	= NULL;
	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? */

	}

	return(TRUE);
}
BYTE
SelectProtocol(void)
{
	strcpy(LastXprLibrary,"xprzmodem.library");
	UseNewLibrary = TRUE;

	return(TRUE);
}
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;

		/* Remember file name. */

	strcpy(RealName,FileName);

		/* Build a new filename if neccessary. */

		if(!Uploading)
		{
			strcpy(RealName,"RAM:");
			if(AddPart(RealName,FilePart(FileName),256))
				FileName = RealName;
			else
				strcpy(RealName,FileName);
		}

		/* Determine file transfer mode... */

	if(File = BufferOpen(FileName,AccessMode))
	{
		CurrentFile = File;
	}

	DidTransfer = TRUE;

	return((LONG)File);
}
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. */

				DeleteFile(RealName);
			}
			else
			{
					/* Try to identify the file type. */

				Identify(RealName);

			}
		}
		else
		{
				/* Set the archived bit on files we uploaded? */

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

		}
	}

	RealName[0] = 0;

	CurrentFile = NULL;

	return(1);
}
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(WriteSerReq && Size)
	{
			/* How many bytes are still in the serial buffer? */

		WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;

		DoIO(WriteSerReq);

			/* 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 = WriteSerReq -> IOSer . io_Actual)
			{
					/* More bytes available than we
					 * were requested?
					 */

				if(Length > Size)
					Length = Size;

					/* Fill the buffer. */

				ReadSerReq -> IOSer . io_Command	= CMD_READ;
				ReadSerReq -> IOSer . io_Data		= Buffer;
				ReadSerReq -> IOSer . io_Length	= Length;
				ReadSerReq -> IOSer . io_Flags		= IOF_QUICK;

				if(DoIO(ReadSerReq))
					Length = 0;
			}

				/* 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(WriteSerReq -> IOSer . io_Actual >= Size)
			{
				ReadSerReq -> IOSer . io_Command	= CMD_READ;
				ReadSerReq -> IOSer . io_Data		= Buffer;
				ReadSerReq -> IOSer . io_Length	= Size;
				ReadSerReq -> IOSer . io_Flags		= IOF_QUICK;

				if(!DoIO(ReadSerReq))
					return(Size);
				else
					return(0);
			}
			else
			{
				register ULONG SignalSet;

					/* Set up the timer. */

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

					/* Set up the read request. */

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

					/* Prevent early termination. */

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

					/* Start IO... */

				SendIO(ReadSerReq);
				SendIO(TimerMsg);

				FOREVER
				{
						/* Build signal mask. */

					if(ZmodemStats)
						SignalSet = (1 << ReadSerPort -> mp_SigBit) | (1 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit) | (1 << ZmodemStats -> UserPort -> mp_SigBit);
					else
						SignalSet = (1 << ReadSerPort -> mp_SigBit) | (1 << TimerMsg -> 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 << TimerMsg -> tr_node . io_Message . mn_ReplyPort -> mp_SigBit))
					{
							/* Abort the read request. */

						AbortIO(ReadSerReq);
						WaitIO(ReadSerReq);

							/* Remove the timer request. */

						WaitIO(TimerMsg);

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

						if(!ReadSerReq -> 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.
								 */

							WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;

							DoIO(WriteSerReq);

								/* Are there any bytes to transfer? */

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

								if(WriteSerReq -> IOSer . io_Actual < Size)
									Size = WriteSerReq -> IOSer . io_Actual;

								ReadSerReq -> IOSer . io_Command	= CMD_READ;
								ReadSerReq -> IOSer . io_Data		= Buffer;
								ReadSerReq -> IOSer . io_Length	= Size;
								ReadSerReq -> IOSer . io_Flags		= IOF_QUICK;

									/* Read the data. */

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

						/* Receive buffer filled? */

					if(SignalSet & (1 << ReadSerPort -> mp_SigBit))
					{
						AbortIO(TimerMsg);
						WaitIO(TimerMsg);

						WaitIO(ReadSerReq);

						return(ReadSerReq -> IOSer . io_Actual);
					}

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

					if(ZmodemStats)
					{
						if(SignalSet & (1 << ZmodemStats -> UserPort -> mp_SigBit))
						{
							if(xpr_chkabort() == -1)
							{
								AbortIO(ReadSerReq);
								AbortIO(TimerMsg);

								WaitIO(ReadSerReq);
								WaitIO(TimerMsg);

								SendAbort = TRUE;

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

	return(0);
}
LONG __saveds __asm
xpr_swrite(register __a0 UBYTE *Buffer,register __d0 LONG Size)
{
	if(WriteSerReq)
	{
		WriteSerReq -> IOSer . io_Command	= CMD_WRITE;
		WriteSerReq -> IOSer . io_Data		= Buffer;
		WriteSerReq -> IOSer . io_Length	= Size;
		WriteSerReq -> IOSer . io_Flags	= IOF_QUICK;

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

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

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

		return((LONG)DoIO(WriteSerReq));
	}
	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);
}
LONG __saveds __asm
xpr_update(register __a0 struct XPR_UPDATE *UpdateInfo)
{
	if(UpdateInfo)
	{
		BYTE	NewByte = FALSE,
			NewTime = FALSE;


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

		if((UpdateInfo -> xpru_updatemask & XPRU_FILENAME) && UpdateInfo -> xpru_filename)
		{
			if(!Uploading)
			{
				if(!RealName[0])
				{
						strcpy(RealName,"RAM:");

					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(BYTES_FULL_TXT),Space * CurrentFile -> InfoData . id_BytesPerBlock,100 * (CurrentFile -> InfoData . id_NumBlocksUsed) / CurrentFile -> InfoData . id_NumBlocks,(Space < Blocks) ? LocaleString(FILE_MAY_NOT_FIT_TXT) : "");

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

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

							BumpWindow(ZmodemStats);

						}
					}
					else
						TransferInfo(19, 2,LocaleString(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(ZmodemStats)
		{
			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);
}
LONG __saveds
xpr_chkabort()
{
	LONG Result = 0;

	if(ZmodemStats)
	{
		struct IntuiMessage	*Massage;
		ULONG			 Class,Code;
		struct Gadget		*Gadget;

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

			GT_ReplyIMsg(Massage);

			if(!Result)
			{
					if(Class == IDCMP_CLOSEWINDOW)
					{

						TransferAborted = TRUE;

						Result = -1;
					}
				
			}
		}
	}

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

		LONG Return,i;

		WriteSerReq -> IOSer . io_Command = SDCMD_QUERY;

		DoIO(WriteSerReq);

		Return = WriteSerReq -> io_SerFlags & 0xFF;

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

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

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

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

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

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

				break;
			}
		}

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

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

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

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

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

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

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

			DoIO(WriteSerReq);

			ReadSerReq -> io_SerFlags	= WriteSerReq -> io_SerFlags;
			ReadSerReq -> io_ExtFlags	= WriteSerReq -> io_ExtFlags;

			ReadSerReq -> io_StopBits	= WriteSerReq -> io_StopBits;
			ReadSerReq -> io_ReadLen	= WriteSerReq -> io_ReadLen;
			ReadSerReq -> io_WriteLen	= WriteSerReq -> 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++]);

		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++]);

			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);
}
LONG __saveds __asm
xpr_finfo(register __a0 UBYTE *FileName,register __d0 LONG InfoType)
{
	BPTR FileLock;

	switch(InfoType)
	{
		case 1:	if(!Uploading)
			{
					strcpy(RealName,"RAM:");

				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);
}
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);
}
VOID __stdargs
TransferInfo(WORD X,WORD Y,BYTE *String,...)
{
	if(String && ZmodemStats)
	{
		va_list	VarArgs;

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

		if(!strncmp(String,"%10ld ",6))
		{
			strcat(SharedBuffer,"                                   ");

			SharedBuffer[47] = 0;
		}
		else
		{
			if(!Stricmp(String,"%ld") || !Stricmp(String,"%10ld") || !Stricmp(String,"`%lc'"))
			{
				strcat(SharedBuffer,"              ");

				SharedBuffer[11] = 0;
			}
		}

		Move(ZmodemStats -> RPort,ORIGIN_X + 4 + X * 8,ORIGIN_Y + 6 + LineOffsets[Y]);
		Text(ZmodemStats -> RPort,SharedBuffer,strlen(SharedBuffer));
	}
}

STATIC VOID __stdargs
TransferText(WORD Y,UBYTE *String,...)
{
	va_list	VarArgs;

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

	Move(ZmodemStats -> RPort,ORIGIN_X,ORIGIN_Y + 6 + LineOffsets[Y]);
	Text(ZmodemStats -> RPort,SharedBuffer,strlen(SharedBuffer));
}
VOID
ShowStats(struct Gadget *Gadget,LONG Value,LONG Max)
{
	if(ZmodemStats)
	{
		struct RastPort	*RPort = ZmodemStats -> RPort;
		LONG		 MaxWidth = Gadget -> Width - 4,Width;

		if(Max < 1)
			Max = 0;

		if(Value > Max)
			Value = Max;

		if((Width = (MaxWidth * Value) / Max) > 0)
		{
			BYTE FgPen = RPort -> FgPen;

			if(Width != MaxWidth)
			{
				SetAPen(RPort,0);

				RectFill(RPort,Gadget -> LeftEdge + 2 + Width - 1,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Gadget -> Width - 3,Gadget -> TopEdge + Gadget -> Height - 2);
			}

               SetAPen(RPort,7);

			RectFill(RPort,Gadget -> LeftEdge + 2,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Width + 1,Gadget -> TopEdge + Gadget -> Height - 2);

			SetAPen(RPort,FgPen);
		}
		else
		{
			BYTE FgPen = RPort -> FgPen;

			SetAPen(RPort,0);

			RectFill(RPort,Gadget -> LeftEdge + 2,Gadget -> TopEdge + 1,Gadget -> LeftEdge + Gadget -> Width - 3,Gadget -> TopEdge + Gadget -> Height - 2);

			SetAPen(RPort,FgPen);
		}
	}
}

VOID __stdargs
ShowString(struct Gadget *Gadget,UBYTE *String,...)
{
     BYTE FgPen,DrawMode;
	if(ZmodemStats)
	{
		va_list		 VarArgs;
		WORD		 Len;
		struct RastPort	*RPort = ZmodemStats -> RPort;

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

		Len = strlen(SharedBuffer);

		Move(RPort,Gadget -> LeftEdge + 2 + (Gadget -> Width - 4 - 8 * Len) / 2,Gadget -> TopEdge + 1 + (Gadget -> Height - 2 - 8) / 2 + 6);

			FgPen = RPort -> FgPen;DrawMode = RPort -> DrawMode;

               SetAPen(RPort,2);

			SetDrMd(RPort,JAM1);

			Text(RPort,SharedBuffer,Len);

			SetAPen(RPort,FgPen);
			SetDrMd(RPort,DrawMode);
		
	}
}
