/********************************************************************
*
*          File: MakePath.c
*   Description: Main code module for MakePath
*        Author: Ole Martin Bjørndalen (olemb@stud.cs.uit.no)
*       Version: 1.1a
*        Status: Public domain
*  Last changed: 27 Sep 1997
*
********************************************************************/

#define __USE_SYSBASE

#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>

#include <string.h>

/* Uncomment the next line to activate debugging */
//#define DEBUG 1
#include "debug.h"

UBYTE fver[] = "$VER: MakePath 1.1a " __AMIGADATE__ " Ole Martin Bjørndalen";

/****** CliTools/MakePath *******************************************
*
*	NAME
*		MakePath -- Creates or completes path.
*
*	TEMPLATE
*		PATH/M/A
*
*	FUNCTION
*		MakePath attempts to create or complete the path you
*		specify. Directories that do not exist will be created.
*		If MakePath returns without errors, the full path is
*		guaranteed to be there.
*
*		MakePath does not create icons for the new drawers.
*
*	ARGUMENTS
*		PATH - One or more paths to create
*
*	RESULT
*		Returns FAIL if something went wrong, else OK.
*
*	EXAMPLES
*		MakePath a/short/path
*		MakePath a/path another/path
*		MakePath this//is//perfectly//legal
*
*	NOTES
*		Unlike MakeDir, MakePath will not fail if a directory
*		already exists. You may want to know this when writing
*		scripts.
*
*	BUGS
*		None known
*
*	AUTHOR
*		Ole Martin Bjørndalen (olemb@stud.cs.uit.no)
*
*	SEE ALSO
*		C:MakeDir
*
*****************************************************************************
*
*/

BOOL MakePath(struct ExecBase *SysBase,
				struct DosLibrary *DOSBase,
				STRPTR path);

/*
	Define an argument structure for ReadArgs().
	If find this is more readable than using a
	LONG array, even for one argument
*/
struct Arguments {
	STRPTR *path;
};

#define TEMPLATE "PATH/M/A"

/*
	There is no startup code, so we'll have to do
	all the setup ourselves. Most of the work is done
	by dos.library,	though.
*/
int startup(void)
{
	LONG result = RETURN_OK;
	struct ExecBase *SysBase;
	struct Process *proc;

	SysBase = (*((struct ExecBase **) 4));
	proc = (struct Process *)FindTask(NULL);

	/*
		Make sure we don't crash if started from WB
	*/
	if(proc->pr_CLI)
	{
		struct DosLibrary *DOSBase;

		if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36L))
		{
			struct RDArgs *rdargs;
			struct Arguments args = { NULL };	

			if(rdargs = ReadArgs(TEMPLATE, (LONG *)&args, NULL))
			{
				int i;

				for(i = 0; args.path[i]; i++)
				{
					if(!MakePath(SysBase, DOSBase, args.path[i]))
					{
						/* Failed! Let's get outta here! */
						result = RETURN_FAIL;
						break;
					}
				}
			}
			else
			{
				PrintFault(IoErr(), NULL);
				result = RETURN_FAIL;
			}

			CloseLibrary((struct Library *)DOSBase);
		}
		else
		{
			proc->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;			
		}
	}
	else
	{
		/*
			Started from WB. Reply startup
			message and exit gracefully
		*/
		struct Message *msg;
		
		WaitPort(&proc->pr_MsgPort);
		
		if(msg = GetMsg(&proc->pr_MsgPort))
		{
			Forbid();
			ReplyMsg(msg);
		}
	}

	return result;
}

/*
	Create the actual path. If this fails, an
	error code is printed and FALSE is returned.
	
	I use SetIoErr() because it allows me to
	use PrintFault() for my own error conditions.
	Thus, MakePath is fully localized.
*/
BOOL MakePath(struct ExecBase *SysBase,
				struct DosLibrary *DOSBase,
				STRPTR path)
{
	BOOL success = FALSE;
	UBYTE *buffer;
	
	D(bug("MakePath(%s)\n", path));
	
	/* Allocate a temporary buffer to hold the path */
	if(buffer = AllocVec(strlen(path) + 1, MEMF_ANY))
	{
		LONG p;

//		D(bug("Buffer allocated\n"));

		/*
			Iterate through the buffer and stop
			between path components
		*/
		for(p = 0; ;p++)
		{
			if(path[p] == '/' || path[p] == '\0')
			{
				BPTR lock;
				
				/*
					Temporary NULL termination
				*/
				buffer[p] = '\0';

				D(bug("Lock(%s)\n", buffer));
				
				/*
					Expect the worst. This will be
					set back to TRUE if all went well
				*/
				success = FALSE;
			
				/* Does the directory exist? */
				if(lock = Lock(buffer, ACCESS_READ))
				{
					__aligned struct FileInfoBlock fib;
				
					D(bug("Examine(%s)\n", buffer));
				
					/* Yes, but is it really a directory? */
					if(Examine(lock, &fib))
					{
						if(fib.fib_DirEntryType > 0)
						{
							success = TRUE;
						}
						else
						{
							/* fail */
							SetIoErr(ERROR_OBJECT_WRONG_TYPE);
						}
					}
					/* else fail */
				}
				else
				{
					/*
						If I didn't test this, the
						"please insert volume X"
						requester would open twice.
						Also, if a serious error has
						occured, calling CreateDir()
						is rather pointless.
					*/
					if(IoErr() == ERROR_OBJECT_NOT_FOUND)
					{
						D(bug("CreateDir(%s)\n", buffer));
												
						if(lock = CreateDir(buffer))
						{
							success = TRUE;
						}
						else
						{
							/* fail */
							D(bug("CreateDir() failed!\n"));
						}
					}
					/* else fail */
					
				} /* Lock() */
				
				if(lock)
					UnLock(lock);
					
				if(!success)
				{
					/*
						An error occured.
						Inform user and bail out
					*/
					PrintFault(IoErr(), buffer);
					break;
				}
				
				if(path[p] == '\0')
				{
					/* Reached the end of the string */
					break;
				}

			} /* if('/' || '\0') */
			
			/* Copy character to work buffer */
			buffer[p] = path[p];

		} /* for() */
	}
	else
	{
		PrintFault(ERROR_NO_FREE_STORE, NULL);
	}

	D(bug("%s\n", success ? "SUCCESS" : "FAILURE"));

	return success;
}
