// --------------------------------------------------------------------------
// Citadel: FileCmd.CPP
//
// File handling user commands for Citadel

#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "roompriv.h"       // this has to go away.
#include "infofile.h"
#include "filecmd.h"
#include "msg.h"
#include "log.h"
#include "group.h"
#include "account.h"
#include "cfgfiles.h"
#include "blurbs.h"
#include "events.h"
#include "menus.h"
#include "aplic.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// attributes() 		aide fn to set file attributes
// blocks() 			displays how many blocks file is
// CanUploadFile()		returns TRUE if filename can be uploaded
// CheckDirectoryRoom() returns TRUE if directory, else -- not dir room
// entertextfile()		menu level .et
// enterwc()			menu level .ew file
// entrycopy()			readable struct -> directory array
// entrymake()			dos transfer struct -> readable struct
// readdirectory()		menu level .rd .rvd routine
// readtextfile()		menu level .rt routine
// readwc() 			menu level .rw file
// renamefile() 		aide fn to rename a file
// textdown()			does wildcarded unformatted file dumps
// textup() 			handles actual text upload
// unlinkfile() 		handles the .au command
// wcdown() 			calls xmodem downloading routines
// wcup()				calls xmodem uploading routines
// upload() 			menu level routine
// moveFile()			copy info-buffer & move file


static void textup(const char *filename, Bool HideIt);
static void textdown(const char *filename, OldNewPick which);

static InternalProtocols XmodemMenu(char cmd);
static void XMnu(char cmd);
static void wcdown(const char *filename, InternalProtocols prot);
static void wcup(const char *filename, InternalProtocols prot);

extern int Zmodem_Send_Batch (const char *filepath, unsigned int baud);
extern int Zmodem_Receive_Batch (const char *filepath, unsigned int baud);


// --------------------------------------------------------------------------
// downloadFileQueue(): .QD.

Bool downloadFileQueue(void)
	{
	if (TI()fileList)
		{
		Bool toRet;

		int ich;
		int ch = tolower(ich = iCharNE());

		if (ch == '\n' || ch == '\r')
			{
			ch = TI()CurrentUser->GetDefaultProtocol();
			}

		const protocols *theProt = GetProtocolByKey(ch, TRUE);

		if (theProt)
			{
			mPrintf(pcts, theProt->name);

			if (!theProt->respDown[0])
				{
				mPrintf(getmsg(438));
				return (FALSE);
				}

			doCR();

			FILE *tmpFile;

			changedir(cfg.aplpath);
			tmpFile = fopen(citfiles[C_FILEQTMP_CIT], FO_W);

			if (tmpFile)
				{
				for (fileQueue *theFile = TI()fileList;
						theFile;
						theFile = (fileQueue *) getNextLL(theFile))
					{
					fprintf(tmpFile, pcts, theFile->fname);
					fprintf(tmpFile, bn);
					}

				fclose(tmpFile);

				doTheDownload(citfiles[C_FILEQTMP_CIT], cfg.aplpath, FALSE,
						theProt, TRUE);
				unlink(citfiles[C_FILEQTMP_CIT]);
				toRet = TRUE;
				}
			else
				{
				CRmPrintf(getmsg(8), citfiles[C_FILEQTMP_CIT]);
				toRet = FALSE;
				}
			}
		else
			{
			if (ch == '?')
				{
				oChar('?');
				upDownMnu('D', TRUE);
				}
			else
				{
				oChar(ich);
				mPrintf(sqst);
				if (!TI()CurrentUser->IsExpert())
					{
					upDownMnu('D', TRUE);
					}
				}

			toRet = FALSE;
			}


		TI()SerialPort.Flush();
		// go back to home
		changedir(cfg.homepath);
		return (toRet);
		}
	else
		{
		CRCRmPrintfCR(getmsg(446));
		changedir(cfg.homepath);
		return (FALSE);
		}
	}


// --------------------------------------------------------------------------
// addFileQueue: Adds files to .Q.

static void doTheAdd(const char *fn)
	{
	if (!IsFilenameLegal(fn, NULL) || !filexists(fn))
		{
		CRmPrintf(getmsg(1050), fn);
		}
	else
		{
		fileQueue *theFile;

		for (theFile = TI()fileList; theFile;
				theFile = (fileQueue *) getNextLL(theFile))
			{
			if (SameString(GetRoomName(TI()thisRoom), theFile->room) &&
					SameString(nameonly(theFile->fname), fn))
				{
				CRmPrintf(getmsg(439), fn);
				break;
				}
			}

		if (!theFile)
			{
			theFile = (fileQueue *) addLL((void **) &TI()fileList,
					sizeof(*theFile) + strlen(fn) +
					strlen(GetCurrentRoomDirectory()) + 1);

			if (theFile)
				{
				CopyStringToBuffer(theFile->room, GetRoomName(TI()thisRoom), LABELSIZE);
				sprintf(theFile->fname, sbs, GetCurrentRoomDirectory(), fn);
				strlwr(theFile->fname);

				const int handle = open(fn, O_RDONLY);

				if (handle >= 0)
					{
					theFile->size = filelength(handle);
					close(handle);
					}

				CRmPrintf(getmsg(440), nameonly(theFile->fname),
						ltoac(theFile->size),
						theFile->size == 1 ? ns : justs,
						dltime(theFile->size));
				}
			else
				{
				CRmPrintf(getmsg(188), fn);
				}
			}
		}
	}

void addFileQueue(void)
	{
	char filename[128];
	char *words[256];
	struct ffblk ff;

	SetDoWhat(FILEQUEUEA);

	TI()UserControl.SetOutFlag(OUTOK);
	doCR();

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	getNormStr(getmsg(1073), filename, 127, ECHO);

	if (!*filename)
		{
		changedir(cfg.homepath);
		return;
		}

	const int count = parse_it(words, filename);

	if (count == 0)
		{
		changedir(cfg.homepath);
		return;
		}

	for (int i = 0; i < count; i++)
		{
		if (ambig(words[i]))
			{
			if (!findfirst(words[i], &ff, 0))
				{
				do
					{
					doTheAdd(ff.ff_name);
					} while (!findnext(&ff));
				}
			else
				{
				CRmPrintf(getmsg(1050), words[i]);
				}
			}
		else
			{
			doTheAdd(words[i]);
			}
		}
	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// listFileQueue(): Lists all files in .Q.

void listFileQueue(void)
	{
	SetDoWhat(FILEQUEUEL);

	TI()UserControl.SetOutFlag(OUTOK);
	doCR();

	if (TI()fileList)
		{
		long totalSize = 0;
		int fileCounter = 0;

		mPrintfCR(getmsg(841), cfg.Uroom_nym, getmsg(442),
				getmsg(443), getmsg(444));

		for (fileQueue *thisFile = TI()fileList; thisFile;
				thisFile = (fileQueue *) getNextLL(thisFile))
			{
			mPrintfCR(getmsg(840), ++fileCounter, thisFile->room,
					nameonly(thisFile->fname), ltoac(thisFile->size),
					dltime(thisFile->size));

			totalSize += thisFile->size;
			}

		if (fileCounter > 1)
			{
			for (int i = 0; i < 71; i++)
				{
				oChar('-');
				}

			CRmPrintfCR(getmsg(839), getmsg(445), ltoac(totalSize),
					dltime(totalSize));
			}
		}
	else
		{
		CRmPrintfCR(getmsg(446));
		}
	}


// --------------------------------------------------------------------------
// removeFileQueue(): Removes files from .Q.

void removeFileQueue(void)
	{
	label rmv;

	SetDoWhat(FILEQUEUER);

	TI()UserControl.SetOutFlag(OUTOK);
	doCR();

	do
		{
		getString(getmsg(1245), rmv, LABELSIZE, TRUE, ECHO, ns);

		if (*rmv == '?')
			{
			listFileQueue();
			}
		} while (*rmv == '?');

	normalizeString(rmv);

	if (*rmv)
		{
		const int r = atoi(rmv);
		fileQueue *theFile = TI()fileList;
		for (int i = 1; theFile;
				theFile = (fileQueue *) getNextLL(theFile), i++)
			{
			if (i == r || SameString(rmv, nameonly(theFile->fname)))
				{
				deleteLLNode((void **) &TI()fileList, i);
				break;
				}
			}

		if (!theFile)
			{
			mPrintf(getmsg(1246));
			}
		}
	}


// --------------------------------------------------------------------------
// clearFileQueue(): Clears file queue.

void clearFileQueue(void)
	{
	SetDoWhat(FILEQUEUEC);

	disposeLL((void **) &TI()fileList);
	}


// --------------------------------------------------------------------------
// attributes(): Aide fn to set file attributes.

void attributes(void)
	{
	label filename;

	SetDoWhat(AIDEATTR);

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	doCR();
	getNormStr(getmsg(1073), filename, LABELSIZE, ECHO);

	if (!*filename)
		{
		changedir(cfg.homepath);
		return;
		}

	if (!IsFilenameLegal(filename, NULL) || ambig(filename))
		{
		CRmPrintfCR(getmsg(1064), filename);
		changedir(cfg.homepath);
		return;
		}

	if (!filexists(filename))
		{
		CRmPrintfCR(getmsg(422));
		changedir(cfg.homepath);
		return;
		}

	Message *Msg = new Message;

	if (Msg)
		{
		Bool ReadOnly, Hidden;
		int attr = _chmod(filename, 0);

		ReadOnly = getYesNo(getmsg(453), attr & FA_RDONLY);
		Hidden = getYesNo(getmsg(454), attr & FA_HIDDEN);

		// set read-only and hidden bits
		if (ReadOnly)
			{
			attr |= FA_RDONLY;
			}
		else
			{
			attr &= ~FA_RDONLY;
			}

		if (Hidden)
			{
			attr |= FA_HIDDEN;
			}
		else
			{
			attr &= ~FA_HIDDEN;
			}

		_chmod(filename, 1, attr);

		Msg->SetTextWithFormat(getmsg(455), filename,
				GetRoomName(TI()thisRoom), TI()CurrentUser->GetName());

		trap(Msg->GetText(), T_AIDE);

		if (IsRoomGroupOnly(TI()thisRoom))
			{
			if (IsRoomMultiGroup(TI()thisRoom))
				{
				Msg->SetGroup(GroupData->GetEntry(1)->GetName());
				}
			else
				{
				Msg->SetGroup(GroupData->GetEntry(
						GetRoomGroupNumber(TI()thisRoom))->GetName());
				}
			}
		Msg->SetRoomNumber(AIDEROOM);
		systemMessage(Msg);

		delete Msg;
		}
	else
		{
		mPrintf(getmsg(188), getmsg(631));
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// blocks(): Displays how many blocks file is upon download.

static void blocks(const char *filename, long length, int bsize)
	{
	TI()UserControl.SetOutFlag(OUTOK);

	CRmPrintf(getmsg(872), filename);

	if (length == -1l)
		{
		return;
		}

	doCR();

	if (!bsize)
		{
		mPrintf(getmsg(456), ltoac(length), (length == 1l) ? ns : justs);
		}
	else
		{
		const int blocks = (int) (length / (long) bsize) + 1;
		label Blocks;
		strcpy(Blocks, ltoac(blocks));
		mPrintf(getmsg(457), Blocks, (blocks == 1) ? ns : justs, ltoac(length),
				(length == 1l) ? ns : justs);
		}

	CRmPrintfCR(getmsg(458), dltime(length));
	}


// --------------------------------------------------------------------------
// CanUploadFile(): Returns TRUE if filename can be uploaded.

static Bool CanUploadFile(const char *filename)
	{
	if (!*filename || ambig(filename))
		{
		return (FALSE);
		}

	// no bad files
	if (!IsFilenameLegal(filename, FALSE))
		{
		CRmPrintfCR(getmsg(1064), filename);
		return (FALSE);
		}

	if (!changedir(GetCurrentRoomDirectory()))
		{
		return (FALSE);
		}

	if (filexists(filename))
		{
		CRmPrintfCR(getmsg(1069), filename);
		changedir(cfg.homepath);
		return (FALSE);
		}

	return (TRUE);
	}


// --------------------------------------------------------------------------
// CheckDirectoryRoom(): returns TRUE if directory, else -- not dir room.

Bool CheckDirectoryRoom(void)
	{
	if (!IsRoomDirectory(TI()thisRoom))
		{
		if (TI()CurrentUser->IsExpert())
			{
			mPrintf(sqst);
			}
		else
			{
			CRCRmPrintfCR(getmsg(215), cfg.Lroom_nym);
			}

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


// --------------------------------------------------------------------------
// entertextfile(): Menu level .et.

void entertextfile(Bool HideIt)
	{
	label filename;
	char comments[64];

	SetDoWhat(ENTERTEXT);

	if (!TI()CurrentUser->IsExpert())
		{
		dispBlb(B_TEXTUP);
		}

	getNormStr(getmsg(1073), filename, LABELSIZE, ECHO);

	if (CanUploadFile(filename))
		{
		getString(getmsg(1061), comments, 63, FALSE, TRUE, ns);

		textup(filename, HideIt);

		if (changedir(GetCurrentRoomDirectory()) && filexists(filename))
			{
			UpdateInfoFile(GetCurrentRoomDirectory(), filename,
					TI()CurrentUser->GetName(), comments, 0, 0);

			Message *Msg = new Message;

			if (Msg)
				{
				if (*comments)
					{
					Msg->SetTextWithFormat(getmsg(450), filename,
							TI()CurrentUser->GetName(), bn, comments);
					}
				else
					{
					Msg->SetTextWithFormat(getmsg(449), filename,
							TI()CurrentUser->GetName());
					}

				Msg->SetRoomNumber(TI()thisRoom);
				systemMessage(Msg);

				delete Msg;
				}
			else
				{
				mPrintf(getmsg(188), getmsg(631));
				}
			}
		}

	changedir(cfg.homepath);
	}

static const char *GetInternalProtocolName(InternalProtocols prot)
	{
	switch (prot)
		{
		case IP_XMODEM:
			{
			return (getmsg(632));
			}

		case IP_CRCXMODEM:
			{
			return (getmsg(633));
			}

		case IP_ONEKXMODEM:
			{
			return (getmsg(634));
			}

		case IP_ZMODEM:
			{
			return (getmsg(635));
			}

		default:
			{
			return (getmsg(636));
			}
		}
	}


// --------------------------------------------------------------------------
// enterwc(): Menu level .ew HIGH level routine.

void enterwc(Bool HideIt)
	{
	label filename;

	const InternalProtocols prot = XmodemMenu('E');

	if (prot == IP_ERROR)
		{
		return;
		}

	SetDoWhat(ENTERWC);

	if (!TI()CurrentUser->IsExpert())
		{
		dispBlb(B_WCUP);
		}

	doCR();
	if (prot != IP_ZMODEM)
		{
		getNormStr(getmsg(1073), filename, LABELSIZE, ECHO);

		if (!CanUploadFile(filename))
			{
			return;
			}
		}

	if (prot == IP_ZMODEM || *filename)
		{
		FillFileInfo(GetCurrentRoomDirectory(), ns, FALSE, NULL, FALSE);

		wcup(filename, prot);

		char TrapString[128];
		sprintf(TrapString, getmsg(972), GetInternalProtocolName(prot),
				GetRoomName(TI()thisRoom));
		trap(TrapString, T_UPLOAD);

		Message *Msg = new Message;

		if (Msg)
			{
			if (FillFileInfo(GetCurrentRoomDirectory(),
					TI()CurrentUser->GetName(), TRUE, Msg->GetTextPointer(),
					HideIt))
				{
				Msg->SetRoomNumber(TI()thisRoom);
				systemMessage(Msg);
				}

			delete Msg;
			}
		else
			{
			FillFileInfo(GetCurrentRoomDirectory(),
					TI()CurrentUser->GetName(), TRUE, NULL, HideIt);
			}
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// readdirectory(): Menu level .rd HIGH level routine.

void readdirectory(OldNewPick which)
	{
	label filename;
	long total_bytes = 0l;

	SetDoWhat(READDIR);

	doEvent(EVT_READDIR);

	doCR();
	if (!getNormStr(getmsg(1073), filename, LABELSIZE, ECHO))
		{
		return;
		}

	if (!*filename)
		{
		strcpy(filename, starstar);
		}
	else if (!strchr(filename, '.'))
		{
		strcat(filename, getmsg(1429));
		}
	else if (*filename == '.')
		{
		label filename2;
		CopyStringToBuffer(filename2, filename, sizeof(filename2) - 1);
		*filename = '*';
		CopyStringToBuffer(filename + 1, filename2, sizeof(filename) - 2);
		}

	if (changedir(GetCurrentRoomDirectory()))
		{
		if (which != OldAndNew)
			{
			if (!getNewOldFileDate())
				{
				return;
				}
			}

		SetOutOK();

		// load our directory structure according to filename
		directoryinfo *files = filldirectory(filename, TI()MRO.Date ? 2 : 1,
				which, TI()MRO.Reverse);

		if (files && files[0].name[0])
			{
			int i;
			int entries = 0;
			char dstamp[10];

			doCR();
			if (TI()MRO.Verbose)
				{
				mPrintf(getmsg(459), (TI()CurrentUser->GetWidth() > 41) ?
							spcspc : ns,
						(TI()CurrentUser->GetWidth() > 45) ?
							getmsg(1678) : getmsg(1677)
						);
				}

			for (entries = 0; files[entries].name[0]; ++entries);

			for (i = 0;

					(i < entries &&
						(TI()UserControl.GetOutFlag() != OUTSKIP) &&
						!TI()UserControl.CheckInput(FALSE));

					++i)
				{
				if (TI()MRO.Verbose)
					{
					getdstamp(dstamp, files[i].date);

					if (TI()CurrentUser->GetWidth() > 41)
						{
						CRmPrintf(getmsg(740), files[i].name, dstamp,
								ltoac(files[i].size),
								TI()CurrentUser->GetWidth() > 45 ?
								spcspc : ns, dltime(files[i].size));
						}
					else
						{
						CRmPrintf(getmsg(838), files[i].name, dstamp, files[i].size,
								dltime(files[i].size));
						}

					total_bytes += files[i].size;
					}
				else
					{
					mPrintf(getmsg(837), files[i].name);
					}

				if (TI()MRO.DotoMessage == REVERSE_READ)
					{
					TI()MRO.DotoMessage = NO_SPECIAL;
					TI()UserControl.SetOutFlag(OUTOK);

					TI()MRO.Reverse = !TI()MRO.Reverse;
					qsort(files, entries, sizeof(*files),
							(TI()MRO.Reverse) ? (QSORT_CMP_FNP) revstrcmp :
							(QSORT_CMP_FNP) fwdstrcmp);

					mPrintf(getmsg(1017), (!TI()MRO.Reverse) ? '+' : '-');
					if (!TI()MRO.Verbose)
						{
						doCR();
						}

					i = entries - (i + 1);
					}
				}

			if (TI()MRO.Verbose && TI()UserControl.GetOutFlag() != OUTSKIP)
				{
				label Files, Bytes;

				strcpy(Bytes, ltoac(total_bytes));
				strcpy(Files, ltoac(i));

				doCR();
				CRmPrintf(getmsg(637), Bytes, (total_bytes == 1) ? ns : justs,
						Files, (i == 1) ? ns : justs, ltoac(bytesfree()));
				}

			doCR();
			}
		else
			{
			CRmPrintfCR(getmsg(1050), filename);
			}

		delete [] files;
		}
	else
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// readtextfile(): Menu level .rt HIGH level routine.

void readtextfile(OldNewPick which)
	{
	label filename;

	SetDoWhat(READTEXT);

	doCR();
	getNormStr(getmsg(1073), filename, 13, ECHO);

	if (*filename)
		{
		if (!strchr(filename, '.'))
			{
			strcat(filename, getmsg(1429));
			}

		textdown(filename, which);
		}
	}


// --------------------------------------------------------------------------
// readwc(): Menu level .rw HIGH level routine.

void readwc(void)
	{
	char filename[80];

	const InternalProtocols prot = XmodemMenu('R');
	if (prot == IP_ERROR)
		{
		return;
		}

	SetDoWhat(READWC);

	if (!TI()CurrentUser->IsExpert())
		{
		dispBlb(B_WCDOWN);
		}

	doCR();
	getNormStr(getmsg(1073), filename, 79, ECHO);

	if (*filename)
		{
		wcdown(filename, prot);
		}
	}


// --------------------------------------------------------------------------
// renamefile(): Aide fn to rename a file.

void renamefile(void)
	{
	label source, destination;

	SetDoWhat(AIDERENAME);

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	doCR();
	getNormStr(getmsg(1063), source, LABELSIZE, ECHO);
	if (!*source)
		{
		changedir(cfg.homepath);
		return;
		}

	if (!IsFilenameLegal(source, FALSE) || ambig(source))
		{
		CRmPrintfCR(getmsg(1064), source);
		changedir(cfg.homepath);
		return;
		}

	if (!filexists(source))
		{
		CRmPrintfCR(getmsg(1050), source);
		changedir(cfg.homepath);
		return;
		}

	getNormStr(getmsg(451), destination, LABELSIZE, ECHO);
	if (!*destination)
		{
		changedir(cfg.homepath);
		return;
		}

	if (!IsFilenameLegal(destination, FALSE) || ambig(destination))
		{
		CRmPrintfCR(getmsg(1064), destination);
		changedir(cfg.homepath);
		return;
		}

	if (filexists(destination))
		{
		CRmPrintfCR(getmsg(1069), destination);
		changedir(cfg.homepath);
		return;
		}

	// if successful
	if (rename(source, destination) == 0)
		{
		fInfo FileInfo;

		if (!GetSingleFileInfo(GetCurrentRoomDirectory(), source,
				&FileInfo))
			{
			memset(&FileInfo, 0, sizeof(FileInfo));
			}

		UpdateInfoFile(GetCurrentRoomDirectory(), destination,
				FileInfo.uploader, FileInfo.comment, FileInfo.downloads,
				FileInfo.uploadtime);

		RemoveFileInfo(GetCurrentRoomDirectory(), source);

		char TrapString[128];

		sprintf(TrapString, getmsg(452), source, destination,
				GetRoomName(TI()thisRoom), TI()CurrentUser->GetName());
		trap(TrapString, T_AIDE);

		Message *Msg = new Message;

		if (Msg)
			{
			Msg->SetText(TrapString);

			if (IsRoomGroupOnly(TI()thisRoom))
				{
				if (IsRoomMultiGroup(TI()thisRoom))
					{
					Msg->SetGroup(GroupData->GetEntry(1)->GetName());
					}
				else
					{
					Msg->SetGroup(GroupData->GetEntry(
							GetRoomGroupNumber(TI()thisRoom))->GetName());
					}
				}
			Msg->SetRoomNumber(AIDEROOM);
			systemMessage(Msg);

			Msg->ClearAll();
			Msg->SetText(TrapString);

			Msg->SetRoomNumber(TI()thisRoom);
			systemMessage(Msg);

			delete Msg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(639));
			}
		}
	else
		{
		CRmPrintfCR(getmsg(80), source);
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// textdown(): Dumps a host file with no formatting.
//
// Notes:
//	This routine handles wildcarding of text downloads.

static void textdown(const char *filename, OldNewPick which)
	{
	int retval = 0;

	TI()UserControl.SetOutFlag(OUTOK);

	// no bad files
	if (!IsFilenameLegal(filename, FALSE))
		{
		CRmPrintf(getmsg(1050), filename);
		return;
		}

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintf(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	if (ambig(filename))
		{
		if (which != OldAndNew)
			{
			if (!getNewOldFileDate())
				{
				return;
				}

			doCR();
			}

		directoryinfo *files = filldirectory(filename, TI()MRO.Date ? 2 : 1,
				which, TI()MRO.Reverse);

		if (files)
			{
			// print out all the files
			int i;
			for (i = 0; files[i].name[0] && (retval != CERROR); i++)
				{
				retval = dumpf(files[i].name, TI()MRO.Verbose, FALSE);
				}

			if (!i)
				{
				CRmPrintf(getmsg(1050), filename);
				}

			delete [] files;
			}

		doCR();
		}
	else
		{
		if (dumpf(filename, TI()MRO.Verbose, FALSE) != CERROR)
			{
			doCR();
			}
		}

	char TrapString[256];
	sprintf(TrapString, getmsg(460), filename, GetRoomName(TI()thisRoom));
	trap(TrapString, T_DOWNLOAD);

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// textup(): Handles textfile uploads.

static void textup(const char *filename, Bool HideIt)
	{
	FILE *upfd;

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	doCR();

	if ((upfd = fopen(filename, FO_W)) == NULL)
		{
		CRmPrintfCR(getmsg(8), filename);
		}
	else
		{
		int i;

		while (((i = iChar()) != 26)  // CTRL+Z
				&& TI()UserControl.GetOutFlag() != OUTSKIP
				&& (HaveConnectionToUser()))
			{
			fputc(i, upfd);
			}

		fclose(upfd);

		doCR();
		if (!getYesNo(getmsg(528), 1))
			{
			unlink(filename);
			}
		else
			{
			if (HideIt && filexists(filename))
				{
				hide(filename);
				}

			char TrapString[256];
			sprintf(TrapString, getmsg(461), filename,
					GetRoomName(TI()thisRoom));

			trap(TrapString, T_UPLOAD);
			}
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// unlinkfile(): Handles .au aide unlink.

void unlinkfile(void)
	{
	char filename[128];

	SetDoWhat(AIDEUNLINK);

	doCR();

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	GetStringWithBlurb(TI()CurrentUser->IsSysop() ? getmsg(710) : getmsg(1073),
			filename, TI()CurrentUser->IsSysop() ? 127 : 12, ns, B_UNLINK);
	normalizeString(filename);

	if (!*filename)
		{
		changedir(cfg.homepath);
		return;
		}

	if ((ambig(filename) || strchr(filename, ' ')) &&
			!TI()CurrentUser->IsSysop())
		{
		CRmPrintfCR(getmsg(793));
		changedir(cfg.homepath);
		return;
		}

	strupr(filename);

	amPrintf(getmsg(1684), cfg.Lroom_nym, GetRoomName(TI()thisRoom),
			TI()CurrentUser->GetName());

	int FilesUnlinked = 0;
	Bool Continuous = FALSE, Abort = FALSE;

	char *words[128];
	const int count = parse_it(words, filename);

	for (int DelIndex = 0; !Abort && DelIndex < count; DelIndex++)
		{
		directoryinfo *files = filldirectory(words[DelIndex], 1, OldAndNew,
				FALSE);

		if (files)
			{
			if (*files[0].name)
				{
				for (int DelIndex2 = 0; *files[DelIndex2].name; DelIndex2++)
					{
					strupr(files[DelIndex2].name);

					int Response;

					if (Continuous)
						{
						// Force "Yes"
						Response = 0;
						}
					else
						{
						char string[128];
						sprintf(string, getmsg(1388), files[DelIndex2].name);

						Response = GetOneKey(string, getmsg(1596),
								getmsg(1596)[0], B_YNAC, getmsg(521),
								getmsg(522), getmsg(653), getmsg(1597));

						TI()UserControl.SetOutFlag(IMPERVIOUS);
						}

					if (Response == -1 || Response == 2)	// ESC or Abort
						{
						Abort = TRUE;
						break;
						}

					if (Response == 0 || Response == 3) // Yes or Continuous
						{
						Bool Success = (unlink(files[DelIndex2].name) == 0);

						if (!Continuous)
							{
							doCR();
							}

						mPrintfCR(getmsg(1598), ns, ns, files[DelIndex2].name,
								Success ? ns : getmsg(1599));

						amPrintf(getmsg(1598), bn, spc, files[DelIndex2].name,
								Success ? ns : getmsg(1599));

						FilesUnlinked += Success;
						}

					if (Response == 3)	// Continuous
						{
						Continuous = TRUE;
						}

					}
				}
			else
				{
				CRmPrintfCR(getmsg(1050), words[DelIndex]);
				}

			delete [] files;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(640));
			}
		}

	CRmPrintfCR(getmsg(429), ltoac(FilesUnlinked), FilesUnlinked == 1 ?
			ns : justs);

	//ReIndexFileInfo();	// We'd rather have speed than size

	if (FilesUnlinked)
		{
		char TrapString[256];

		sprintf(TrapString, getmsg(462), FilesUnlinked == 1 ? ns : justs,
				filename, GetRoomName(TI()thisRoom),
				TI()CurrentUser->GetName());

		trap(TrapString, T_AIDE);

		SaveAideMess
				(
				IsRoomGroupOnly(TI()thisRoom) ?
					(
					IsRoomMultiGroup(TI()thisRoom) ?
						(
						GroupData->GetEntry(1)->GetName()
						)
					:
						(
						GroupData->GetEntry(
							GetRoomGroupNumber(TI()thisRoom))->GetName()
						)
					)
				:
					(
					NULL
					)
				);
		}
	else
		{
		amZap();
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// wcdown() calls xmodem downloading routines.
//		0=wc, 1=wx

static void wcdown(const char *filename, InternalProtocols prot)
	{
	long transTime1, transTime2;

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	if (prot == IP_ZMODEM)
		{
		int count;
		char *words[256];
		char temp[256];

		CopyStringToBuffer(temp, filename, sizeof(temp) - 1);

		count = parse_it(words, temp);

		if (count == 0)
			{
			changedir(cfg.homepath);
			return;
			}

		for (int i = 0; i < count; i++)
			{
			if (!IsFilenameLegal(words[i], FALSE) ||
					!filexists(words[i]) && !ambig(words[i]))
				{
				CRmPrintfCR(getmsg(1050), words[i]);
				changedir(cfg.homepath);
				return;
				}
			}

		int total_files = 0;
		long total_bytes = 0;

		// Disambiguate
		for (i = 0; i < count; i++)
			{
			directoryinfo *files = filldirectory(words[i], 0,
					OldAndNew, FALSE);

			if (files)
				{
				for (int j = 0;
						files[j].name[0]
						&& TI()UserControl.GetOutFlag() == OUTOK;
						j++)
					{
					total_files++;
					total_bytes += files[j].size;

					blocks(files[j].name, files[j].size, 0);
					}

				delete [] files;
				}
			}

		TI()UserControl.SetOutFlag(OUTOK);

		if (total_files > 1)
			{
			CRmPrintfCR(getmsg(641), ltoac(total_bytes));
			mPrintfCR(getmsg(642), dltime(total_bytes));
			}
		}
	else
		{
		if (!IsFilenameLegal(filename, FALSE) || !filexists(filename))
			{
			CRmPrintfCR(getmsg(1050), filename);
			changedir(cfg.homepath);
			return;
			}

		if (ambig(filename))
			{
			CRmPrintfCR(getmsg(471));
			changedir(cfg.homepath);
			return;
			}

		// display # blocks & download time
		FILE *stream = fopen(filename, FO_R);
		const long length = filelength(fileno(stream));
		fclose(stream);

		blocks(filename, length, (prot == 2) ? 1024 : 128);
		}

	if (getYesNo(getmsg(463), 0))
		{
		// later to be replaced by my own xmodem routines
		time(&transTime1);
		doCR();

		if (prot == IP_ZMODEM)
			{
			Zmodem_Send_Batch(filename, (uint) connectbauds[TI()ModemSpeed]);
			}
		else
			{
			xsend(filename, prot);
			}

		time(&transTime2);

		if (cfg.accounting && TI()CurrentUser->IsAccounting() &&
				!TI()CurrentUserAccount->IsSpecialTime())
			{
			TI()CurrentUserAccount->AfterTransfer(transTime1, transTime2,
					FALSE);
			}

		char TrapString[256];

		if (prot == IP_ZMODEM)
			{
			sprintf(TrapString, getmsg(1605), filename,
					GetRoomName(TI()thisRoom));
			}
		else
			{
			sprintf(TrapString, getmsg(464), filename,
					GetRoomName(TI()thisRoom));
			}

		trap(TrapString, T_DOWNLOAD);
		}

	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// wcup(): Calls xmodem uploading routines.

static void wcup(const char *filename, InternalProtocols prot)
	{
	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	if (getYesNo(getmsg(463), 0))
		{
		long transTime1, transTime2;

		// when did they start the Upload
		time(&transTime1);
		doCR();

		if (prot == IP_ZMODEM)
			{
			Zmodem_Receive_Batch(ns, (uint) connectbauds[TI()ModemSpeed]);
			}
		else
			{
			xreceive(filename, prot);
			}

		time(&transTime2);

		if (cfg.accounting && TI()CurrentUser->IsAccounting() &&
				!TI()CurrentUserAccount->IsSpecialTime())
			{
			TI()CurrentUserAccount->AfterTransfer(transTime1, transTime2,
					TRUE);
			}

		TI()SerialPort.Flush();

		char TrapString[256];

		if (prot == IP_ZMODEM)
			{
			sprintf(TrapString, getmsg(1604), GetRoomName(TI()thisRoom));
			}
		else
			{
			sprintf(TrapString, getmsg(465), filename,
					GetRoomName(TI()thisRoom));
			}

		trap(TrapString, T_UPLOAD);
		}

	changedir(cfg.homepath);
	}

void doTheDownload(const char *filename, const char *thePath, Bool fullPath,
		const protocols *theProt, Bool queue)
	{
	long transTime1, transTime2;
	char TrapString[256];

	const int c = toupper(DoMenuPrompt(getmsg(466), NULL));

	if (c == 'D' || c == 'H' || c == CR || c == LF || c == 'Y')
										// old prompt compatible
		{
		if (c == 'H')
			{
			mPrintf(getmsg(467));
			}

		mPrintfCR(getmsg(212));

		if (!fullPath && !queue)
			{
			FillFileInfo(GetCurrentRoomDirectory(), ns, FALSE, NULL, FALSE);
			}

		time(&transTime1);

		wxsnd(thePath, filename, theProt, queue ? 1 : 0);

		time(&transTime2);

		if (fullPath)
			{
			sprintf(TrapString, getmsg(469), theProt->name, filename,
					GetRoomName(TI()thisRoom));
			}
		else if (queue)
			{
			sprintf(TrapString, getmsg(1592), theProt->name,
					GetRoomName(TI()thisRoom));
			}
		else
			{
			Message *Msg = new Message;

			if (Msg)
				{
				if (FillFileInfo(GetCurrentRoomDirectory(),
						TI()CurrentUser->GetName(), TRUE,
						Msg->GetTextPointer(), FALSE))
					{
					Msg->SetRoomNumber(TI()thisRoom);
					systemMessage(Msg);

					sprintf(TrapString, getmsg(972), theProt->name,
							GetRoomName(TI()thisRoom));

					trap(TrapString, T_UPLOAD);
					}

				delete Msg;
				}
			else
				{
				if (FillFileInfo(GetCurrentRoomDirectory(),
						TI()CurrentUser->GetName(), TRUE, NULL, FALSE))
					{
					sprintf(TrapString, getmsg(972), theProt->name,
							GetRoomName(TI()thisRoom));

					trap(TrapString, T_UPLOAD);
					}
				}

			sprintf(TrapString, getmsg(468), theProt->name, filename);
			}

		trap(TrapString, T_DOWNLOAD);

		if (cfg.accounting && TI()CurrentUser->IsAccounting() &&
					!TI()CurrentUserAccount->IsSpecialTime())
			{
			TI()CurrentUserAccount->AfterTransfer(transTime1, transTime2,
					FALSE);
			}

		changedir(thePath);

		if (c == 'H')
			{
			time_t t;
			time(&t);
			int i;
			int ch = -1;
			Bool do_hangup = TRUE;

			TI()UserControl.SetOutFlag(IMPERVIOUS);

			CRCRmPrintfCR(getmsg(470));

			do
				{
				i = (char) (20L - (time(NULL) - t));

				if (i != ch)
					{
					if (cfg.countbeep)
						{
						mPrintf("\a");
						}

					mPrintf(i ? getmsg(836) : getmsg(500), i);
					ch = i;
					}

				if (BBSCharReady())
					{
					ch = toupper(iCharNE());
					do_hangup = (ch == 'H');
					break;
					}
				} while (i);

			doCR();

			if (do_hangup)
				{
				TI()MRO.Verbose = FALSE;
				terminate(TRUE);
				}
			}
		}
	else
		{
		mPrintfCR(getmsg(653));
		}
	}


// --------------------------------------------------------------------------
// download(): Menu level download routine.

void download(char c)
	{
	int ch, ich = 0;

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	if (!c)
		{
		ch = tolower(ich = iCharNE());
		}
	else
		{
		ch = c;
		}

	if (ch == '\n' || ch == '\r')
		{
		ch = TI()CurrentUser->GetDefaultProtocol();
		}

	const protocols *theProt = GetProtocolByKey(ch);

	if (theProt)
		{
		char filename[81];
		int i;

		mPrintfCR(pcts, theProt->name);

		if (!TI()CurrentUser->IsExpert())
			{
			dispBlb(B_WCDOWN);
			changedir(GetCurrentRoomDirectory());	// it was okay above
			}

		getNormStr(getmsg(1073), filename, (theProt->batch) ? 80 : 12, ECHO);

		if (!*filename)
			{
			changedir(cfg.homepath);
			return;
			}

		if (theProt->batch)
			{
			char temp[81];
			char *words[256];

			CopyStringToBuffer(temp, filename, sizeof(temp) - 1);

			const int count = parse_it(words, temp);

			if (count == 0)
				{
				return;
				}

			for (i = 0; i < count; i++)
				{
				if (!IsFilenameLegal(words[i], FALSE) ||
						!ambig(words[i]) && !filexists(words[i]))
					{
					CRmPrintfCR(getmsg(1050), words[i]);
					changedir(cfg.homepath);
					return;
					}
				}

			// Disambiguate
			int total_files = 0;
			long total_bytes = 0l;
			for (i = 0; i < count; i++)
				{
				directoryinfo *files = filldirectory(words[i], 0,
						OldAndNew, FALSE);

				if (files)
					{
					for (int j = 0; files[j].name[0] &&
							TI()UserControl.GetOutFlag() == OUTOK; j++)
						{
						total_files++;
						total_bytes += files[j].size;

						blocks(files[j].name, files[j].size, theProt->block);
						}

					if (!j)
						{
						CRmPrintfCR(getmsg(1050), words[i]);
						}

					delete [] files;
					}
				}
			TI()UserControl.SetOutFlag(OUTOK);

			if (total_files > 1)
				{
				CRmPrintfCR(getmsg(641), ltoac(total_bytes));
				mPrintfCR(getmsg(642), dltime(total_bytes));
				}
			else if (!total_files)
				{
				changedir(cfg.homepath);
				return;
				}
			}
		else
			{
			if (!IsFilenameLegal(filename, FALSE) || !filexists(filename))
				{
				CRmPrintfCR(getmsg(1050), filename);
				changedir(cfg.homepath);
				return;
				}

			if (ambig(filename))
				{
				CRmPrintfCR(getmsg(471));
				changedir(cfg.homepath);
				return;
				}

			// display # blocks & download time
			FILE *stream = fopen(filename, FO_R);
			const long length = filelength(fileno(stream));
			fclose(stream);

			blocks(filename, length, theProt->block);
			}

		doTheDownload(filename, GetCurrentRoomDirectory(), FALSE, theProt,
				FALSE);
		}
	else
		{
		if (ch == '?')
			{
			oChar('?');
			upDownMnu('D', FALSE);
			}
		else
			{
			oChar(ich);
			mPrintf(sqst);
			if (!TI()CurrentUser->IsExpert())
				{
				upDownMnu('D', FALSE);
				}
			}
		}

	TI()SerialPort.Flush();
	changedir(cfg.homepath);
	}


// --------------------------------------------------------------------------
// upload(): Menu level routine.

void upload(char c, Bool HideIt)
	{
	int ch, ich;

	if (!c)
		{
		ch = tolower(ich = iCharNE());
		}
	else
		{
		ch = ich = c;
		}

	if (ch == '\n' || ch == '\r')
		{
		ch = (!TI()CurrentUser->GetDefaultProtocol()) ?
				0 : TI()CurrentUser->GetDefaultProtocol();
		}

	const protocols *theProt = GetProtocolByKey(ch);

	if (theProt)
		{
		mPrintfCR(pcts, theProt->name);

		if (!TI()CurrentUser->IsExpert())
			{
			dispBlb(B_WCUP);
			}

		changedir(GetCurrentRoomDirectory());
		const long freespace = bytesfree();

		if (freespace <= cfg.diskfree)
			{
			CRmPrintfCR(getmsg(996), ltoac(freespace / 1024l));
			return;
			}

		CRmPrintfCR(getmsg(472), ltoac(bytesfree()));

		ActuallyDoTheUpload(theProt, HideIt, TRUE);
		}
	else
		{
		if (ch == '?')
			{
			oChar('?');
			upDownMnu('U', FALSE);
			}
		else
			{
			oChar(ich);
			mPrintf(sqst);
			if (!TI()CurrentUser->IsExpert())
				{
				upDownMnu('U', FALSE);
				}
			}
		}

	changedir(cfg.homepath);
	}

void ActuallyDoTheUpload(const protocols *theProt, Bool HideIt, Bool Ask)
	{
	char comments[64];
	label filename;

	if (!theProt->batch)
		{
		getNormStr(getmsg(1073), filename, LABELSIZE, ECHO);

		if (!CanUploadFile(filename))
			{
			changedir(GetCurrentRoomDirectory());
			return;
			}

		getString(getmsg(1061), comments, 63, FALSE, ECHO, ns);
		}
	else
		{
		FillFileInfo(GetCurrentRoomDirectory(), ns, FALSE, NULL, FALSE);
		}

	if (!Ask || getYesNo(getmsg(463), 0))
		{
		long transTime1, transTime2;

		time(&transTime1);		// when did they start the Upload

		wxrcv(GetCurrentRoomDirectory(), theProt->batch ? ns : filename,
				theProt);

		time(&transTime2);		// when did they get done

		if (cfg.accounting && TI()CurrentUser->IsAccounting() &&
					!TI()CurrentUserAccount->IsSpecialTime())
			{
			TI()CurrentUserAccount->AfterTransfer(transTime1, transTime2,
					TRUE);
			}

		TI()SerialPort.Flush();

		if (!theProt->batch)
			{
			if (HideIt && filexists(filename))
				{
				hide(filename);
				}

			if (filexists(filename))
				{
				UpdateInfoFile(GetCurrentRoomDirectory(), filename,
						TI()CurrentUser->GetName(), comments, 0, 0);

				char TrapString[256];
				sprintf(TrapString, getmsg(473), theProt->name,
						filename, GetRoomName(TI()thisRoom));

				trap(TrapString, T_UPLOAD);

				Message *Msg = new Message;

				if (Msg)
					{
					if (comments[0])
						{
						Msg->SetTextWithFormat(getmsg(450), filename,
								TI()CurrentUser->GetName(), bn, comments);
						}
					else
						{
						Msg->SetTextWithFormat(getmsg(449), filename,
								TI()CurrentUser->GetName());
						}

					Msg->SetRoomNumber(TI()thisRoom);
					systemMessage(Msg);

					delete Msg;
					}
				else
					{
					mPrintf(getmsg(188), getmsg(643));
					}
				}
			}
		else
			{
			char TrapString[256];

			sprintf(TrapString, getmsg(972), theProt->name,
					GetRoomName(TI()thisRoom));

			trap(TrapString, T_UPLOAD);

			Message *Msg = new Message;

			if (Msg)
				{
				if (FillFileInfo(GetCurrentRoomDirectory(),
						TI()CurrentUser->GetName(), TRUE,
						Msg->GetTextPointer(), HideIt))
					{
					Msg->SetRoomNumber(TI()thisRoom);
					systemMessage(Msg);
					}

				delete Msg;
				}
			else
				{
				FillFileInfo(GetCurrentRoomDirectory(),
						TI()CurrentUser->GetName(), TRUE, NULL, HideIt);

				mPrintf(getmsg(188), getmsg(643));
				}
			}
		}
	}

// XmodemMenu
static InternalProtocols XmodemMenu(char cmd)
	{
	int ch, ich;

	ch = toupper(ich = iCharNE());

	if (ch == '\n' || ch == '\r')
		{
		ch = 'X';
		}

	switch (ch)
		{
		case 'C':
			{
			mPrintf(getmsg(633));
			return (IP_CRCXMODEM);
			}

		case '1':
			{
			mPrintf(getmsg(634));
			return (IP_ONEKXMODEM);
			}

		case 'X':
			{
			mPrintf(getmsg(632));
			return (IP_XMODEM);
			}

		case 'Z':
			{
			mPrintf(getmsg(635));
			return (IP_ZMODEM);
			}

		case '?':
			{
			oChar('?');
			XMnu(cmd);
			return (IP_ERROR);
			}

		default:
			{
			oChar(ich);
			mPrintf(sqst);
			if (!TI()CurrentUser->IsExpert())
				{
				XMnu(cmd);
				}

			return (IP_ERROR);
			}
		}
	}

static void XMnu(char cmd)
	{
	if (cmd == 'R')
		{
		showMenu(M_READWC);
		}
	else if (cmd == 'E')
		{
		showMenu(M_ENTERWC);
		}
	}

// Up/Down menu
void upDownMnu(char cmd, Bool respOnly)
	{
	const MenuNames TheMenu = cmd == 'U' ? M_UPLOAD : 
			respOnly ? M_RESPONSEDOWNLOAD : M_DOWNLOAD;

	if (getMnuOff(TheMenu, NULL) > 0)
		{
		showMenu(TheMenu);
		}
	else
		{
		doCR(2);

		for (protocols *theProt = extProtList; theProt;
				theProt = (protocols *) getNextLL(theProt))
			{
			if ((!respOnly || *theProt->respDown) && !theProt->NetOnly)
				{
				mPrintfCR(getmsg(835), cmd, theProt->CommandKey,
						theProt->MenuName);
				}
			}

		mPrintfCR(getmsg(474), cmd);
		}
	}

Bool getNewOldFileDate(void)
	{
	label usrIn;
	datestruct ds;
	struct date f_date;
	struct time f_time;
	Bool first = TRUE;

	do
		{
		if (!HaveConnectionToUser())
			{
			return (FALSE);
			}

		if (!first)
			{
			mPrintfCR(getmsg(1080));
			}

		first = FALSE;

		GetStringWithBlurb(getmsg(475), usrIn, LABELSIZE, ns, B_DATESET);

		gdate(usrIn, &ds);
		} while (!ds.Date);


	f_date.da_year = ds.Year + 1900;	// i hate dostounix
	f_date.da_mon = ds.Month + 1;		// i hate dostounix
	f_date.da_day = ds.Date;

	f_time.ti_min = 0;
	f_time.ti_hour = 0;
	f_time.ti_sec = 0;
	f_time.ti_hund = 0;

	TI()MRO.CheckDate = dostounix(&f_date, &f_time);

	return (TRUE);
	}


// --------------------------------------------------------------------------
// moveFile(): Move a file, copying its file info.

void MoveFile(void)
	{
	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	SetDoWhat(AIDEMOVE);

	doCR();

	label SourceFileName;
	getNormStr(getmsg(1063), SourceFileName, LABELSIZE, ECHO);

	// Source file checking...
	if (!*SourceFileName)
		{
		changedir(cfg.homepath);
		return;
		}

	strupr(SourceFileName);

	if (!IsFilenameLegal(SourceFileName, FALSE) || ambig(SourceFileName))
		{
		CRmPrintfCR(getmsg(1064), SourceFileName);
		changedir(cfg.homepath);
		return;
		}

	if (!filexists(SourceFileName))
		{
		CRmPrintfCR(getmsg(1050), SourceFileName);
		changedir(cfg.homepath);
		return;
		}

	char Prompt[128];
	sprintf(Prompt, getmsg(1065), cfg.Lroom_nym);

	label DestRoomName;
	getNormStr(Prompt, DestRoomName, LABELSIZE, ECHO);

	if (!*DestRoomName)
		{
		changedir(cfg.homepath);
		return;
		}

	r_slot roomNo;

	if ((roomNo = RoomExists(DestRoomName)) == CERROR)
		{
		roomNo = PartialRoomExists(DestRoomName, TI()thisRoom, FALSE);
		}

	if (roomNo == CERROR)
		{
		CRmPrintfCR(getmsg(1066), cfg.Lroom_nym, DestRoomName);
		changedir(cfg.homepath);
		return;
		}

	if (!IsRoomDirectory(roomNo))
		{
		CRmPrintfCR(getmsg(1067), DestRoomName, cfg.Lroom_nym);
		changedir(cfg.homepath);
		return;
		}

	aRoom rBuf;
	getRoom(roomNo, &rBuf);

	char DestinationPath[64];
	CopyStringToBuffer(DestinationPath, rBuf.rbdirname,
			sizeof(DestinationPath) - 1);
	CopyStringToBuffer(DestRoomName, rBuf.rbname, LABELSIZE);

	if (!changedir(DestinationPath))
		{
		CRmPrintfCR(getmsg(1068), DestRoomName);
		changedir(cfg.homepath);
		return;
		}

	// Source file at destination?
	char DestFullFilePath[128];
	sprintf(DestFullFilePath, sbs, DestinationPath, SourceFileName);

	if (filexists(DestFullFilePath))
		{
		CRmPrintfCR(getmsg(1069), DestFullFilePath);
		changedir(cfg.homepath);
		return;
		}

	if (!changedir(GetCurrentRoomDirectory()))
		{
		CRmPrintfCR(getmsg(1068), GetRoomName(TI()thisRoom));
		changedir(cfg.homepath);
		return;
		}

	sprintf(Prompt, getmsg(526), SourceFileName, cfg.Lroom_nym, DestRoomName);

	if (getYesNo(Prompt, 1))
		{
		fInfo FileInfo;
		label Uploader;
		char FileComment[65];
		int Downloads;
		long UploadTime;

		if (rename(SourceFileName, DestFullFilePath) != 0)
			{
			CRmPrintf(getmsg(1070));

			if (copyfile(SourceFileName, DestFullFilePath))
				{
				doCR();
				unlink(SourceFileName);
				}
			else
				{
				CRCRmPrintfCR(getmsg(1071), SourceFileName);
				changedir(cfg.homepath);
				return;
				}
			}

		if (GetSingleFileInfo(GetCurrentRoomDirectory(), SourceFileName,
				&FileInfo))
			{
			CopyStringToBuffer(Uploader, FileInfo.uploader, LABELSIZE);
			CopyStringToBuffer(FileComment, FileInfo.comment,
					sizeof(FileComment) - 1);
			Downloads = FileInfo.downloads;
			UploadTime = FileInfo.uploadtime;
			}
		else
			{
			FileComment[0] = '\0';
			Uploader[0] = '\0';
			Downloads = 0;
			UploadTime = 0;
			}

		UpdateInfoFile(DestinationPath, SourceFileName, Uploader,
				FileComment, Downloads, UploadTime);

		RemoveFileInfo(GetCurrentRoomDirectory(), SourceFileName);

		char TrapString[256];

		sprintf(TrapString, getmsg(1072), SourceFileName, DestRoomName,
				GetRoomName(TI()thisRoom), TI()CurrentUser->GetName());
		trap(TrapString, T_AIDE);

		Message *Msg = new Message;

		if (Msg)
			{
			Msg->SetText(TrapString);
			Msg->SetRoomNumber(AIDEROOM);
			systemMessage(Msg);

			delete Msg;
			}
		else
			{
			mPrintf(getmsg(188), getmsg(644));
			}

		sprintf(Prompt, getmsg(527), cfg.Lmsg_nym, cfg.Lroom_nym);

		if (getYesNo(Prompt, 1))
			{
			Msg = new Message;

			if (Msg)
				{
				long length;
				const int handle = open(DestFullFilePath, O_RDONLY);

				if (handle >= 0)
					{
					length = filelength(handle);
					close(handle);
					}
				else
					{
					length = 0;
					}

				Msg->SetTextWithFormat(getmsg(645), cfg.Lroom_nym, bn,
						SourceFileName, ltoac(length),
						length == 1 ? ns : justs, bn, FileComment);

				Msg->SetRoomNumber(roomNo);
				systemMessage(Msg);

				delete Msg;
				}
			else
				{
				mPrintf(getmsg(188), getmsg(644));
				}
			}
		}

	changedir(cfg.homepath);
	}

protocols *GetProtocolByKey(char Key, Bool Network)
	{
	for (protocols *theProt = extProtList; theProt;
			theProt = (protocols *) getNextLL(theProt))
		{
		if (tolower(Key) == tolower(theProt->CommandKey) &&
				(Network || !theProt->NetOnly))
			{
			break;
			}
		}

	return (theProt);
	}
