/*
**	Verify.c
**
**	Check if files/drawers/programs are where they should be
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
**	:ts=4
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

struct Path
{
	BPTR	path_Next,
			path_Lock;
};

	/* FindPath(struct Window *Parent,STRPTR Path,BOOL CanCreate,LONG *Error):
	 *
	 *	For the complete path of a file, this checks if the
	 *	path leading to the file actually exists. This is
	 *	primarily for files the program should create. Thus,
	 *	the file in question may not yet exist.
	 */

BOOL
FindPath(struct Window *Parent,STRPTR Path,BOOL CanCreate,LONG *Error)
{
	UBYTE	LocalBuffer[MAX_FILENAME_LENGTH];
	STRPTR	Index;

	strcpy(LocalBuffer,Path);

	Index = PathPart(LocalBuffer);

	*Index = 0;

	return(FindDrawer(Parent,LocalBuffer,CanCreate,Error));
}

	/* FindDrawer(struct Window *Parent,STRPTR Drawer,BOOL CanCreate,LONG *Error):
	 *
	 *	Check if a given drawer exists and give
	 *	the user the choice to create it if necessary.
	 */

BOOL
FindDrawer(struct Window *Parent,STRPTR Drawer,BOOL CanCreate,LONG *Error)
{
	APTR	OldPtr = ThisProcess -> pr_WindowPtr;
	BPTR	FileLock;
	BOOL	Result = FALSE;
	LONG	LocalError;

	if(!Config -> MiscConfig -> ProtectiveMode)
		return(TRUE);

	if(!Error)
		Error = &LocalError;

		// Lock out DOS requesters

	ThisProcess -> pr_WindowPtr = (APTR)-1;

		// Does the drawer exist?

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

			// Take a closer look

		if(Examine(FileLock,&FileInfo))
		{
				// Is this actually a file?

			if(FileInfo . fib_DirEntryType < 0)
				*Error = ERR_NOT_A_DRAWER;
			else
			{
				*Error = 0;

				Result = TRUE;
			}
		}
		else
			*Error = IoErr();

		UnLock(FileLock);
	}
	else
	{
		LONG Result2 = IoErr();

			// If it doesn't exist, see if we should ask the user to create it

		if(Result2 == ERROR_OBJECT_NOT_FOUND && Parent && CanCreate)
		{
			UBYTE	LocalBuffer[MAX_FILENAME_LENGTH];
			STRPTR	Index;
			BOOL	CanCreate = FALSE;

				// Chop off the last path part

			strcpy(LocalBuffer,Drawer);

			Index = PathPart(LocalBuffer);

			*Index = 0;

				// Get a lock on the parent drawer

			if(FileLock = Lock(LocalBuffer,ACCESS_READ))
			{
				struct InfoData __aligned InfoData;

					// Inquire volume information

				if(Info(FileLock,&InfoData))
				{
						// Can we write to this volume?

					if(InfoData . id_DiskState == ID_VALIDATED)
						CanCreate = TRUE;
				}

				UnLock(FileLock);
			}

				// Give it a try?

			if(CanCreate)
			{
				LT_LockWindow(Parent);

					// Ask if we should create it

				if(ShowRequest(Parent,LocaleString(MSG_VERIFY_NO_DRAWER_TXT),LocaleString(MSG_GLOBAL_YES_NO_TXT),Drawer))
				{
					if(FileLock = CreateDir(Drawer))
					{
						Result2 = 0;

						Result = TRUE;

						UnLock(FileLock);
					}
					else
						Result2 = IoErr();
				}
				else
				{
					Result2 = 0;
					Result = TRUE;
				}

				LT_UnlockWindow(Parent);
			}
		}

		*Error = Result2;
	}

	ThisProcess -> pr_WindowPtr = OldPtr;

	if(Window && !Result && *Error)
	{
		LT_LockWindow(Parent);

		if(*Error == ERR_NOT_A_DRAWER)
			ShowError(Parent,*Error,NULL,Drawer);
		else
			ShowError(Parent,ERR_DRAWER_NOT_FOUND,*Error,Drawer);

		LT_UnlockWindow(Parent);
	}

	return(Result);
}

	/* FindFile(struct Window *Parent,STRPTR File,LONG *Error):
	 *
	 *	Try to locate a file.
	 */

BOOL
FindFile(struct Window *Parent,STRPTR File,LONG *Error)
{
	APTR	OldPtr = ThisProcess -> pr_WindowPtr;
	BPTR	FileLock;
	BOOL	Result = FALSE;
	LONG	LocalError;

	if(!Config -> MiscConfig -> ProtectiveMode)
		return(TRUE);

	if(!Error)
		Error = &LocalError;

		// Lock out DOS requesters

	ThisProcess -> pr_WindowPtr = (APTR)-1;

		// Try to get a lock on the file

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

			// Take a closer look

		if(Examine(FileLock,&FileInfo))
		{
				// Is it actually a drawer?

			if(FileInfo . fib_DirEntryType > 0)
				*Error = ERR_NOT_A_FILE;
			else
			{
				*Error = 0;

				Result = TRUE;
			}
		}
		else
			*Error = IoErr();

		UnLock(FileLock);
	}
	else
		*Error = IoErr();

	ThisProcess -> pr_WindowPtr = OldPtr;

	if(Window && !Result && *Error)
	{
		LT_LockWindow(Parent);

		if(*Error == ERR_NOT_A_FILE)
			ShowError(Parent,*Error,NULL,File);
		else
			ShowError(Parent,ERR_FILE_NOT_FOUND,*Error,File);

		LT_UnlockWindow(Parent);
	}

	return(Result);
}

	/* FindARexxComment(STRPTR LocalBuffer,LONG Len):
	 *
	 *	Check for a valid ARexx file comment.
	 */

STATIC BOOL
FindARexxComment(STRPTR LocalBuffer,LONG Len)
{
	register LONG	i;
	register LONG	c;

	for(i = 0 ; i < Len - 1 ; i++)
	{
		c = LocalBuffer[i];

			// Stop on invalid characters.

		if((c < ' ' && c != '\r' && c != '\n' && c != '\a') || (c >= 127 && c < 160))
			break;
		else
		{
				// Looks like the typical
				// introductory comment line.

			if(c == '/' && LocalBuffer[i + 1] == '*')
				return(TRUE);
		}
	}

	return(FALSE);
}

	/* FindProgram(struct Window *Parent,STRPTR Program,LONG *Error):
	 *
	 *	Try to find a program given by name. Firstly, try to access the
	 *	file by its name. If unsuccessful, search the Shell path list.
	 *	As a last resort, search the global AmigaDOS resident program
	 *	list. Note that while this routine may fail program execution
	 *	may still work. WShell for example maintains its own internal
	 *	resident list.
	 */

BOOL
FindProgram(struct Window *Parent,STRPTR Program,LONG *Error)
{
	UBYTE	LocalBuffer[MAX_FILENAME_LENGTH];
	APTR	OldPtr = ThisProcess -> pr_WindowPtr;
	BPTR	FileLock;
	LONG	i;
	BOOL	Result		= FALSE,
			CanExecute	= FALSE;
	LONG	LocalError;

	if(!Config -> MiscConfig -> ProtectiveMode)
		return(TRUE);

	if(!Error)
		Error = &LocalError;

		// Chop off the program arguments

	while(*Program == ' ')
		Program++;

	strcpy(LocalBuffer,Program);

	for(i = 0 ; i < strlen(LocalBuffer) ; i++)
	{
		if(LocalBuffer[i] == ' ')
		{
			LocalBuffer[i] = 0;

			break;
		}
	}

	Program = LocalBuffer;

		// Lock out DOS requesters

	ThisProcess -> pr_WindowPtr = (APTR)-1;

		// Try to lock the program

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

			// Take a closer look

		if(Examine(FileLock,&FileInfo))
		{
				// Is it actually a drawer?

			if(FileInfo . fib_DirEntryType > 0)
				*Error = ERR_NOT_A_FILE;
			else
			{
				BOOL GotIt = FALSE;

					// So far, so good

				*Error = 0;

				Result = TRUE;

					// Is it marked as being executable?

				if(!(FileInfo . fib_Protection & FIBF_EXECUTE))
				{
					BPTR	FileHandle;
					ULONG	Value;

						// Look at the contents

					if(FileHandle = Open(Program,MODE_OLDFILE))
					{
						if(Read(FileHandle,&Value,sizeof(ULONG)) == sizeof(ULONG))
						{
								// Not a failproof check, but better than nothing

							if(Value == HUNK_HEADER)
							{
								CanExecute = TRUE;
								GotIt = TRUE;
							}
						}

						Close(FileHandle);
					}
				}

				if(!GotIt)
				{
						// Does it have the script bit set? */

					if(FileInfo . fib_Protection & FIBF_SCRIPT)
						CanExecute = TRUE;
					else
					{
						BPTR FileHandle;

							// Now check if it's an ARexx script.

						if(FileHandle = Open(Program,MODE_OLDFILE))
						{
							UBYTE	LocalBuffer[256];
							LONG	Len;

							if((Len = Read(FileHandle,LocalBuffer,256)) > 0)
							{
								if(CanExecute = FindARexxComment(LocalBuffer,Len))
									Result = TRUE;
							}

							Close(FileHandle);
						}
					}
				}
			}
		}

		UnLock(FileLock);
	}
	else
	{
		LONG Result2 = IoErr();

		if(Result2 == ERROR_DEVICE_NOT_MOUNTED)
			*Error = ERROR_OBJECT_NOT_FOUND;
		else
			*Error = Result2;
	}

		// Give it another try

	if(!Result || !CanExecute)
	{
			// Take care

		if(ThisProcess -> pr_CLI)
		{
			struct CommandLineInterface	*CLI = BADDR(ThisProcess -> pr_CLI);
			BPTR						 StartCD,
										 Drawer;
			struct Path					*Path;
			BOOL						 InitialCD	= TRUE,
										 GotIt		= FALSE;

				// Run down the Shell path search list

			for(Path = BADDR(CLI -> cli_CommandDir) ; Path && !GotIt ; Path = BADDR(Path -> path_Next))
			{
				Drawer = CurrentDir(Path -> path_Lock);

				if(InitialCD)
				{
					StartCD		= Drawer;
					InitialCD	= FALSE;
				}

					// Try to lock the program

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

						// Take a closer look

					if(Examine(FileLock,&FileInfo))
					{
							// Is this a file marked as being executable?

						if(FileInfo . fib_DirEntryType < 0)
						{
							if(!(FileInfo . fib_Protection & FIBF_EXECUTE))
							{
								BPTR	FileHandle;
								ULONG	Value;

									// Look at the contents

								if(FileHandle = Open(Program,MODE_OLDFILE))
								{
									if(Read(FileHandle,&Value,sizeof(ULONG)) == sizeof(ULONG))
									{
											// Not a failproof check, but better than nothing

										if(Value == HUNK_HEADER)
											GotIt = TRUE;
									}

									Close(FileHandle);
								}
							}

							if(!GotIt)
							{
									// Does it look like a script?

								if(FileInfo . fib_Protection & FIBF_SCRIPT)
									GotIt = TRUE;
								else
								{
									BPTR FileHandle;

										// Now check if it's an ARexx script.

									if(FileHandle = Open(Program,MODE_OLDFILE))
									{
										UBYTE	LocalBuffer[256];
										LONG	Len;

										if((Len = Read(FileHandle,LocalBuffer,256)) > 0)
										{
											if(CanExecute = FindARexxComment(LocalBuffer,Len))
												GotIt = TRUE;
										}

										Close(FileHandle);
									}
								}
							}
						}
					}

					UnLock(FileLock);
				}
			}

			if(!GotIt)
			{
				BPTR CommandDir = Lock("C:",ACCESS_READ);

				Drawer = CurrentDir(CommandDir);

				if(InitialCD)
				{
					StartCD		= Drawer;
					InitialCD	= FALSE;
				}

					// Try to lock the program

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

						// Take a closer look

					if(Examine(FileLock,&FileInfo))
					{
							// Is this a file marked as being executable?

						if(FileInfo . fib_DirEntryType < 0)
						{
							if(!(FileInfo . fib_Protection & FIBF_EXECUTE))
							{
								BPTR	FileHandle;
								ULONG	Value;

									// Look at the contents

								if(FileHandle = Open(Program,MODE_OLDFILE))
								{
									if(Read(FileHandle,&Value,sizeof(ULONG)) == sizeof(ULONG))
									{
											// Not a failproof check, but better than nothing

										if(Value == HUNK_HEADER)
											GotIt = TRUE;
									}

									Close(FileHandle);
								}
							}

							if(!GotIt)
							{
									// Does it look like a script?

								if(FileInfo . fib_Protection & FIBF_SCRIPT)
									GotIt = TRUE;
								else
								{
									BPTR FileHandle;

										// Now check if it's an ARexx script.

									if(FileHandle = Open(Program,MODE_OLDFILE))
									{
										UBYTE	LocalBuffer[256];
										LONG	Len;

										if((Len = Read(FileHandle,LocalBuffer,256)) > 0)
										{
											if(CanExecute = FindARexxComment(LocalBuffer,Len))
												GotIt = TRUE;
										}

										Close(FileHandle);
									}
								}
							}
						}
					}

					UnLock(FileLock);
				}

				UnLock(CommandDir);
			}

			if(!InitialCD)
				CurrentDir(StartCD);

			if(GotIt)
			{
				CanExecute = TRUE;

				*Error = 0;

				Result = TRUE;
			}
		}
	}

		// As a last resort, scan the resident program list

	if(!Result)
	{
		Forbid();

		if(FindSegment(Program,NULL,DOSFALSE) || FindSegment(Program,NULL,DOSTRUE))
		{
			CanExecute = TRUE;

			*Error = 0;

			Result = TRUE;
		}

		Permit();
	}

		// Last check for ARexx script.

	if(!Result)
	{
		UBYTE RexxName[MAX_FILENAME_LENGTH];

		strcpy(RexxName,"REXX:");

		if(AddPart(RexxName,Program,MAX_FILENAME_LENGTH))
		{
			BPTR FileHandle;

				// Now check if it's an ARexx script.

			if(FileHandle = Open(RexxName,MODE_OLDFILE))
			{
				UBYTE	LocalBuffer[256];
				LONG	Len;

				if((Len = Read(FileHandle,LocalBuffer,256)) > 0)
				{
					if(CanExecute = FindARexxComment(LocalBuffer,Len))
						Result = TRUE;
				}

				Close(FileHandle);
			}
		}
	}

		// Last check for AmigaDOS script

	if(!Result)
	{
		UBYTE ScriptName[MAX_FILENAME_LENGTH];

		strcpy(ScriptName,"S:");

		if(AddPart(ScriptName,Program,MAX_FILENAME_LENGTH))
		{
			BPTR FileLock;

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

				if(Examine(FileLock,&FileInfo))
				{
					if(FileInfo . fib_DirEntryType < 0 && (FileInfo . fib_Protection & FIBF_SCRIPT) && FileInfo . fib_Size > 0)
						CanExecute = Result = TRUE;
				}

				UnLock(FileLock);
			}
		}
	}

	if(!CanExecute && Result)
	{
		*Error = ERROR_NOT_EXECUTABLE;

		Result = FALSE;
	}

	ThisProcess -> pr_WindowPtr = OldPtr;

	if(Window && !Result && *Error)
	{
		LT_LockWindow(Parent);

		if(*Error == ERR_NOT_A_FILE)
			ShowError(Parent,*Error,NULL,Program);
		else
			ShowError(Parent,ERR_PROGRAM_NOT_FOUND,*Error,Program);

		LT_UnlockWindow(Parent);
	}

	return(Result);
}

	/* FindLibDev(struct Window *Parent,STRPTR File,LONG Type,LONG *Error):
	 *
	 *	Try to locate a file.
	 */

BOOL
FindLibDev(struct Window *Parent,STRPTR File,LONG Type,LONG *Error)
{
	UBYTE		 LocalBuffer[MAX_FILENAME_LENGTH];
	STRPTR		 OriginalFile;
	APTR		 OldPtr = ThisProcess -> pr_WindowPtr;
	BPTR		 FileLock;
	BOOL		 Result		= FALSE,
				 Replaced	= FALSE;
	LONG		 LocalError;
	struct Node	*Node;

	if(!Config -> MiscConfig -> ProtectiveMode)
		return(TRUE);

	if(!Error)
		Error = &LocalError;

	Forbid();

	if(Type == NT_LIBRARY)
		Node = FindName(&SysBase -> LibList,PathPart(File));
	else
		Node = FindName(&SysBase -> DeviceList,PathPart(File));

	Permit();

	if(Node)
	{
		*Error = 0;

		return(TRUE);
	}

		// Lock out DOS requesters

	ThisProcess -> pr_WindowPtr = (APTR)-1;

		// Try to get a lock on the file

Look:

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

			// Take a closer look

		if(Examine(FileLock,&FileInfo))
		{
				// Is it actually a drawer?

			if(FileInfo . fib_DirEntryType > 0)
				*Error = ERR_NOT_A_FILE;
			else
			{
				if(ValidateFile(File,Type,NULL))
				{
					*Error = 0;

					Result = TRUE;
				}
				else
					*Error = ERROR_OBJECT_WRONG_TYPE;
			}
		}
		else
			*Error = IoErr();

		UnLock(FileLock);
	}
	else
		*Error = IoErr();

	if(!Result && !Replaced)
	{
		Replaced = TRUE;

		if(Type == NT_LIBRARY)
			strcpy(LocalBuffer,"Libs:");
		else
			strcpy(LocalBuffer,"Devs:");

		OriginalFile = File;

		if(AddPart(LocalBuffer,File,MAX_FILENAME_LENGTH))
		{
			File = LocalBuffer;

			goto Look;
		}
	}

	if(Replaced)
		File = OriginalFile;

	ThisProcess -> pr_WindowPtr = OldPtr;

	if(Window && !Result && *Error)
	{
		LT_LockWindow(Parent);

		if(*Error == ERR_NOT_A_FILE)
			ShowError(Parent,*Error,NULL,File);
		else
			ShowError(Parent,ERR_FILE_NOT_FOUND,*Error,File);

		LT_UnlockWindow(Parent);
	}

	return(Result);
}
