/*
**	termTransfer.c
**
**	File transfer routines
**
**	Copyright © 1990-1995 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The action strings to display. */

STATIC STRPTR	SendQuery[3],
		ReceiveQuery[3],
		TransferTypes[3];

	/* CreateArgName(STRPTR String):
	 *
	 *	Create another node based upon a string given.
	 *	Encloses parameters in quotes if necessary and
	 *	`escapes' quotes.
	 */

STATIC struct Node * __regargs
CreateArgName(STRPTR String)
{
	struct Node	*Node;
	STRPTR		 Index = String;
	LONG		 Len,NewLen;
	BOOLEAN		 Quotes = FALSE;

	Len = NewLen = strlen(String);

	while(*Index)
	{
		if(*Index == '\"')
			NewLen++;

		if(*Index == ' ' && !Quotes)
		{
			NewLen += 2;

			Quotes = TRUE;
		}

		Index++;
	}

	if(!Len)
	{
		NewLen += 2;

		Quotes = TRUE;
	}

	if(NewLen > Len)
	{
		if(Node = (struct Node *)AllocVecPooled(sizeof(struct Node) + NewLen + 1,MEMF_ANY))
		{
			STRPTR Dest;

			Node -> ln_Name = (STRPTR)(Node + 1);

			Dest	= Node -> ln_Name;
			Index	= String;

			if(Quotes)
				*Dest++ = '\"';

			while(*Index)
			{
				if(*Index == '\"')
					*Dest++ = '*';

				*Dest++ = *Index++;
			}

			if(Quotes)
				*Dest++ = '\"';

			*Dest = 0;
		}
	}
	else
	{
		if(Node = (struct Node *)AllocVecPooled(sizeof(struct Node) + Len + 1,MEMF_ANY))
			strcpy(Node -> ln_Name = (STRPTR)(Node + 1),String);
	}

	return(Node);
}

	/* BuildString():
	 *
	 *	Build a command line based upon a template and
	 *	existing file transfer info.
	 */

STATIC STRPTR __regargs
BuildString(STRPTR Source,STRPTR From,STRPTR To,BYTE Type,BOOLEAN ReceiveMode,STRPTR SingleFile,struct FileTransferInfo *Info,LONG *Error)
{
	struct List	*List;
	STRPTR		 Result = NULL;

	if(Info)
	{
		if(Info -> FileList . mlh_Head -> mln_Succ -> mln_Succ)
			Info -> CurrentFile = (struct FileTransferNode *)Info -> FileList . mlh_Head;
		else
			Info = NULL;
	}

	*Error = 0;

	if(List = CreateList())
	{
		struct Node		*Node;
		UBYTE			 LocalBuffer[MAX_FILENAME_LENGTH],
					 TempBuffer[MAX_FILENAME_LENGTH];
		STRPTR			 Index = Source,
					 Dest = LocalBuffer;
		struct FileRequester	*FileRequest;

		while(*Index && !(*Error))
		{
			if(*Index == '%')
			{
				struct Node	*InsertHere = List -> lh_TailPred;
				BOOLEAN		 InsertBefore = TRUE;

				Index++;

				if(!(*Index))
				{
					*Dest = 0;

					if(LocalBuffer[0])
					{
						if(Node = CreateNode(LocalBuffer))
						{
							AddTail(List,Node);

							LocalBuffer[0] = 0;
						}
						else
							*Error = ERR_NO_MEM;
					}

					break;
				}

				switch(*Index)
				{
					case 'f':

						if(SingleFile)
						{
							if(Node = CreateArgName(SingleFile))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							SingleFile = NULL;

							break;
						}

						if(Info)
						{
							if(Node = CreateArgName(Info -> CurrentFile -> Name))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							if(Info -> CurrentFile -> Node . mln_Succ -> mln_Succ)
								Info -> CurrentFile = (struct FileTransferNode *)Info -> CurrentFile -> Node . mln_Succ;
							else
								Info = NULL;

							break;
						}

						LT_LockWindow(Window);

						if(ReceiveMode)
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT + Type),To,"",TempBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE);
						else
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_UPLOAD_FILE_TXT + Type),From,"",TempBuffer,NULL,FALSE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),FALSE);

						if(FileRequest)
						{
							if(Node = CreateArgName(TempBuffer))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							FreeAslRequest(FileRequest);
						}
						else
							*Error = ERR_ABORTED;

						LT_UnlockWindow(Window);

						break;

					case 'F':

						if(SingleFile)
						{
							if(Node = CreateArgName(FilePart(SingleFile)))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							SingleFile = NULL;

							break;
						}

						if(Info)
						{
							if(Node = CreateArgName(FilePart(Info -> CurrentFile -> Name)))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							if(Info -> CurrentFile -> Node . mln_Succ -> mln_Succ)
								Info -> CurrentFile = (struct FileTransferNode *)Info -> CurrentFile -> Node . mln_Succ;
							else
								Info = NULL;

							break;
						}

						LT_LockWindow(Window);

						if(ReceiveMode)
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT + Type),To,"",TempBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE);
						else
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_UPLOAD_FILE_TXT + Type),From,"",TempBuffer,NULL,FALSE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),FALSE);

						if(FileRequest)
						{
							if(Node = CreateArgName(FilePart(TempBuffer)))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							FreeAslRequest(FileRequest);
						}
						else
							*Error = ERR_ABORTED;

						LT_UnlockWindow(Window);

						break;

					case 'm':

						if(SingleFile)
						{
							if(Node = CreateArgName(SingleFile))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							SingleFile = NULL;

							break;
						}

						if(Info)
						{
							struct FileTransferNode *SomeNode = Info -> CurrentFile;

							while(SomeNode -> Node . mln_Succ && !(*Error))
							{
								if(Node = CreateArgName(SomeNode -> Name))
									AddTail(List,Node);
								else
									*Error = ERR_NO_MEM;

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

							Info = NULL;

							break;
						}

						LT_LockWindow(Window);

						TempBuffer[0] = 0;

						if(ReceiveMode)
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT + Type),To,"",TempBuffer,NULL,TRUE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE);
						else
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_UPLOAD_FILE_TXT + Type),From,"",TempBuffer,NULL,FALSE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),FALSE);

						LT_UnlockWindow(Window);

						if(FileRequest)
						{
							LONG i;

							if(TempBuffer[0] && FileRequest -> fr_NumArgs == 1)
							{
								if(Node = CreateArgName(TempBuffer))
								{
									AddTail(List,Node);

									break;
								}
								else
									*Error = ERR_NO_MEM;
							}

							for(i = 0 ; i < FileRequest -> fr_NumArgs && !(*Error) ; i++)
							{
								if(FileRequest -> fr_ArgList[i] . wa_Lock)
								{
									if(NameFromLock(FileRequest -> fr_ArgList[i] . wa_Lock,TempBuffer,MAX_FILENAME_LENGTH))
									{
										if(AddPart(TempBuffer,FileRequest -> fr_ArgList[i] . wa_Name,MAX_FILENAME_LENGTH))
										{
											if(Node = CreateArgName(TempBuffer))
												AddTail(List,Node);
											else
												*Error = ERR_NO_MEM;
										}
										else
											*Error = IoErr();
									}
									else
										*Error = IoErr();
								}
								else
								{
									strcpy(TempBuffer,FileRequest -> fr_Drawer);

									if(AddPart(TempBuffer,FileRequest -> fr_ArgList[i] . wa_Name,MAX_FILENAME_LENGTH))
									{
										if(Node = CreateArgName(TempBuffer))
											AddTail(List,Node);
										else
											*Error = ERR_NO_MEM;
									}
									else
										*Error = IoErr();
								}
							}

							FreeAslRequest(FileRequest);
						}
						else
							*Error = ERR_ABORTED;

						break;

					case 'M':

						if(SingleFile)
						{
							if(Node = CreateArgName(FilePart(SingleFile)))
								AddTail(List,Node);
							else
								*Error = ERR_NO_MEM;

							SingleFile = NULL;

							break;
						}

						if(Info)
						{
							struct FileTransferNode *SomeNode = Info -> CurrentFile;

							while(SomeNode -> Node . mln_Succ && !(*Error))
							{
								if(Node = CreateArgName(FilePart(SomeNode -> Name)))
									AddTail(List,Node);
								else
									*Error = ERR_NO_MEM;

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

							Info = NULL;

							break;
						}

						LT_LockWindow(Window);

						TempBuffer[0] = 0;

						if(ReceiveMode)
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT + Type),To,"",TempBuffer,NULL,TRUE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE);
						else
							FileRequest = GetFile(Window,LocaleString(MSG_TERMTRANSFER_UPLOAD_FILE_TXT + Type),From,"",TempBuffer,NULL,FALSE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),FALSE);

						LT_UnlockWindow(Window);

						if(FileRequest)
						{
							LONG i;

							if(TempBuffer[0] && FileRequest -> fr_NumArgs == 1)
							{
								if(Node = CreateArgName(FilePart(TempBuffer)))
								{
									AddTail(List,Node);

									break;
								}
								else
									*Error = ERR_NO_MEM;
							}

							for(i = 0 ; i < FileRequest -> fr_NumArgs && !(*Error) ; i++)
							{
								if(FileRequest -> fr_ArgList[i] . wa_Lock)
								{
									if(AddPart(TempBuffer,FileRequest -> fr_ArgList[i] . wa_Name,MAX_FILENAME_LENGTH))
									{
										if(Node = CreateArgName(TempBuffer))
											AddTail(List,Node);
										else
											*Error = ERR_NO_MEM;
									}
									else
										*Error = IoErr();
								}
								else
								{
									if(Node = CreateArgName(FileRequest -> fr_ArgList[i] . wa_Name))
										AddTail(List,Node);
									else
										*Error = ERR_NO_MEM;
								}
							}

							FreeAslRequest(FileRequest);
						}
						else
							*Error = ERR_ABORTED;

						break;

					case 'b':
					case 'B':

						SPrintf(SharedBuffer,"%ld",Config -> SerialConfig -> BaudRate);

						if(Node = CreateNode(SharedBuffer))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case 'c':
					case 'C':

						SPrintf(SharedBuffer,"%ld",DTERate ? DTERate : Config -> SerialConfig -> BaudRate);

						if(Node = CreateNode(SharedBuffer))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case 'p':
					case 'P':

						if(Node = CreateArgName(RexxPortName))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case 'd':
					case 'D':

						if(Node = CreateArgName(Config -> SerialConfig -> SerialDevice))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case 'u':
					case 'U':

						SPrintf(SharedBuffer,"%ld",Config -> SerialConfig -> UnitNumber);

						if(Node = CreateNode(SharedBuffer))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case '<':

						if(Node = CreateArgName(From))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case '>':

						if(Node = CreateArgName(To))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					case 's':
					case 'S':

						if(!GetPubScreenName(Window -> WScreen,SharedBuffer))
							SharedBuffer[0] = 0;

						if(Node = CreateArgName(SharedBuffer))
							AddTail(List,Node);
						else
							*Error = ERR_NO_MEM;

						break;

					default:

						*Dest = *Index;

						InsertBefore = FALSE;

						break;
				}

				if(InsertBefore && Node)
				{
					struct Node *OtherNode;

					*Dest = 0;

					if(LocalBuffer[0])
					{
						if(OtherNode = CreateNode(LocalBuffer))
						{
							Insert(List,OtherNode,InsertHere);

							LocalBuffer[0] = 0;
						}
						else
							*Error = ERR_NO_MEM;
					}

					Dest = LocalBuffer;
				}
			}
			else
				*Dest++ = *Index;

			Index++;
		}

		*Dest = 0;

		if(LocalBuffer[0] && !(*Error))
		{
			if(Node = CreateNode(LocalBuffer))
				AddTail(List,Node);
			else
				*Error = ERR_NO_MEM;
		}

		if(!(*Error))
		{
			if(List -> lh_Head -> ln_Succ)
			{
				LONG Total = 0,Len;

				for(Node = List -> lh_Head ; Node -> ln_Succ ; Node = Node -> ln_Succ)
					Total += strlen(Node -> ln_Name) + 1;

				if(Result = (STRPTR)AllocVecPooled(Total,MEMF_ANY))
				{
					STRPTR Buffer = Result;

					for(Node = List -> lh_Head ; Node -> ln_Succ ; Node = Node -> ln_Succ)
					{
						Len = strlen(Node -> ln_Name);

						memcpy(Buffer,Node -> ln_Name,Len);

						Buffer += Len;

						*Buffer++ = ' ';
					}

					Buffer[-1] = 0;
				}
			}
			else
				Result = "";
		}

		DeleteList(List);
	}
	else
		*Error = ERR_NO_MEM;

	return(Result);
}

	/* ReplaceName(STRPTR Original):
	 *
	 *	Replace a filename with a mangled version for
	 *	file uploads.
	 */

STATIC STRPTR __regargs
ReplaceName(STRPTR Source)
{
	if(Config -> TransferConfig -> MangleFileNames)
	{
		UBYTE LocalName[MAX_FILENAME_LENGTH],*Char;

		strcpy(OriginalName,Source);

		ShrinkName(FilePart(Source),LocalName,12,TRUE);

		strcpy(ShrunkenName,Source);

		Char = PathPart(ShrunkenName);

		*Char = 0;

		AddPart(ShrunkenName,LocalName,MAX_FILENAME_LENGTH);

		return(ShrunkenName);
	}
	else
	{
		OriginalName[0] = 0;

		return(Source);
	}
}

	/* FreeFileTransferInfo(struct FileTransferInfo *Info):
	 *
	 *	Free a file transfer info list as allocated
	 *	by AllocFileTransferInfo() and AddFileTransferNode().
	 */

VOID __regargs
FreeFileTransferInfo(struct FileTransferInfo *Info)
{
	if(Info)
	{
		struct FileTransferNode	*Node,
					*Next;

		Node = (struct FileTransferNode *)Info -> FileList . mlh_Head;

		while(Next = (struct FileTransferNode *)Node -> Node . mln_Succ)
		{
			FreeVecPooled(Node);

			Node = Next;
		}

		FreeVecPooled(Info);
	}
}

	/* AllocFileTransferInfo():
	 *
	 *	Allocate a FileTransferInfo structure for use with
	 *	AddFileTransferNode().
	 */

struct FileTransferInfo *
AllocFileTransferInfo()
{
	struct FileTransferInfo *Info;

	if(Info = (struct FileTransferInfo *)AllocVecPooled(sizeof(struct FileTransferInfo),MEMF_ANY | MEMF_CLEAR))
	{
		NewList((struct List *)&Info -> FileList);

		return(Info);
	}

	return(NULL);
}

	/* AddFileTransferNode(struct FileTransferInfo *Info,STRPTR Name,ULONG Size):
	 *
	 *	Allocate a file transfer information node.
	 */

BYTE __regargs
AddFileTransferNode(struct FileTransferInfo *Info,STRPTR Name,ULONG Size)
{
	struct FileTransferNode	*Node;
	WORD			 Len = strlen(Name) + 1;

	if(Node = (struct FileTransferNode *)AllocVecPooled(sizeof(struct FileTransferNode) + Len,MEMF_ANY))
	{
		Node -> Size	= Size;
		Node -> Name	= (STRPTR)(Node + 1);

		strcpy(Node -> Name,Name);

		AddTail((struct List *)&Info -> FileList,(struct Node *)Node);

		Info -> TotalSize += Size;

		Info -> TotalFiles++;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* Compare(struct FileTransferNode **A,struct FileTransferNode **B):
	 *
	 *	Local subroutine required by qsort().
	 */

STATIC int __stdargs
Compare(struct FileTransferNode **A,struct FileTransferNode **B)
{
	return(Stricmp(FilePart((*A) -> Name),FilePart((*B) -> Name)));
}

	/* SortFileTransferInfo(struct FileTransferInfo *Info):
	 *
	 *	Sorts the file transfer information list in ascending
	 *	order, but makes sure that the files are sorted
	 *	in descending order (i.e. the larger files come
	 *	first and files of equal size are sorted
	 *	lexically).
	 */

VOID __regargs
SortFileTransferInfo(struct FileTransferInfo *Info)
{
	if(Info -> TotalFiles > 1)
	{
		struct FileTransferNode **NodeList;

		if(NodeList = (struct FileTransferNode **)AllocVecPooled(sizeof(struct FileTransferNode *) * Info -> TotalFiles,MEMF_ANY))
		{
			struct FileTransferNode	*Node,
						*Next;
			LONG			 i = 0;

			Node = (struct FileTransferNode *)Info -> FileList . mlh_Head;

			while(Next = (struct FileTransferNode *)Node -> Node . mln_Succ)
			{
				NodeList[i++] = Node;

				Node = Next;
			}

			qsort((APTR)NodeList,Info -> TotalFiles,sizeof(struct FileTransferNode *),Compare);

			NewList((struct List *)&Info -> FileList);

			for(i = 0 ; i < Info -> TotalFiles ; i++)
				AddTail((struct List *)&Info -> FileList,(struct Node *)&NodeList[i] -> Node);

			FreeVecPooled(NodeList);
		}
	}

	Info -> DoneSize	= 0;
	Info -> DoneFiles	= 0;

	Info -> CurrentFile	= (struct FileTransferNode *)Info -> FileList . mlh_Head;
	Info -> CurrentSize	= Info -> CurrentFile -> Size;
}

	/* BuildFileTransferInfo(struct FileRequester *FileRequester):
	 *
	 *	Build a file transfer information list from
	 *	information provided by a FileRequester structure.
	 */

struct FileTransferInfo * __regargs
BuildFileTransferInfo(struct FileRequester *FileRequester)
{
	struct FileTransferInfo	*Info;
	LONG			 FilesFound = 0;

	if(Info = AllocFileTransferInfo())
	{
		BYTE	Success = FALSE;
		BPTR	NewLock,
			OldLock;

		if(NewLock = Lock(FileRequester -> rf_Dir,ACCESS_READ))
		{
			OldLock = CurrentDir(NewLock);

			if(FileRequester -> rf_NumArgs)
			{
				struct FileInfoBlock *FileInfo;

				if(FileInfo = (struct FileInfoBlock *)AllocDosObject(DOS_FIB,TAG_DONE))
				{
					BPTR		 FileLock;
					struct WBArg	*ArgList = FileRequester -> rf_ArgList;
					LONG		 i;

					Success = TRUE;

					for(i = 0 ; Success && i < FileRequester -> rf_NumArgs ; i++)
					{
						if(ArgList[i] . wa_Name)
						{
							if(ArgList[i] . wa_Lock)
							{
								CurrentDir(ArgList[i] . wa_Lock);

								if(FileLock = Lock(ArgList[i] . wa_Name,ACCESS_READ))
								{
									if(Examine(FileLock,FileInfo))
									{
										if(FileInfo -> fib_DirEntryType < 0)
										{
											if(NameFromLock(FileLock,SharedBuffer,512))
											{
												if(!AddFileTransferNode(Info,SharedBuffer,FileInfo -> fib_Size))
													Success = FALSE;
												else
													FilesFound++;
											}
										}
									}

									UnLock(FileLock);
								}

								CurrentDir(NewLock);
							}
							else
							{
								if(FileLock = Lock(ArgList[i] . wa_Name,ACCESS_READ))
								{
									if(Examine(FileLock,FileInfo))
									{
										if(FileInfo -> fib_DirEntryType < 0)
										{
											if(NameFromLock(FileLock,SharedBuffer,512))
											{
												if(!AddFileTransferNode(Info,SharedBuffer,FileInfo -> fib_Size))
													Success = FALSE;
												else
													FilesFound++;
											}
										}
									}

									UnLock(FileLock);
								}
							}
						}
					}
				}

				FreeDosObject(DOS_FIB,FileInfo);
			}
			else
			{
				struct AnchorPath *Anchor;

				STRPTR FileName;

				if(FileRequester -> rf_NumArgs > 1 && FileRequester -> rf_ArgList)
					FileName = FileRequester -> rf_ArgList -> wa_Name;
				else
					FileName = FileRequester -> rf_File;

				if(Anchor = (struct AnchorPath *)AllocVecPooled(sizeof(struct AnchorPath),MEMF_ANY | MEMF_CLEAR))
				{
					if(!MatchFirst(FileName,Anchor))
					{
						Success = TRUE;

						if(Anchor -> ap_Info . fib_DirEntryType < 0)
						{
							if(NameFromLock(NewLock,SharedBuffer,512))
							{
								if(AddPart(SharedBuffer,Anchor -> ap_Info . fib_FileName,512))
								{
									if(!AddFileTransferNode(Info,SharedBuffer,Anchor -> ap_Info . fib_Size))
										Success = FALSE;
									else
										FilesFound++;
								}
							}
						}

						if(Success)
						{
							while(!MatchNext(Anchor))
							{
								if(NameFromLock(NewLock,SharedBuffer,512))
								{
									if(AddPart(SharedBuffer,Anchor -> ap_Info . fib_FileName,512))
									{
										if(!AddFileTransferNode(Info,SharedBuffer,Anchor -> ap_Info . fib_Size))
										{
											Success = FALSE;

											break;
										}
										else
											FilesFound++;
									}
								}
							}
						}

						if(IoErr() != ERROR_NO_MORE_ENTRIES)
							Success = FALSE;
					}

					MatchEnd(Anchor);

					FreeVecPooled(Anchor);
				}
			}

			CurrentDir(OldLock);

			UnLock(NewLock);
		}

		if(Success && FilesFound)
		{
			SortFileTransferInfo(Info);

			return(Info);
		}
		else
			FreeFileTransferInfo(Info);
	}

	return(NULL);
}

	/* SendTextFile(STRPTR TheFile):
	 *
	 *	Send a single text file via xpr.
	 */

VOID __regargs
SendTextFile(BYTE Type,STRPTR TheFile)
{
	STRPTR	From,To,Name;
	BYTE	Mode;

	TheFile = ReplaceName(TheFile);

	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> ASCIIUploadType == XFER_INTERNAL)
	{
		InternalASCIIUpload(TheFile,TRUE);

		return;
	}

	switch(Type)
	{
		case TRANSFER_BINARY:

			Name	= Config -> TransferConfig -> BinaryUploadLibrary;
			Mode	= Config -> TransferConfig -> BinaryUploadType;
			From	= Config -> PathConfig -> BinaryUploadPath;
			To	= Config -> PathConfig -> BinaryDownloadPath;
			break;

		case TRANSFER_TEXT:

			Name	= Config -> TransferConfig -> TextUploadLibrary;
			Mode	= Config -> TransferConfig -> TextUploadType;
			From	= Config -> PathConfig -> TextUploadPath;
			To	= Config -> PathConfig -> TextDownloadPath;
			break;

		case TRANSFER_ASCII:

			Name	= Config -> TransferConfig -> ASCIIUploadLibrary;
			Mode	= Config -> TransferConfig -> ASCIIUploadType;
			From	= Config -> PathConfig -> ASCIIUploadPath;
			To	= Config -> PathConfig -> ASCIIDownloadPath;
			break;
	}

	if(Mode == XFER_DEFAULT && Config -> TransferConfig -> DefaultType == XFER_EXTERNALPROGRAM)
	{
		Name	= Config -> TransferConfig -> DefaultLibrary;
		Mode	= XFER_EXTERNALPROGRAM;
	}

	if(Mode == XFER_EXTERNALPROGRAM)
	{
		STRPTR	String;
		LONG	Error;

		BlockWindows();

		if(String = BuildString(Name,From,To,Type,FALSE,TheFile,NULL,&Error))
		{
			ClearSerial();

			LaunchCommand(String);

			RestartSerial(FALSE);

			FreeVecPooled(String);
		}

		ReleaseWindows();
	}
	else
	{
		BYTE OldStatus = Status;
		BPTR NewDir,OldDir;

		if(Type == TRANSFER_ASCII)
			NewDir = Lock(Config -> PathConfig -> ASCIIUploadPath,ACCESS_READ);
		else
			NewDir = Lock(Config -> PathConfig -> TextUploadPath,ACCESS_READ);

		if(NewDir)
			OldDir = CurrentDir(NewDir);
		else
			OldDir = NULL;

		Uploading = TRUE;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

			XprIO -> xpr_filename = TheFile;

			if(TransferPanel(SendQuery[TRANSFER_TEXT]))
			{
				Status = STATUS_UPLOAD;

				ClearSerial();

				LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[TRANSFER_TEXT]);

				if(ReadRequest && WriteRequest)
				{
					if(XProtocolSend(XprIO))
						TransferFailed = FALSE;
					else
						TransferFailed = TRUE;
				}
				else
					TransferFailed = TRUE;

				if(TransferFailed || TransferAborted)
					Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
				else
					Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

				if(TransferFailed || TransferError)
				{
					if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
						WakeUp(TransferWindow,SOUND_BADTRANSFER);

					DeleteTransferPanel(TRUE);
				}
				else
				{
					if(TransferWindow)
					{
						if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
							WakeUp(TransferWindow,SOUND_GOODTRANSFER);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

				RestartSerial(FALSE);
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		Uploading = FALSE;

		if(Config -> CommandConfig -> UploadMacro[0])
			SerialCommand(Config -> CommandConfig -> UploadMacro);

		DidTransfer = FALSE;
	}
}

	/* StartXprReceive():
	 *
	 *	Receive files via xpr.
	 */

VOID __regargs
StartXprReceive(BYTE Type,STRPTR Name,BYTE WaitForIt)
{
	STRPTR	From,To,Cmd;
	BYTE	Mode;

	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> ASCIIDownloadType == XFER_INTERNAL)
	{
		InternalASCIIDownload(Name,WaitForIt);

		return;
	}

	switch(Type)
	{
		case TRANSFER_BINARY:

			Cmd	= Config -> TransferConfig -> BinaryDownloadLibrary;
			Mode	= Config -> TransferConfig -> BinaryDownloadType;
			From	= Config -> PathConfig -> BinaryUploadPath;
			To	= Config -> PathConfig -> BinaryDownloadPath;
			break;

		case TRANSFER_TEXT:

			Cmd	= Config -> TransferConfig -> TextDownloadLibrary;
			Mode	= Config -> TransferConfig -> TextDownloadType;
			From	= Config -> PathConfig -> TextUploadPath;
			To	= Config -> PathConfig -> TextDownloadPath;
			break;

		case TRANSFER_ASCII:

			Cmd	= Config -> TransferConfig -> ASCIIDownloadLibrary;
			Mode	= Config -> TransferConfig -> ASCIIDownloadType;
			From	= Config -> PathConfig -> ASCIIUploadPath;
			To	= Config -> PathConfig -> ASCIIDownloadPath;
			break;
	}

	if(Mode == XFER_DEFAULT && Config -> TransferConfig -> DefaultType == XFER_EXTERNALPROGRAM)
	{
		Cmd	= Config -> TransferConfig -> DefaultLibrary;
		Mode	= XFER_EXTERNALPROGRAM;
	}

	if(Mode == XFER_EXTERNALPROGRAM)
	{
		STRPTR	String;
		LONG	Error;

		BlockWindows();

		if(String = BuildString(Cmd,From,To,Type,TRUE,Name,NULL,&Error))
		{
			ClearSerial();

			LaunchCommand(String);

			RestartSerial(TRUE);

			FreeVecPooled(String);
		}

		ReleaseWindows();
	}
	else
	{
		struct FileRequester	*FileRequest;
		UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
		BYTE			 OldStatus = Status;
		BPTR			 NewDir,OldDir;

		ClearGenericList(GenericListTable[GLIST_DOWNLOAD]);

		LocalizeString(ReceiveQuery,MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT,MSG_TERMTRANSFER_DOWNLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* Select the download path. */

		switch(Type)
		{
			case TRANSFER_BINARY:

				DownloadPath = Config -> PathConfig -> BinaryDownloadPath;
				break;

			case TRANSFER_TEXT:

				DownloadPath = Config -> PathConfig -> TextDownloadPath;
				break;

			case TRANSFER_ASCII:

				DownloadPath = Config -> PathConfig -> ASCIIDownloadPath;
				break;
		}

		if(DownloadPath[0])
		{
			if(NewDir = Lock(DownloadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* Set up the library if necessary. */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Do we need to ask the user for
				 * the destination file name?
				 */

			if(TransferBits & XPRS_NORECREQ)
			{
					/* Obviously not, let's open
					 * the transfer info window as
					 * usual and download the file(s).
					 */

				if(TransferPanel(ReceiveQuery[Type]))
				{
					Status = STATUS_DOWNLOAD;

					ClearSerial();

					LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_DOWNLOAD_TXT),TransferTypes[Type]);

						/* Receive the data. */

					if(ReadRequest && WriteRequest)
					{
						if(XProtocolReceive(XprIO))
							TransferFailed = FALSE;
						else
							TransferFailed = TRUE;
					}
					else
						TransferFailed = TRUE;

						/* In case the transfer has been aborted,
						 * flush the input buffer of dirty data.
						 */

					if(TransferAborted)
						xpr_sflush();

					if(TransferAborted || TransferFailed)
						Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
					else
						Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

					if(TransferFailed || TransferError)
					{
						if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
							WakeUp(TransferWindow,SOUND_BADTRANSFER);

						DeleteTransferPanel(WaitForIt);
					}
					else
					{
						if(TransferWindow)
						{
							if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
								WakeUp(TransferWindow,SOUND_GOODTRANSFER);

							WaitTime(2,0);
						}

						DeleteTransferPanel(FALSE);
					}

					Status = OldStatus;

						/* Queue another read request. */

					RestartSerial(FALSE);
				}
			}
			else
			{
				if(!Name)
				{
					if(FileRequest = GetFile(Window,ReceiveQuery[Type],DownloadPath,"",DummyBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE))
					{
							/* Save the download path. */

						strcpy(DownloadPath,FileRequest -> rf_Dir);

						ConfigChanged = TRUE;

							/* Install the name of the file to receive. */

						XprIO -> xpr_filename = DummyBuffer;

						FreeAslRequest(FileRequest);

						Name = DummyBuffer;
					}
				}
				else
				{
					STRPTR Index;

					strcpy(DownloadPath,Name);

					ConfigChanged = TRUE;

					Index = PathPart(DownloadPath);

					*Index = 0;

					XprIO -> xpr_filename = Name;
				}

					/* Download the file(s). */

				if(Name)
				{
						/* Open the transfer panel. */

					if(TransferPanel(ReceiveQuery[Type]))
					{
						Status = STATUS_DOWNLOAD;

						ClearSerial();

						LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_DOWNLOAD_TXT),TransferTypes[Type]);

							/* Receive the file. */

						if(ReadRequest && WriteRequest)
						{
							if(XProtocolReceive(XprIO))
								TransferFailed = FALSE;
							else
								TransferFailed = TRUE;
						}
						else
							TransferFailed = TRUE;

							/* In case the transfer has been aborted,
							 * flush the input buffer of dirty data.
							 */

						if(TransferAborted)
							xpr_sflush();

						if(TransferAborted || TransferFailed)
							Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
						else
							Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

						if(TransferFailed || TransferError)
						{
							if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
								WakeUp(TransferWindow,SOUND_BADTRANSFER);

							DeleteTransferPanel(WaitForIt);
						}
						else
						{
							if(TransferWindow)
							{
								if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
									WakeUp(TransferWindow,SOUND_GOODTRANSFER);

								WaitTime(2,0);
							}

							DeleteTransferPanel(FALSE);
						}

						Status = OldStatus;

							/* Queue another read
							 * request.
							 */

						RestartSerial(FALSE);
					}
				}
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		DownloadPath = NULL;

		DidTransfer = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> DownloadMacro[0])
				SerialCommand(Config -> CommandConfig -> DownloadMacro);
		}
	}
}

	/* StartXprSend():
	 *
	 *	Send files via xpr.
	 */

BYTE __regargs
StartXprSend(BYTE Type,BYTE WaitForIt)
{
	STRPTR	From,To,Name;
	BYTE	Mode;

	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> ASCIIDownloadType == XFER_INTERNAL)
		return(InternalASCIIUpload(NULL,WaitForIt));

	switch(Type)
	{
		case TRANSFER_BINARY:

			Name	= Config -> TransferConfig -> BinaryUploadLibrary;
			Mode	= Config -> TransferConfig -> BinaryUploadType;
			From	= Config -> PathConfig -> BinaryUploadPath;
			To	= Config -> PathConfig -> BinaryDownloadPath;
			break;

		case TRANSFER_TEXT:

			Name	= Config -> TransferConfig -> TextUploadLibrary;
			Mode	= Config -> TransferConfig -> TextUploadType;
			From	= Config -> PathConfig -> TextUploadPath;
			To	= Config -> PathConfig -> TextDownloadPath;
			break;

		case TRANSFER_ASCII:

			Name	= Config -> TransferConfig -> ASCIIUploadLibrary;
			Mode	= Config -> TransferConfig -> ASCIIUploadType;
			From	= Config -> PathConfig -> ASCIIUploadPath;
			To	= Config -> PathConfig -> ASCIIDownloadPath;
			break;
	}

	if(Mode == XFER_DEFAULT && Config -> TransferConfig -> DefaultType == XFER_EXTERNALPROGRAM)
	{
		Name	= Config -> TransferConfig -> DefaultLibrary;
		Mode	= XFER_EXTERNALPROGRAM;
	}

	if(Mode == XFER_EXTERNALPROGRAM)
	{
		STRPTR	String;
		LONG	Error;
		BYTE	Result;

		BlockWindows();

		if(String = BuildString(Name,From,To,Type,FALSE,NULL,NULL,&Error))
		{
			ClearSerial();

			if(LaunchCommand(String))
				Result = FALSE;
			else
				Result = TRUE;

			RestartSerial(TRUE);

			FreeVecPooled(String);
		}
		else
			Result = FALSE;

		ReleaseWindows();

		return(Result);
	}
	else
	{
		struct FileRequester	*FileRequest;
		UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
		BYTE			 OldStatus = Status;
		STRPTR			 UploadPath;
		BYTE			 DidSend = TRUE;
		BPTR			 NewDir,OldDir;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* We are uploading data. */

		Uploading = TRUE;

			/* Select the upload path. */

		switch(Type)
		{
			case TRANSFER_BINARY:

				UploadPath = Config -> PathConfig -> BinaryUploadPath;
				break;

			case TRANSFER_TEXT:

				UploadPath = Config -> PathConfig -> TextUploadPath;
				break;

			case TRANSFER_ASCII:

				UploadPath = Config -> PathConfig -> ASCIIUploadPath;
				break;
		}

		if(UploadPath[0])
		{
			if(NewDir = Lock(UploadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Do we need to use our own file requester or
				 * will xpr handle this job for us?
				 */

			if(TransferBits & XPRS_NOSNDREQ)
			{
					/* Open the transfer info window. */

				if(TransferPanel(SendQuery[Type]))
				{
					Status = STATUS_UPLOAD;

						/* Shut up the serial line. */

					ClearSerial();

					LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

						/* Perform upload. */

					if(ReadRequest && WriteRequest)
					{
						if(XProtocolSend(XprIO))
							TransferFailed = FALSE;
						else
							TransferFailed = TRUE;
					}
					else
						TransferFailed = TRUE;

					if(TransferFailed || TransferAborted)
						Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
					else
						Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

					if(TransferFailed || TransferError)
					{
						if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
							WakeUp(TransferWindow,SOUND_BADTRANSFER);

						DeleteTransferPanel(WaitForIt);
					}
					else
					{
						if(TransferWindow)
						{
							if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
								WakeUp(TransferWindow,SOUND_GOODTRANSFER);

							WaitTime(2,0);
						}

						DeleteTransferPanel(FALSE);
					}

					Status = OldStatus;

						/* And request another character. */

					RestartSerial(FALSE);
				}
			}
			else
			{
					/* We will need the file requester to find
					 * out which file(s) are to be transferred.
					 * Multiple files and wildcards are
					 * supported as well as plain file names.
					 */

				if(FileRequest = GetFile(Window,SendQuery[Type],UploadPath,"",DummyBuffer,"",FALSE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),TRUE))
				{
					strcpy(UploadPath,FileRequest -> rf_Dir);

					ConfigChanged = TRUE;

					if(FileTransferInfo = BuildFileTransferInfo(FileRequest))
					{
							/* Make sure that at least the
							 * first file gets transferred
							 * in case the protocol does
							 * no support batch upload.
							 */

						XprIO -> xpr_filename = ReplaceName(FileTransferInfo -> CurrentFile -> Name);

						if(TransferPanel(SendQuery[Type]))
						{
							Status = STATUS_UPLOAD;

							ClearSerial();

							LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

							if(ReadRequest && WriteRequest)
							{
								if(XProtocolSend(XprIO))
									TransferFailed = FALSE;
								else
									TransferFailed = TRUE;
							}
							else
								TransferFailed = TRUE;

							if(TransferFailed || TransferAborted)
								Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
							else
								Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

							if(TransferFailed || TransferError)
							{
								if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
									WakeUp(TransferWindow,SOUND_BADTRANSFER);

								DeleteTransferPanel(WaitForIt);
							}
							else
							{
								if(TransferWindow)
								{
									if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
										WakeUp(TransferWindow,SOUND_GOODTRANSFER);

									WaitTime(2,0);
								}

								DeleteTransferPanel(FALSE);
							}

							Status = OldStatus;

							RestartSerial(FALSE);
						}
						else
							MyEasyRequest(Window,LocaleString(MSG_TERMTRANSFER_FAILED_TO_LOCATE_DIRECTORY_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),FileRequest -> rf_Dir);

						if(FileTransferInfo)
						{
							FreeFileTransferInfo(FileTransferInfo);

							FileTransferInfo = NULL;
						}
					}

					FreeAslRequest(FileRequest);
				}
				else
					DidSend = FALSE;
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		Uploading = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> UploadMacro[0])
				SerialCommand(Config -> CommandConfig -> UploadMacro);
		}

		DidTransfer = FALSE;

		return(DidSend);
	}
}

	/* StartXprSendFromList():
	 *
	 *	Send files via xpr.
	 */

BYTE __regargs
StartXprSendFromList(BYTE Type,BYTE WaitForIt)
{
	STRPTR	From,To,Name;
	BYTE	Mode;

	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> ASCIIDownloadType == XFER_INTERNAL)
		return(InternalASCIIUpload(NULL,WaitForIt));

	switch(Type)
	{
		case TRANSFER_BINARY:

			Name	= Config -> TransferConfig -> BinaryUploadLibrary;
			Mode	= Config -> TransferConfig -> BinaryUploadType;
			From	= Config -> PathConfig -> BinaryUploadPath;
			To	= Config -> PathConfig -> BinaryDownloadPath;
			break;

		case TRANSFER_TEXT:

			Name	= Config -> TransferConfig -> TextUploadLibrary;
			Mode	= Config -> TransferConfig -> TextUploadType;
			From	= Config -> PathConfig -> TextUploadPath;
			To	= Config -> PathConfig -> TextDownloadPath;
			break;

		case TRANSFER_ASCII:

			Name	= Config -> TransferConfig -> ASCIIUploadLibrary;
			Mode	= Config -> TransferConfig -> ASCIIUploadType;
			From	= Config -> PathConfig -> ASCIIUploadPath;
			To	= Config -> PathConfig -> ASCIIDownloadPath;
			break;
	}

	if(Mode == XFER_DEFAULT && Config -> TransferConfig -> DefaultType == XFER_EXTERNALPROGRAM)
	{
		Name	= Config -> TransferConfig -> DefaultLibrary;
		Mode	= XFER_EXTERNALPROGRAM;
	}

	if(Mode == XFER_EXTERNALPROGRAM)
	{
		STRPTR	String;
		LONG	Error;
		BYTE	Result;

		BlockWindows();

		if(String = BuildString(Name,From,To,Type,FALSE,NULL,FileTransferInfo,&Error))
		{
			ClearSerial();

			if(LaunchCommand(String))
				Result = FALSE;
			else
				Result = TRUE;

			RestartSerial(TRUE);

			FreeVecPooled(String);
		}
		else
			Result = FALSE;

		if(FileTransferInfo)
		{
			FreeFileTransferInfo(FileTransferInfo);

			FileTransferInfo = NULL;
		}

		ReleaseWindows();

		return(Result);
	}
	else
	{
		BYTE	OldStatus	= Status,
			DidSend		= TRUE;
		BPTR	NewDir,
			OldDir;
		STRPTR	UploadPath;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* We are uploading data. */

		Uploading = TRUE;

		switch(Type)
		{
			case TRANSFER_BINARY:

				UploadPath = Config -> PathConfig -> BinaryUploadPath;
				break;

			case TRANSFER_TEXT:

				UploadPath = Config -> PathConfig -> TextUploadPath;
				break;

			case TRANSFER_ASCII:

				UploadPath = Config -> PathConfig -> ASCIIUploadPath;
				break;
		}

		if(UploadPath[0])
		{
			if(NewDir = Lock(UploadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(WaitForIt)
			{
				if(SelectProtocol(LastXprLibrary,Window))
				{
					if(ProtocolSetup(FALSE))
					{
						SaveProtocolOpts();

						strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
					}
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Make sure that at least the
				 * first file gets transferred
				 * in case the protocol does
				 * no support batch upload.
				 */

			XprIO -> xpr_filename = ReplaceName(FileTransferInfo -> CurrentFile -> Name);

			if(TransferPanel(SendQuery[Type]))
			{
				Status = STATUS_UPLOAD;

				ClearSerial();

				LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

				if(ReadRequest && WriteRequest)
				{
					if(XProtocolSend(XprIO))
						TransferFailed = FALSE;
					else
						TransferFailed = TRUE;
				}
				else
					TransferFailed = TRUE;

				if(TransferFailed || TransferAborted)
					Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
				else
					Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

				if(TransferFailed || TransferError)
				{
					if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
						WakeUp(TransferWindow,SOUND_BADTRANSFER);

					DeleteTransferPanel(WaitForIt);
				}
				else
				{
					if(TransferWindow)
					{
						if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
							WakeUp(TransferWindow,SOUND_GOODTRANSFER);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

				RestartSerial(FALSE);
			}

			if(FileTransferInfo)
			{
				FreeFileTransferInfo(FileTransferInfo);

				FileTransferInfo = NULL;
			}
		}
		else
		{
			SetTransferMenu(FALSE);

			DidSend = FALSE;
		}

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		Uploading = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> UploadMacro[0])
				SerialCommand(Config -> CommandConfig -> UploadMacro);
		}

		DidTransfer = FALSE;

		return(DidSend);
	}
}

	/* ChangeProtocol(STRPTR ProtocolName):
	 *
	 *	Select a different file transfer protocol.
	 */

BYTE __regargs
ChangeProtocol(STRPTR ProtocolName,BYTE Type)
{
	UBYTE NameBuffer[40],i;

	if(Type == XFER_DEFAULT)
	{
		ProtocolName = Config -> TransferConfig -> DefaultLibrary;

		Type = Config -> TransferConfig -> DefaultType;
	}

	if(Type == XFER_XPR)
	{
		if(!ProtocolName || !ProtocolName[0])
			ProtocolName = Config -> TransferConfig -> DefaultLibrary;

		if(!Stricmp(ProtocolName,LastXprLibrary) && XProtocolBase)
			return(TRUE);
	}

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

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;

		SetTransferMenu(FALSE);
	}

	if(Type == XFER_INTERNAL)
	{
		UsesZModem = FALSE;

		SetTransferMenu(TRUE);

		strcpy(TransferProtocolName,"ASCII");

		return(TRUE);
	}

	if(Type == XFER_EXTERNALPROGRAM)
	{
		WORD i;

		memcpy(NameBuffer,FilePart(ProtocolName),40);

		NameBuffer[39] = 0;

		for(i = 0 ; i < 40 ; i++)
		{
			if(NameBuffer[i] == ' ')
			{
				NameBuffer[i] = 0;

				break;
			}
		}

		NameBuffer[0] = ToUpper(NameBuffer[0]);

		strcpy(TransferProtocolName,NameBuffer);

		UsesZModem = FALSE;

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

		SetTransferMenu(TRUE);

		return(TRUE);
	}

		/* Clear the XPR interface buffer. */

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

		/* Copy the name of the library. */

	strcpy(NameBuffer,FilePart(ProtocolName));

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

		/* Reset the scanner. */

	FlowInit(TRUE);

		/* Obtain the protocol default settings. */

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

	NameBuffer[3] = ToUpper(NameBuffer[3]);

		/* Initialize the interface structure. */

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

		/* Try to open the library. */

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

		ClearSerial();

		TransferBits = XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(TRUE);

			/* Successful initialization? */

		if(TransferBits & XPRS_SUCCESS)
		{
			SetTransferMenu(TRUE);

			strcpy(LastXprLibrary,ProtocolName);

			if(TransferBits & XPRS_HOSTMON)
				ConTransfer = ConTransferHost;
			else
				ConTransfer = ConProcess;

			if(TransferBits & XPRS_HOSTNOWAIT)
			{
				if(!HostReadBuffer)
					HostReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY);
			}
			else
			{
				if(HostReadBuffer)
				{
					FreeVecPooled(HostReadBuffer);

					HostReadBuffer = NULL;
				}
			}

			strcpy(TransferProtocolName,&NameBuffer[3]);

			return(TRUE);
		}
		else
		{
			MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),ProtocolName);

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			SetTransferMenu(FALSE);

			LastXprLibrary[0] = 0;

			TransferBits = 0;

			ConTransfer = ConProcess;

			TransferProtocolName[0] = 0;
		}
	}
	else
	{
		ConTransfer = ConProcess;

		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),ProtocolName);

		SetTransferMenu(FALSE);

		TransferBits = 0;

		LastXprLibrary[0] = 0;

		TransferProtocolName[0] = 0;
	}

	return(FALSE);
}

	/* ResetProtocol():
	 *
	 *	Return to the previously selected file
	 *	transfer protocol.
	 */

VOID
ResetProtocol()
{
	ChangeProtocol(NULL,XFER_DEFAULT);
}

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

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

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

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;

		SetTransferMenu(FALSE);
	}

	if(Config -> TransferConfig -> DefaultType == XFER_EXTERNALPROGRAM)
	{
		if(Config -> TransferConfig -> DefaultLibrary[0])
		{
			WORD i;

			memcpy(NameBuffer,FilePart(Config -> TransferConfig -> DefaultLibrary),40);

			NameBuffer[39] = 0;

			for(i = 0 ; i < 40 ; i++)
			{
				if(NameBuffer[i] == ' ')
				{
					NameBuffer[i] = 0;

					break;
				}
			}

			NameBuffer[0] = ToUpper(NameBuffer[0]);

			strcpy(TransferProtocolName,NameBuffer);

			UsesZModem = FALSE;

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

			SetTransferMenu(TRUE);

			return(TRUE);
		}
		else
			return(FALSE);
	}

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

	NameBuffer[3] = ToUpper(NameBuffer[3]);

		/* Reset the scanner. */

	FlowInit(TRUE);

		/* Obtain the protocol default settings. */

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

		/* Initialize the interface structure. */

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

		/* Try to open the library. */

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

		ClearSerial();

		TransferBits = XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(IgnoreOptions != TRUE);

			/* Successful initialization? */

		if(TransferBits & XPRS_SUCCESS)
		{
			SetTransferMenu(TRUE);

			if(TransferBits & XPRS_HOSTMON)
				ConTransfer = ConTransferHost;
			else
				ConTransfer = ConProcess;

			if(TransferBits & XPRS_HOSTNOWAIT)
			{
				if(!HostReadBuffer)
					HostReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY);
			}
			else
			{
				if(HostReadBuffer)
				{
					FreeVecPooled(HostReadBuffer);

					HostReadBuffer = NULL;
				}
			}

			strcpy(TransferProtocolName,&NameBuffer[3]);

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

			ConTransfer = ConProcess;

			SetTransferMenu(FALSE);

			TransferProtocolName[0] = 0;
		}
	}
	else
	{
		ConTransfer = ConProcess;

		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LastXprLibrary);

		LastXprLibrary[0] = 0;

		TransferBits = 0;

		SetTransferMenu(FALSE);

		TransferProtocolName[0] = 0;
	}

	return(FALSE);
}

	/* 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	= xpr_stealopts;
		XprIO -> xpr_extension	= 0;
		XprIO -> xpr_options	= NULL;

		ClearSerial();

		XProtocolSetup(XprIO);

		DeleteTransferPanel(FALSE);

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

		NewOptions = FALSE;

		SetEnvDOS(NameBuffer,ProtocolOptsBuffer);

			/* Reinitialize the library. */

		XprIO -> xpr_filename	= ProtocolOptsBuffer;
		XprIO -> xpr_gets	= xpr_gets;
		XprIO -> xpr_extension	= 4;
		XprIO -> xpr_options	= xpr_options;

		XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(FALSE);
	}
}

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

BYTE __regargs
SelectProtocol(STRPTR Name,struct Window *ParentWindow)
{
	strcpy(SharedBuffer,LastXprLibrary);

	if(PickFile(Window,"Libs:","xpr#?.library",LocaleString(MSG_TERMXPR_SELECT_TRANSFER_PROTOCOL_TXT),SharedBuffer,NT_LIBRARY))
	{
		if(Stricmp(SharedBuffer,LastXprLibrary))
		{
			strcpy(LastXprLibrary,SharedBuffer);

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* TransferCleanup():
	 *
	 *	We did a file transfer (auto-download?) and
	 *	will need to close the transfer window.
	 */

VOID
TransferCleanup()
{
	if(DidTransfer)
	{
		if(TransferFailed || TransferError)
		{
			if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
				WakeUp(TransferWindow,SOUND_BADTRANSFER);

			DeleteTransferPanel(TRUE);
		}
		else
		{
			if(TransferWindow)
			{
				if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
					WakeUp(TransferWindow,SOUND_GOODTRANSFER);

				WaitTime(2,0);
			}

			DeleteTransferPanel(FALSE);
		}

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		if(Config -> CommandConfig -> DownloadMacro[0])
			SerialCommand(Config -> CommandConfig -> DownloadMacro);

		Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

		DidTransfer = FALSE;
	}
	else
	{
		if(TransferFailed || TransferError)
		{
			if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
				WakeUp(TransferWindow,SOUND_BADTRANSFER);

			DeleteTransferPanel(TRUE);
		}
		else
		{
			if(TransferWindow)
			{
				if(Config -> TransferConfig -> TransferNotification == XFERNOTIFY_ALWAYS || Config -> TransferConfig -> TransferNotification == XFERNOTIFY_END)
					WakeUp(TransferWindow,SOUND_GOODTRANSFER);

				WaitTime(2,0);
			}

			DeleteTransferPanel(FALSE);
		}
	}

	BinaryTransfer = TRUE;

	Status = STATUS_READY;

	ReleaseWindows();
}

	/* RemoveUploadListItem(STRPTR Name):
	 *
	 *	Remove a named item from the upload list.
	 */

VOID __regargs
RemoveUploadListItem(STRPTR Name)
{
	BPTR NameLock;

	if(NameLock = Lock(Name,ACCESS_READ))
	{
		struct GenericList	*List = GenericListTable[GLIST_UPLOAD];
		struct Node		*Node;
		STRPTR			 Base = FilePart(Name);

		ObtainSemaphore(&List -> ListSemaphore);

		Node = (struct Node *)List -> ListHeader . mlh_Head;

		while(Node -> ln_Succ)
		{
			if(!Stricmp(Base,FilePart(Node -> ln_Name)))
			{
				BPTR ListLock;

				if(ListLock = Lock(Node -> ln_Name,ACCESS_READ))
				{
					if(SameLock(ListLock,NameLock) == LOCK_SAME)
					{
						Forbid();

						ReleaseSemaphore(&List -> ListSemaphore);

						DeleteGenericListNode(List,Node);

						Permit();

						UnLock(ListLock);

						UnLock(NameLock);

						return;
					}

					UnLock(ListLock);
				}
			}

			Node = Node -> ln_Succ;
		}

		ReleaseSemaphore(&List -> ListSemaphore);

		UnLock(NameLock);
	}
}
