/*
Auto:		sc <file>
Auto:		Protect Move P ADD
*/

/* $Revision Header built automatically *************** (do not edit) ************
**
** © Copyright by GuntherSoft
**
** File             : SnakeSYS:CPrgs/Utils/Move.c
** Created on       : Wednesday, 11.08.93 15:01:14
** Created by       : Kai Iske
** Current revision : V37.11
**
**
** Purpose
** -------
**   - Small move utility which supports wildcards and doesn`t use
**     Rename() nor Copy() nor anything similar...
**
** Revision V37.11
** ---------------
** created on Monday, 30.05.94 09:20:04  by  Kai Iske.   LogMessage :
**  -*-  changed on Donnerstag, 12.01.95 13:26:43  by  Kai Iske.   LogMessage :
**   - Changed version numbering to official scheme
**  -*-  changed on Mittwoch, 11.01.95 11:04:23  by  Kai Iske.   LogMessage :
**   - One couldn`t move the entire contents of a directory
**     to a dir within that directory (eg. Move #? Dummy)
**  -*-  changed on Mittwoch, 11.01.95 11:03:55  by  Kai Iske.   LogMessage :
**   - One couldn`t move directories, if the destination dirs
**     already existed (eg. Move Dummy1 Dummy2 t:; if Dummy1
**     and Dummy2 both existed in t:)
**  -*-  changed on Montag, 05.09.94 23:16:13  by  Kai Iske.   LogMessage :
**   - OldWindow and HitMask vars weren`t preset to 0
**     (Reported by : Christian Buchner)
**  -*-  created on Monday, 30.05.94 09:20:04  by  Kai Iske.   LogMessage :
**   - When one issued something like this "Move #? dummy/",
**     Move stopped when trying to move dummy to itself ;) Fixed
**
** Revision V1.10
** --------------
** created on Sunday, 06.03.94 16:57:29  by  Kai Iske.   LogMessage :
**  -*-  created on Sunday, 06.03.94 16:57:29  by  Kai Iske.   LogMessage :
**   - Move didn`t end up some operations with a "moved" string,
**     thus leading to corrupted display
**     (Reported by : Albert-Jan Brouwer)
**   - One still couldn`t rename a directory
**     (Reported by : Albert-Jan Brouwer)
**
** Revision V1.9
** --------------
** created on Saturday, 26.02.94 14:15:24  by  Kai Iske.   LogMessage :
**  -*-  changed on Saturday, 26.02.94 14:29:20  by  Kai Iske.   LogMessage :
**   - On recursive moves, Move will indent the output according to
**     recursion depth. This is for better readability
**  -*-  changed on Saturday, 26.02.94 14:19:22  by  Kai Iske.   LogMessage :
**   - Move will refuse to move a dir to itself now.
**  -*-  created on Saturday, 26.02.94 14:15:24  by  Kai Iske.   LogMessage :
**   - One couldn`t move a dir to another dir on the same device
**
** Revision V1.8
** --------------
** created on Sunday, 19.12.93 01:44:05  by  Kai Iske.   LogMessage :
**  -*-  changed on Sunday, 19.12.93 01:47:54  by  Kai Iske.   LogMessage :
**   - Added PURE information to Doc, since Move really is pure
**     (Suggested by : Albert-Jan Brouver)
**  -*-  changed on Sunday, 19.12.93 01:46:23  by  Kai Iske.   LogMessage :
**   - Due to the move-renaming bug one couldn`t rename a dir
**     an Assign was put on.
**     (Reported by : Albert-Jan Brouver)
**  -*-  created on Sunday, 19.12.93 01:44:05  by  Kai Iske.   LogMessage :
**   - Fixed move-renaming of directories.
**     (Reported by : Albert-Jan Brouver)
**   - Move had some severe problems recursively moving
**     the contents of a directory
**
** Revision V1.7
** --------------
** created on Saturday, 04.12.93 23:19:03  by  Kai Iske.   LogMessage :
**  -*-  changed on Saturday, 04.12.93 23:26:25  by  Kai Iske.   LogMessage :
**   - Error while moving to a "device full" disk
**     (Reported by : Chris Conger)
**  -*-  created on Saturday, 04.12.93 23:19:03  by  Kai Iske.   LogMessage :
**   - FileName length-bug fixed
**     (Reported by : Juergen Lang)
**   - Recompiled using SAS 6.50
**   - Stack usage reduced
**
** Revision V1.6
** --------------
** created on Friday, 15.10.93 15:51:13  by  Kai Iske.   LogMessage :
**  -*-  changed on Saturday, 16.10.93 15:17:29  by  Kai Iske.   LogMessage :
**   - Move got screwed up, when attempting to move a
**     zero size file
**  -*-  changed on Friday, 15.10.93 16:03:35  by  Kai Iske.   LogMessage :
**   - Added Buffer Keyword to specify the maximum buffer
**     size for an inter device move. Normally Move would have used
**     the max amount of available free memory in order to
**     accomplish the move, or the size needed by the file...
**     (Suggested by : Christof Damian)
**  -*-  created on Friday, 15.10.93 15:51:13  by  Kai Iske.   LogMessage :
**   - One couldn`t rename a file using move, because the
**     "destination" file was removed before doing the rename,
**     ie. the source file was deleted.
**     (Reported by : Chris Conger)
**
** Revision V1.5
** --------------
** created on Tuesday, 05.10.93 01:42:53  by  Kai Iske.   LogMessage :
**  -*-  changed on Tuesday, 05.10.93 02:03:22  by  Kai Iske.   LogMessage :
**   - Freed the FileInfoBlock _BEFORE_ setting the Bits, Comment
**     etc. This lead to MungWall hits (of course it did)
**     (Reported by Nico Francois)
**  -*-  created on Tuesday, 05.10.93 01:42:53  by  Kai Iske.   LogMessage :
**   - Exidentially called one ExAll() without ED_SIZE
**     This lead to some calls to DoTheMove without the Size set ;)
**     (Reported by  Karsten Weiss)
**
** Revision V1.4
** --------------
** created on Friday, 01.10.93 16:55:15  by  Kai Iske.   LogMessage :
**  -*-  created on Friday, 01.10.93 16:55:15  by  Kai Iske.   LogMessage :
**   - Now uses Rename() when source and dest are on the same
**     device. This is faster ;) Why haven`t I thought of that
**     earlier ??? ;)
**
** Revision V1.3
** --------------
** created on Wednesday, 25.08.93 16:32:43  by  Kai Iske.   LogMessage :
**   - UnLock Problem solved when doing recursive Dir-Moves
**     This bug lead to "Object in use" error messages
**
** Revision V1.2
** --------------
** created on Tuesday, 17.08.93 22:48:38  by  Kai Iske.   LogMessage :
**   - Added CTRL-C checking
**   - Added FORCE Flag in order to move read/deleteprotected or
**     to overwrite a delete/writeprotected files
**
** Revision V1.1
** --------------
** created on Monday, 16.08.93 22:53:34  by  Kai Iske.   LogMessage :
**   - Added well known CLONE, DATES, COM, NOPRO,
**     QUIET and NOREQ options also used by e.g. Rename
**
** Revision V1.0
** --------------
** created on Wednesday, 11.08.93 15:01:14  by  Kai Iske.   LogMessage :
**     --- Initial release ---
**
*********************************************************************************/
#define REVISION "37.11"
#define REVDATE  "12.01.95"
#define REVTIME  "13:26:43"
#define AUTHOR   "Kai Iske"
#define VERNUM   37
#define REVNUM   11

#include	<string.h>
#include	<stdlib.h>
#include	<exec/types.h>
#include	<proto/exec.h>
#include	<proto/dos.h>
#include	<exec/memory.h>
#include	<exec/execbase.h>
#include	<dos/exall.h>
#include	<dos/dos.h>
#include	<dos/dosextens.h>





/**********************************************************************/
/*                         Defines for Flags                          */
/**********************************************************************/
#define	CLONE		0x0001
#define	DATES		0x0002
#define	NOPRO		0x0004
#define	COM		0x0008
#define	QUIET		0x0010
#define	FORCE		0x0020

#define	WATCHSIGS	SIGBREAKF_CTRL_C



/**********************************************************************/
/*                      Static char definitions                       */
/**********************************************************************/
static	const char	*Version	= "$VER: Move "REVISION" ("REVDATE")\0";
static	const char	*Template	= "FROM/M,TO/A,CLONE/S,DATES/S,NOPRO/S,COM/S,QUIET/S,NOREQ/S,FORCE/S,BUFFER/N/K";
enum	{FROM_ARG, TO_ARG, CLONE_ARG, DATES_ARG, NOPRO_ARG, COM_ARG, QUIET_ARG, NOREQ_ARG, FORCE_ARG, BUFFER_ARG, LAST_ARG};



/**********************************************************************/
/*                             Prototypes                             */
/**********************************************************************/
int		__saveds DoMove(void);
static	BOOL	__stdargs DoTheMove(char *FromFile, char *ToPath, BOOL PatternFrom, BPTR OutHandle, LONG Type, ULONG Size, UWORD CopyFlags, LONG *HitMask, ULONG MaxBuffer, ULONG RecDepth);
static	BOOL	__stdargs DoMoveFile(char *FromFile, char *ToPath, char *Buffer, BPTR OutHandle, ULONG Size, UWORD CopyFlags, LONG *HitMask, ULONG MaxBuffer);



/**********************************************************************/
/*                           Library bases                            */
/**********************************************************************/
struct	DosLibrary	*DOSBase	= NULL;



/**********************************************************************/
/*                          The main program                          */
/**********************************************************************/
int __saveds DoMove(void)
{
	struct	FileInfoBlock	*FIB;
	struct	ExecBase	*SysBase	= *((struct ExecBase **)0x4L);
	struct	Process		*MyProc		= (struct Process *)SysBase->ThisTask;
	struct	RDArgs		*RDArgs;
	struct	ExAllControl	*EAC;
	struct	ExAllData	*EAB, *EAD;
	APTR			*Args,
				*OldWindow	= NULL;
	BPTR			DirLock,
				OutHandle;
	char			**FromPtr;
	char			*ToPtr;
	char			*Pattern;
	char			*PathName	= NULL;
	char			*Buffer		= NULL;
	LONG			HitMask		= 0;
	UWORD			NumFrom		= 0,
				PatternType,
				CopyFlags	= 0;
	ULONG			MaxBuffer	= 0;
	BOOL			PatternFrom,
				GoOn		= FALSE,
				Scanning;




		// Ignore startup from WB

	if(!(MyProc->pr_CLI))
	{
		struct	Message	*MyMsg;

		WaitPort(&MyProc->pr_MsgPort);
		MyMsg = GetMsg(&MyProc->pr_MsgPort);
		Disable();
		ReplyMsg(MyMsg);
		return(10);
	}

		// Do the wild thing

	if((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)))
	{
			// Allocate buffers

		if((Pattern = AllocVec(1024, MEMF_CLEAR)) && (PathName = AllocVec(1024, MEMF_CLEAR)) && (Buffer = AllocVec(1024, MEMF_CLEAR)))
		{
			if((FIB = AllocVec(sizeof(struct FileInfoBlock), MEMF_CLEAR)))
			{
					// Get buffer for ReadArgs()

				if((Args = AllocVec((LAST_ARG * sizeof(ULONG)), MEMF_CLEAR)))
				{
						// Get structure for ExAll()

					if((EAC = AllocDosObject(DOS_EXALLCONTROL, NULL)))
					{
							// Get buffer for ExAll()

						if((EAB = AllocVec((sizeof(struct ExAllData)*20), MEMF_CLEAR)))
						{
								// Parse commandline

							if((RDArgs = ReadArgs((char *)Template, (LONG *)Args, NULL)))
							{
									// From and to really there ??

								if(Args[FROM_ARG] && Args[TO_ARG])
								{
										// A O.K. so far

									GoOn		= TRUE;

										// Get output handle

									OutHandle	= Output();

										// Get Flags

									if(Args[CLONE_ARG])
										CopyFlags	|=	CLONE;
									if(Args[DATES_ARG])
										CopyFlags	|=	DATES;
									if(Args[NOPRO_ARG])
										CopyFlags	|=	NOPRO;
									if(Args[COM_ARG])
										CopyFlags	|=	COM;
									if(Args[QUIET_ARG])
										CopyFlags	|=	QUIET;
									if(Args[FORCE_ARG])
										CopyFlags	|=	FORCE;
									if(Args[BUFFER_ARG])
										MaxBuffer	=	*((ULONG *)Args[BUFFER_ARG]) * 1024;

										// Check for NOREQ Option

									if(Args[NOREQ_ARG])
									{
										OldWindow		= MyProc->pr_WindowPtr;
										MyProc->pr_WindowPtr	= (void *)(-1L);
									}

										// Get pointers to Files

									FromPtr	= (char **)Args[FROM_ARG];
									ToPtr	= (char *)Args[TO_ARG];

										// Count FROM entries

									while(*FromPtr++)
										NumFrom++;

										// Restore FromPtr

									FromPtr	= (char **)Args[FROM_ARG];

										// Set pattern if there are more than 1
										// FROM files or if the only FROM file
										// is a pattern

									if(NumFrom > 1)
										PatternFrom = TRUE;
									else
										PatternFrom = ParsePatternNoCase(*FromPtr, Pattern, 1024);

										// If a pattern is used, check
										// whether the dest really is a dir

									if(PatternFrom)
									{
											// Try to obtain a lock

										if((DirLock = Lock(ToPtr, ACCESS_READ)))
										{
												// Check file

											if(Examine(DirLock, FIB))
											{
													// Get type of destination entry

												GoOn = (FIB->fib_DirEntryType >= 0);

													// If it`s not a directory (multiple files are to be moved)
													// issue an error

												if(!GoOn)
													FPuts(OutHandle, "\nMove : Multiple files may not be moved to a single file\n");
											}
											else
											{
													// Examine() failed -> Abort

												GoOn = FALSE;
												PrintFault(IoErr(), "\nMove ");
											}

												// Unlock CheckDir

											UnLock(DirLock);
										}
										else
										{
												// If lock failed issue an error

											PrintFault(IoErr(), "\nMove ");
											GoOn = FALSE;
										}
									}



										// Loop for all source files

									while(NumFrom && GoOn)
									{
											// Check for CTRL-C

										if(GoOn)
											HitMask	= CheckSignal(WATCHSIGS);

										if(!HitMask && GoOn)
										{
												// Check if this source is a pattern

											strcpy(Buffer, FilePart(*FromPtr));
											strupr(Buffer);
											PatternType = ParsePatternNoCase(Buffer, Pattern, 1024);

												// It is a pattern

											if(PatternType == 1)
											{
													// Create name of source

												strcpy(Buffer, *FromPtr);
												*PathPart(Buffer)	= '\0';

													// Get Lock for Source - Directory

												if((DirLock = Lock(Buffer, ACCESS_READ)))
												{
														// Setup ExAllControl-Structure

													EAC->eac_LastKey	= 0L;
													EAC->eac_MatchString	= Pattern;
													EAC->eac_MatchFunc	= NULL;

													do
													{
															// Check for CTRL-C

														if(GoOn)
															HitMask = CheckSignal(WATCHSIGS);

														if(HitMask)
															GoOn = FALSE;

															// Scan directory

														Scanning = ExAll(DirLock, EAB, (20*sizeof(struct ExAllData)), ED_SIZE, EAC);

															// Issue Error

														if(GoOn && (!Scanning) && (IoErr() != ERROR_NO_MORE_ENTRIES))
														{
															PrintFault(IoErr(), "\nMove ");
															GoOn = FALSE;
														}

															// End of Dir reached

														if(EAC->eac_Entries == 0)
															Scanning = FALSE;
														else if(GoOn)
														{
															EAD	= EAB;

															do
															{
																	// Check for CTRL-C

																if(GoOn)
																	HitMask = CheckSignal(WATCHSIGS);

																if(!HitMask && GoOn)
																{
																		// Create filename for this file to be moved

																	strcpy(PathName, *FromPtr);
																	*PathPart(PathName)	= '\0';
																	AddPart(PathName, EAD->ed_Name, 1024);

																		// Move file

																	GoOn	= DoTheMove(PathName, ToPtr, TRUE, OutHandle, EAD->ed_Type, EAD->ed_Size, CopyFlags, &HitMask, MaxBuffer, 1);

																	EAD	= EAD->ed_Next;
																}
																else
																	GoOn = FALSE;

															} while(EAD && GoOn);
														}
													} while(Scanning);

														// Unlock source directory

													UnLock(DirLock);
												}
												else
												{
														// Issue error, if dir could not be locked

													PrintFault(IoErr(), "\nMove ");
													GoOn	= FALSE;
												}
											}
												// No pattern

											else if(PatternType == 0)
											{
													// Try to lock source file/dir

												if((DirLock = Lock(*FromPtr, ACCESS_READ)))
												{
														// Examine this lock

													if(Examine(DirLock, FIB))
													{
															// Unlock directory and do the move

														UnLock(DirLock);
														GoOn	= DoTheMove(*FromPtr, ToPtr, TRUE, OutHandle, FIB->fib_DirEntryType, FIB->fib_Size, CopyFlags, &HitMask, MaxBuffer, 1);
													}
													else
													{
															// Issue error on failed Examine()

														PrintFault(IoErr(), "\nMove ");
														GoOn = FALSE;
														UnLock(DirLock);
													}
												}
												else
												{
														// No Lock() no more moves

													PrintFault(IoErr(), "\nMove ");
													GoOn = FALSE;
												}
											}

												// Error condition

											else
											{
												PrintFault(IoErr(), "\nMove ");
												GoOn = FALSE;
											}

											NumFrom--;
											FromPtr++;
										}
										else
											GoOn	= FALSE;
									}
								}

									// Free ReadArgs

								FreeArgs(RDArgs);
							}
							else
								PrintFault(IoErr(), "\nMove ");

								// Free ExAll-Buffer

							FreeVec(EAB);
						}
						else
							PrintFault(ERROR_NO_FREE_STORE, "\nMove ");

							// Free ExAllControl Structure

						FreeDosObject(DOS_EXALLCONTROL, (void *)EAC);
					}
					else
						PrintFault(IoErr(), "\nMove ");

						// Free Argument Buffer

					FreeVec(Args);
				}
				else
					PrintFault(ERROR_NO_FREE_STORE, "\nMove ");

				FreeVec(FIB);
			}
			else
				PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
		}
		else
			PrintFault(ERROR_NO_FREE_STORE, "\nMove ");


			// Free allocated buffers

		if(Pattern)
			FreeVec(Pattern);

		if(PathName)
			FreeVec(PathName);

		if(Buffer)
			FreeVec(Buffer);


			// Check for Abort-Signals

		if(HitMask && !(CopyFlags & QUIET))
		{
				// Display appropriate message

			if(HitMask & SIGBREAKF_CTRL_C)
				FPuts(OutHandle, "\nMove ^C...\n");
		}

			// Close DOSLibrary

		CloseLibrary((struct Library *)DOSBase);
	}

		// Restore old window pointer (if any)

	if(OldWindow)
		MyProc->pr_WindowPtr	= OldWindow;


		// Depending on this flag return appropriate return code

	if(GoOn)
		return(0);
	else
		return(10);
}




/**********************************************************************/
/*                            Do the move                             */
/**********************************************************************/
static BOOL __stdargs DoTheMove(char *FromFile, char *ToPath, BOOL PatternFrom, BPTR OutHandle, LONG Type, ULONG Size, UWORD CopyFlags, LONG *HitMask, ULONG MaxBuffer, ULONG RecDepth)
{
	struct	ExAllControl	*EAC;
	struct	ExAllData	*EAB, *EAD;
	char	*NewDir;
	char	*Buffer		= NULL;
	char	*ToDir		= NULL;
	char	*DispBuff	= NULL;
	UWORD	Len;
	BPTR	DirLock;
	BOOL	RetVal		= TRUE,
		Scanning,
		DoRename	= FALSE,
		MoveDirToSame	= FALSE,
		Skipped		= FALSE;


		// Try to allocate buffers

	if((NewDir = AllocVec(1024, MEMF_CLEAR)) && (Buffer = AllocVec(1024, MEMF_CLEAR)) && (ToDir = AllocVec(1024, MEMF_CLEAR)) && (DispBuff = AllocVec(1024, MEMF_CLEAR)))
	{
			// Is it a directory ???
			// If yes, recursively loop for all entries within dir

		if(Type >= 0)
		{
			strcpy(Buffer, FromFile);

				// Get length of from name

			Len	= strlen(Buffer);

				// Really got a from name ?!?

			if(Len)
			{
				if(Buffer[Len - 1] == '/')
					Buffer[Len - 1] = '\0';

					// Copy dest name to buffer

				strcpy(NewDir, ToPath);

					// Check for dir moving to itself...
					// ...and for renaming

				if((DirLock = Lock(Buffer, ACCESS_READ)))
				{
					BPTR	TestLock;

					if((TestLock = Lock(NewDir, ACCESS_READ)))
					{
							// Working on same volume?

						DoRename	= (SameLock(DirLock, TestLock) == LOCK_SAME_VOLUME);

							// Renaming a directory ???

						MoveDirToSame	= (SameLock(DirLock, TestLock) == LOCK_SAME);

						UnLock(TestLock);
					}

					UnLock(DirLock);
				}


					// Ok, we are to move
					// If a pattern is used print the name of the dir to be moved

				if(PatternFrom && !(CopyFlags & QUIET))
				{
					int	i;

						// Prepend number of spaces according to recursion depth

					for(i = 0; i < RecDepth; i++)
						DispBuff[i]	= ' ';
					DispBuff[i]	= '\0';

					strcat(DispBuff, FromFile);
					strcat(DispBuff, "...");
					Write(OutHandle, " ", 1);
					Write(OutHandle, DispBuff, strlen(DispBuff));
				}


					// Destination directory available ???
					// If so, we are to move to that directory
					// Otherwise, we`ll simply rename

				if((DirLock = Lock(NewDir, ACCESS_READ)))
				{
					UnLock(DirLock);

						// Add name of from directory

					if(!MoveDirToSame)
						AddPart(NewDir, FilePart(Buffer), 1024);
				}


					// Simple rename; no cross-device move ?!?

				if(DoRename)
				{
					BOOL	DoTheRename	= TRUE;

						// Trying to rename a directory?
						// Only allowed when not in pattern or multimode

					if(MoveDirToSame)
						DoTheRename	= !PatternFrom;

						// Still rename the directory?

					if(DoTheRename)
					{
						BPTR	TestLock;

							// Destination is already there?
							// If so, only move contents

						if((TestLock = Lock(NewDir, ACCESS_READ)))
						{
							DoRename	= FALSE;
							UnLock(TestLock);
						}
						else
						{
								// Do the rename

							if(!Rename(Buffer, NewDir))
							{
								PrintFault(IoErr(), "\nMove ");
								RetVal = FALSE;
							}
						}
					}
					else
						Skipped	= TRUE;
				}

				if(!DoRename)
				{
						// Clone directory name of source

					strcpy(ToDir, Buffer);

						// Try to lock directory or
						// create it, if not existent

					if(!(DirLock = Lock(NewDir, ACCESS_READ)))
					{
						if((DirLock = CreateDir(NewDir)) && PatternFrom && !(CopyFlags & QUIET))
							Write(OutHandle, " [created]...", 13);
					}

						// Got the destination directory ???

					if(DirLock)
					{
							// Unlock Directory

						UnLock(DirLock);

							// Lock sourcedirectory

						if((DirLock = Lock(Buffer, ACCESS_READ)))
						{
								// Get ExAll Control for recursive directory search

							if((EAC = AllocDosObject(DOS_EXALLCONTROL, NULL)))
							{
									// Allocate buffer for move

								if((EAB = AllocVec((20*sizeof(struct ExAllData)), MEMF_CLEAR)))
								{
									EAC->eac_LastKey	= 0L;
									EAC->eac_MatchString	= NULL;
									EAC->eac_MatchFunc	= NULL;

									do
									{
											// Check for CTRL-C

										if(RetVal)
											(*HitMask) = CheckSignal(WATCHSIGS);

										if((*HitMask))
											RetVal = FALSE;

											// Scan directory for entries

										Scanning = ExAll(DirLock, EAB, (20*sizeof(struct ExAllData)), ED_SIZE, EAC);

											// Check for an error

										if(RetVal && (!Scanning) && (IoErr() != ERROR_NO_MORE_ENTRIES))
										{
											PrintFault(IoErr(), "\nMove ");
											RetVal = FALSE;
										}

											// End of Dir reached

										if(EAC->eac_Entries == 0)
											Scanning = FALSE;
										else if(RetVal)
										{
											EAD = EAB;

											do
											{
													// Check for CTRL-C

												if(RetVal)
													(*HitMask) = CheckSignal(WATCHSIGS);

												if(!(*HitMask) && RetVal)
												{
														// Create filename of source

													strcpy(Buffer, FromFile);
													AddPart(Buffer, EAD->ed_Name, 1024);

														// And start recursion

													RetVal = DoTheMove(Buffer, NewDir, FALSE, OutHandle, EAD->ed_Type, EAD->ed_Size, CopyFlags, HitMask, MaxBuffer, RecDepth + 1);

														// Loop for all entries

													EAD = EAD->ed_Next;
												}
												else
													RetVal = FALSE;

											} while(EAD && RetVal);
										}
									} while(Scanning);

										// Free ExAll Buffer

									FreeVec(EAB);
								}
								else
								{
									PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
									RetVal = FALSE;
								}

									// Free ExAllControl Structure

								FreeDosObject(DOS_EXALLCONTROL, EAC);
							}
							else
							{
								PrintFault(IoErr(), "\nMove ");
								RetVal = FALSE;
							}

							UnLock(DirLock);
						}
						else
						{
							PrintFault(IoErr(), "\nMove ");
							RetVal = FALSE;
						}
					}
					else
					{
						PrintFault(IoErr(), "\nMove ");
						RetVal = FALSE;
					}
				}

					// Ok, this dir was moved (no rename), so remove it

				if(RetVal && !DoRename)
				{
						// With force mode set on,
						// First make dir deletable

					if(CopyFlags & FORCE)
						SetProtection(ToDir, ~(FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_WRITE|FIBF_DELETE));

						// Delete directory

					if(!DeleteFile(ToDir))
					{
						PrintFault(IoErr(), "\nMove ");
						RetVal = FALSE;
					}
				}

					// Display "moved" message

				if(PatternFrom && RetVal && !(CopyFlags & QUIET))
				{
					if(!Skipped)
						FPuts(OutHandle, " moved\n");
					else
						FPuts(OutHandle, " skipped\n");
				}
			}
		}
		else
		{
				// Ok, we are to move
				// If a pattern is used print the name of the file to be moved

			if(PatternFrom && !(CopyFlags & QUIET))
			{
				int	i;

					// Prepend number of spaces according to recursion depth

				for(i = 0; i < RecDepth; i++)
					DispBuff[i]	= ' ';
				DispBuff[i]	= '\0';

				strcat(DispBuff, FromFile);
				strcat(DispBuff, "...");
				Write(OutHandle, " ", 1);
				Write(OutHandle, DispBuff, strlen(DispBuff));
			}

				// On a plain file -> Simply move it

			RetVal = DoMoveFile(FromFile, ToPath, Buffer, OutHandle, Size, CopyFlags, HitMask, MaxBuffer);

				// If there was a pattern (or a single directory)
				// end the Move string

			if(PatternFrom && RetVal && !(CopyFlags & QUIET))
			{
				if(!Skipped)
					FPuts(OutHandle, " moved\n");
				else
					FPuts(OutHandle, " skipped\n");
			}
		}
	}
	else
	{
		PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
		RetVal = FALSE;
	}

		// De-Allocate buffers

	if(NewDir)
		FreeVec(NewDir);

	if(Buffer)
		FreeVec(Buffer);

	if(ToDir)
		FreeVec(ToDir);

	if(DispBuff)
		FreeVec(DispBuff);

		// Return Code

	return(RetVal);
}








/**********************************************************************/
/*                       Physically move a file                       */
/**********************************************************************/
static BOOL __stdargs DoMoveFile(char *FromFile, char *ToPath, char *Buffer, BPTR OutHandle, ULONG Size, UWORD CopyFlags, LONG *HitMask, ULONG MaxBuffer)
{
	struct	FileInfoBlock	*FIB;
	BPTR			InFile,
				OutFile;
	char			*DestName;
	APTR			CopyBuffer;
	ULONG			ReadSize,
				AvailSize;
	LONG			InKey		= -1,
				OutKey		= -1;
	BOOL			RetVal		= TRUE,
				ErrCpy		= FALSE,
				LoopCpy		= TRUE,
				DoRename	= FALSE,
				RemoveFirst	= FALSE;


		// Allocate buffer for Destination name

	if(!(DestName = AllocVec(1024, MEMF_CLEAR)))
	{
		PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
		return(FALSE);
	}

	if((FIB = AllocVec(sizeof(struct FileInfoBlock), MEMF_CLEAR)))
	{

		strcpy(DestName, ToPath);

			// Check, if destfile already exists
			// If so, check if it`s a dir.
			// Yes -> Copy with sourcename
			// No -> Copy with given name

		if((OutFile = Lock(DestName, ACCESS_READ)))
		{
			if(Examine(OutFile, FIB))
			{
					// If dest is a directory move with source name

				if(FIB->fib_DirEntryType >= 0)
					AddPart(DestName, FilePart(FromFile), 1024);
				else
				{
					OutKey		= ((struct FileLock *)BADDR(OutFile))->fl_Key;
					RemoveFirst	= TRUE;
				}
			}
			else
			{
					// No examine -> Use source filename

				AddPart(DestName, FilePart(FromFile), 1024);
			}

			UnLock(OutFile);
		}

			// Try to lock input file

		if((InFile = Lock(FromFile, ACCESS_READ)))
		{
				// Get DiskBlock for inputfile

			InKey	= ((struct FileLock *)BADDR(InFile))->fl_Key;

				// Get old protection bits, comment and filedate

			if(Examine(InFile, FIB))
			{
					// In Force mode -> Force read and delete permissons
					// for input file

				if(CopyFlags & FORCE)
					SetProtection(FromFile, ~(FIBF_OTR_READ|FIBF_OTR_DELETE|FIBF_READ|FIBF_DELETE));
			}
			else
			{
				PrintFault(IoErr(), "\nMove ");
				RetVal = FALSE;
			}

				// If so, unlock

			UnLock(InFile);
		}
		else
		{
			PrintFault(IoErr(), "\nMove ");
			RetVal = FALSE;
		}

			// Can we possibly simple rename the file?

		if((InFile = Lock(FromFile, ACCESS_READ)))
		{
			if((OutFile = Lock(ToPath, ACCESS_READ)))
			{
				DoRename	= (SameLock(InFile, OutFile) == LOCK_SAME_VOLUME);

				UnLock(OutFile);
			}

			UnLock(InFile);
		}

			// No Rename -> Do the right move ;)

		if(!DoRename)
		{
				// Open Input file

			if(RetVal && (InFile = Open(FromFile, MODE_OLDFILE)))
			{
					// Outfile already there ??? and in force mode ???

				if((CopyFlags & FORCE) && ((OutFile = Lock(DestName, ACCESS_READ))))
				{
						// Force delete and write permissons

					UnLock(OutFile);
					SetProtection(DestName, ~(FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_WRITE|FIBF_DELETE));
				}


					// Open Output file

				if((OutFile = Open(DestName, MODE_NEWFILE)))
				{
						// Is there any data within the file ???

					if(Size)
					{
							// Get available size of memory
							// or take the user`s buffer size into account

						AvailSize	= (MaxBuffer != 0) ? MaxBuffer : (AvailMem(MEMF_LARGEST) - 102400);
						Size		= (Size > AvailSize) ? AvailSize : Size;

							// Loop till we get a buffer

						while(Size && !(CopyBuffer = AllocVec(Size, MEMF_CLEAR)))
							Size	-= 1024;


						if(CopyBuffer)
						{
								// Loop for portions of the source file

							while(RetVal && LoopCpy)
							{
									// Check for CTRL-C

								if(RetVal)
									(*HitMask) = CheckSignal(WATCHSIGS);

								if(!(*HitMask) && RetVal)
								{
										// Read part of file

									if((ReadSize = Read(InFile, CopyBuffer, Size)) > 0)
									{
											// And write it back

										if(Write(OutFile, CopyBuffer, ReadSize) == -1)
										{
											PrintFault(IoErr(), "\nMove ");
											RetVal	= FALSE;
											ErrCpy	= TRUE;
										}
									}
									else
									{
											// EOF ??? -> End copy

										if(ReadSize == 0)
											LoopCpy	= FALSE;
										else
										{
												// Otherwise issue error

											PrintFault(IoErr(), "\nMove ");
											RetVal	= FALSE;
											ErrCpy	= TRUE;
										}
									}
								}
								else
									RetVal = FALSE;
							}

							FreeVec(CopyBuffer);
						}
						else
						{
							PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
							RetVal	= FALSE;
							ErrCpy	= TRUE;
						}
					}

					Close(OutFile);
				}
				else
				{
						// Issue error

					PrintFault(IoErr(), "\nMove ");
					RetVal = FALSE;
				}
					// Close InFile

				Close(InFile);
			}
			else
			{
					// Issue error

				PrintFault(IoErr(), "\nMove ");
				RetVal = FALSE;
			}
		}
		else
		{
				// If RemoveFirst is not set, check again ;)

			if(!RemoveFirst)
			{
				if((OutFile = Lock(DestName, ACCESS_READ)))
				{
					if(Examine(OutFile, FIB))
					{
							// If dest is a file, delete first

						if(FIB->fib_DirEntryType < 0)
						{
								// Get Disk block of dest file

							OutKey	= ((struct FileLock *)BADDR(OutFile))->fl_Key;
							RemoveFirst = TRUE;
						}
					}
					UnLock(OutFile);
				}
			}

				// Check if only a rename should be accomplished

			if(InKey != -1 && OutKey != -1)
			{
				if(InKey == OutKey)
					RemoveFirst = FALSE;
			}

				// In Rename() mode, the destination has to be removed first

			if(RemoveFirst)
			{
					// If dest file is protected check for FORCE flag

				if(CopyFlags & FORCE)
					SetProtection(DestName, ~(FIBF_OTR_WRITE|FIBF_OTR_DELETE|FIBF_WRITE|FIBF_DELETE));

				if(!DeleteFile(DestName))
				{
					PrintFault(IoErr(), "\nMove ");
					RetVal = FALSE;
				}
			}

			if(RetVal)
			{
					// Now try to "rename" the file

				if(!Rename(FromFile, DestName))
				{
					PrintFault(IoErr(), "\nMove ");
					RetVal = FALSE;
				}
			}
		}

			// Error while copying ?? -> Remove Dest file

		if(ErrCpy)
		{
			FPuts(OutHandle, "\nMove : Error while moving; destination removed.\n");
			DeleteFile(DestName);
		}

			// No Error -> Remove Source file

		if(RetVal && !ErrCpy)
		{
			if(!DoRename && !(RetVal = DeleteFile(FromFile)))
				PrintFault(IoErr(), "\nMove ");
			else
			{
					// Set bits etc. as requested

				if((CopyFlags & CLONE) || (CopyFlags & DATES))
					SetFileDate(DestName, &FIB->fib_Date);

				if((CopyFlags & CLONE) || (CopyFlags & COM))
					SetComment(DestName, FIB->fib_Comment);

				if(((CopyFlags & CLONE) && !(CopyFlags & NOPRO)) || (!(CopyFlags & CLONE) && !(CopyFlags & NOPRO)))
					SetProtection(DestName, FIB->fib_Protection);
			}
		}

			// Free FileInfo Block

		FreeVec(FIB);
	}
	else
	{
		PrintFault(ERROR_NO_FREE_STORE, "\nMove ");
		RetVal = FALSE;
	}

		// Free buffer for destination name

	FreeVec(DestName);

	return(RetVal);
}
