/*
 * $Id: sashimi.c 1.5 1998/04/07 21:16:28 olsen Exp olsen $
 *
 * Sashimi -- intercepts raw serial debugging output on your own machine
 *
 * Written by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
 * Public Domain
 *
 * :ts=4
 */

#include <exec/memory.h>

#include <devices/timer.h>

#include <dos/dosextens.h>
#include <dos/rdargs.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include <pragmas/exec_sysbase_pragmas.h>
#include <pragmas/dos_pragmas.h>

#include <string.h>
#include <stdio.h>

/****************************************************************************/

STRPTR Version = "$VER: Sashimi 1.3 (7.4.98)\r\n";

/****************************************************************************/

#define OK		(0)
#define NOT		!
#define BUSY	NULL

/****************************************************************************/

#define MILLION 1000000

/****************************************************************************/

extern struct Library * SysBase;
extern struct Library * DOSBase;

/****************************************************************************/

typedef LONG	SWITCH;
typedef LONG *	NUMBER;
typedef STRPTR	KEY;

struct
{
	/* Startup options */
	SWITCH	On;			/* Ignored */
	NUMBER	BufferK;	/* Buffer size, to the power of two */
	NUMBER	BufferSize;	/* Buffer size in bytes */
	SWITCH	NoPrompt;	/* Do not show the initial prompt message */
	SWITCH	Quiet;		/* Do not produce any output at all */
	SWITCH	AskExit;	/* Ask whether to exit the program */
	SWITCH	AskSave;	/* Ask for a file to save the buffer to when exiting */
	SWITCH	TimerOn;	/* Check the ring buffer every 1/10 of a second */
	SWITCH	Console;	/* Open a console window for I/O */
	KEY		Window;		/* Console window specifier */

	/* Runtime options */
	SWITCH	Off;		/* Turn Sashimi off */
	SWITCH	Save;		/* Save the ring buffer contents */
	KEY		SaveAs;		/* Save the ring buffer contents under a specific name */
	SWITCH	Empty;		/* Empty the ring buffer */
} ShellArguments;

const STRPTR ShellTemplate = "ON/S,BUFK/N,BUFFERSIZE/N,NOPROMPT/S,QUIET/S,ASKEXIT/S,"
                             "ASKSAVE/S,TIMERON/S,CONSOLE/S,WINDOW/K,"
                             "OFF/S,SAVE/S,SAVEAS/K,EMPTY/S";

/****************************************************************************/

struct SashimiResource
{
	struct Library	sr_Library;			/* Global link */

	struct Task *	sr_Owner;			/* Current owner of the patches */
	LONG			sr_OwnerSigBit;
	ULONG			sr_OwnerSigMask;	/* Signal mask to send when a new line is in the buffer. */

	UBYTE *			sr_FIFO;			/* The message buffer */
	ULONG			sr_FIFOTotalSize;	/* Number of bytes allocated for the buffer */
	ULONG			sr_FIFOReadIndex;	/* Read index counter */
	ULONG			sr_FIFOWriteIndex;	/* Write index counter */
	ULONG			sr_FIFOBytesStored;	/* Number of bytes in the FIFO */
	BOOL			sr_FIFOOverrun;		/* TRUE if the write index counter has
										 * overrun the read index counter.
										 */
	BOOL			sr_FIFOWrapped;		/* TRUE if the write index counter has
										 * wrapped around the ring buffer.
										 */
};

struct SashimiResource * SashimiResource;
const STRPTR SashimiResourceName = "sashimi.resource";

/****************************************************************************/

extern LONG __far LVORawIOInit;
extern LONG __far LVORawMayGetChar;
extern LONG __far LVORawPutChar;

/****************************************************************************/

APTR OldRawIOInit;
APTR OldRawMayGetChar;
APTR OldRawPutChar;

/****************************************************************************/

VOID __saveds __asm
NewRawIOInit(VOID)
{
}

LONG __saveds __asm
NewRawMayGetChar(VOID)
{
	return('y');
}

/****************************************************************************/

VOID __saveds __asm
NewRawPutChar(register __d0 UBYTE c)
{
	/* Do not store NUL bytes. */
	if(c != '\0')
	{
		STATIC ULONG Position = 0;

		/* Filter out extra <cr> characters. */
		if(c != '\r' || Position > 0)
		{
			struct SashimiResource * sr = SashimiResource;

			/* Store another bbyte in the buffer */
			sr->sr_FIFO[sr->sr_FIFOWriteIndex] = c;
			sr->sr_FIFOWriteIndex = (sr->sr_FIFOWriteIndex + 1) % sr->sr_FIFOTotalSize;

			/* Check if the ring buffer was overrun */
			sr->sr_FIFOBytesStored++;
			if(sr->sr_FIFOBytesStored >= sr->sr_FIFOTotalSize)
			{
				sr->sr_FIFOOverrun = TRUE;

				/* Move the read index to the same position as
				 * the write index.
				 */
				sr->sr_FIFOReadIndex = sr->sr_FIFOWriteIndex;
			}

			/* If the buffer wraps around, remember it. */
			if(sr->sr_FIFOWriteIndex == 0)
				sr->sr_FIFOWrapped = TRUE;

			/* Notify Sashimi every time there is an end of line
			 * character in the stream.
			 */
			if(c == '\n' || c == '\r')
				Signal(sr->sr_Owner,sr->sr_OwnerSigMask);
		}

		if(c == '\r' || c == '\n')
			Position = 0;
		else
			Position++;
	}
}

/****************************************************************************/

VOID
RemovePatches(VOID)
{
	APTR res;

	/* We disable the interrupts because the raw I/O routines can
	 * be called from within interrupt code.
	 */
	Disable();

	/* For every patch planted, remove it and check whether the code
	 * had been patched before. If it has, restore the patch. Note that
	 * this is not bullet proof :(
	 */
	res = SetFunction(SysBase,(LONG)&LVORawIOInit,(ULONG (*)())OldRawIOInit);
	if(res != (APTR)NewRawIOInit)
		SetFunction(SysBase,(LONG)&LVORawIOInit,(ULONG (*)())res);

	res = SetFunction(SysBase,(LONG)&LVORawMayGetChar,(ULONG (*)())OldRawMayGetChar);
	if(res != (APTR)NewRawMayGetChar)
		SetFunction(SysBase,(LONG)&LVORawMayGetChar,(ULONG (*)())res);

	res = SetFunction(SysBase,(LONG)&LVORawPutChar,(ULONG (*)())OldRawPutChar);
	if(res != (APTR)NewRawPutChar)
		SetFunction(SysBase,(LONG)&LVORawPutChar,(ULONG (*)())res);

	Enable();
}

VOID
InstallPatches(VOID)
{
	/* We disable the interrupts because the raw I/O routines can
	 * be called from within interrupt code.
	 */
	Disable();

	OldRawIOInit		= SetFunction(SysBase,(LONG)&LVORawIOInit,		(ULONG (*)())NewRawIOInit);
	OldRawMayGetChar	= SetFunction(SysBase,(LONG)&LVORawMayGetChar,	(ULONG (*)())NewRawMayGetChar);
	OldRawPutChar		= SetFunction(SysBase,(LONG)&LVORawPutChar,		(ULONG (*)())NewRawPutChar);

	Enable();
}

/****************************************************************************/

VOID
FreeSashimiResource(struct SashimiResource * resource)
{
	if(resource != NULL)
	{
		FreeSignal(resource->sr_OwnerSigBit);

		FreeVec(resource->sr_FIFO);
		FreeVec(resource);
	}
}

LONG
RemoveSashimiResource(struct SashimiResource * resource)
{
	LONG error = OK;

	if(resource != NULL)
	{
		Forbid();

		/* Allow the resource to be removed only if
		 * there are no customers using it.
		 */
		if(resource->sr_Library.lib_OpenCnt == 0)
		{
			RemResource(resource);
		}
		else
		{
			error = ERROR_OBJECT_IN_USE;
		}

		Permit();
	}

	return(error);
}

LONG
AddSashimiResource(ULONG bufferSize,struct SashimiResource ** resourcePtr)
{
	struct SashimiResource * resource;
	LONG error = OK;

	resource = AllocVec(sizeof(*resource),MEMF_ANY|MEMF_CLEAR|MEMF_PUBLIC);
	if(resource != NULL)
	{
		resource->sr_Library.lib_Node.ln_Name	= (char *)SashimiResourceName;
		resource->sr_Library.lib_Node.ln_Type	= NT_RESOURCE;
		resource->sr_Owner						= FindTask(NULL);
		resource->sr_FIFOTotalSize				= bufferSize;

		resource->sr_OwnerSigBit = AllocSignal(-1);
		if(resource->sr_OwnerSigBit != -1)
		{
			resource->sr_OwnerSigMask = (1UL << resource->sr_OwnerSigBit);

			resource->sr_FIFO = AllocVec(resource->sr_FIFOTotalSize,MEMF_ANY|MEMF_PUBLIC);
			if(resource->sr_FIFO != NULL)
			{
				Forbid();

				/* Do not add the resource if it has already been installed. */
				if(OpenResource((STRPTR)SashimiResourceName) == NULL)
				{
					AddResource(resource);
				}
				else
				{
					error = ERROR_OBJECT_EXISTS;
				}

				Permit();
			}
			else
			{
				error = ERROR_NO_FREE_STORE;
			}
		}
		else
		{
			error = ERROR_NO_FREE_STORE;
		}
	}
	else
	{
		error = ERROR_NO_FREE_STORE;
	}

	if(error != OK)
	{
		FreeSashimiResource(resource);
		resource = NULL;
	}

	*resourcePtr = resource;

	return(error);
}

/****************************************************************************/

VOID
CloseSashimiResource(struct SashimiResource * resource)
{
	if(resource != NULL)
	{
		Forbid();

		resource->sr_Library.lib_OpenCnt--;

		Permit();
	}
}

struct SashimiResource *
OpenSashimiResource(VOID)
{
	struct SashimiResource * resource;

	Forbid();

	resource = OpenResource((STRPTR)SashimiResourceName);
	if(resource != NULL)
	{
		resource->sr_Library.lib_OpenCnt++;
	}

	Permit();

	return(resource);
}

/****************************************************************************/

VOID
EmptyBuffer(struct SashimiResource * resource)
{
	Disable();

	resource->sr_FIFOReadIndex		= 0;
	resource->sr_FIFOWriteIndex		= 0;
	resource->sr_FIFOBytesStored	= 0;
	resource->sr_FIFOOverrun		= FALSE;
	resource->sr_FIFOWrapped		= FALSE;

	Enable();
}

LONG
SaveBuffer(const STRPTR name,struct SashimiResource * resource)
{
	STRPTR bufferCopy;
	LONG totalSize;
	LONG error = OK;

	totalSize = resource->sr_FIFOTotalSize;

	/* We allocate a temporary buffer to store the ring
	 * buffer data in.
	 */
	bufferCopy = AllocVec(totalSize,MEMF_ANY|MEMF_PUBLIC);
	if(bufferCopy != NULL)
	{
		BOOL wrapped;
		BOOL overrun;
		LONG writeIndex;
		LONG bytesInBuffer;
		BPTR file;

		wrapped = resource->sr_FIFOWrapped;
		overrun = resource->sr_FIFOOverrun;

		/* The index counter indicates how many bytes
		 * have been written to the ring buffer, unless
		 * the buffer index has wrapped around already.
		 */
		writeIndex = bytesInBuffer = resource->sr_FIFOWriteIndex;

		/* If the buffer index has wrapped around, then
		 * the entire buffer is filled with data.
		 */
		if(wrapped)
			bytesInBuffer = totalSize;

		/* Make a copy of the current buffer contents. */
		if(bytesInBuffer > 0)
		{
			Forbid();

			/* For a wrapped buffer, "unfold" the FIFO. */
			if(writeIndex < bytesInBuffer && wrapped)
			{
				/* Store the oldest buffer contents first. */
				CopyMem(resource->sr_FIFO + writeIndex,bufferCopy,bytesInBuffer - writeIndex);

				/* Add the most recent data. */
				CopyMem(resource->sr_FIFO,bufferCopy + bytesInBuffer - writeIndex,writeIndex);
			}
			else
			{
				CopyMem(resource->sr_FIFO,bufferCopy,bytesInBuffer);
			}

			Permit();
		}

		/* Write the buffer contents. */
		file = Open((STRPTR)name,MODE_NEWFILE);
		if(file != NULL)
		{
			if(overrun)
			{
				if(FPrintf(file,"BUFFER WAS OVERRUN - Data may have been lost\n") < 0)
					error = IoErr();
			}

			if(error == OK && wrapped)
			{
				if(FPrintf(file,"BUFFER WRAPPED - This is the most recent captured data\n\n") < 0)
					error = IoErr();
			}

			/* FPrintf() is a buffered I/O routine, this is why we need to flush the
			 * output buffer here. Otherwise, it would be flushed after the Write()
			 * command below is finished and the file is closed. This is not what
			 * we want as that would have the effect of adding the messages above
			 * to the end of the file.
			 */
			if(error == OK)
			{
				Flush(file);
			}

			if(error == OK && bytesInBuffer > 0)
			{
				if(Write(file,bufferCopy,bytesInBuffer) != bytesInBuffer)
					error = IoErr();
			}

			Close(file);
		}
		else
		{
			error = IoErr();
		}

		FreeVec(bufferCopy);
	}
	else
	{
		error = ERROR_NO_FREE_STORE;
	}

	return(error);
}

/****************************************************************************/

int
main(int argc,char **argv)
{
	int result = RETURN_FAIL;

	/* Kickstart 2.04 and a Shell window are required. */
	if(DOSBase->lib_Version >= 37 && argc > 0)
	{
		struct RDArgs * rdargs;

		rdargs = ReadArgs((STRPTR)ShellTemplate,(LONG *)&ShellArguments,NULL);
		if(rdargs != NULL)
		{
			struct SashimiResource * resource;
			struct MsgPort * timePort = NULL;
			struct timerequest * timeRequest = NULL;
			BOOL added = FALSE;
			BOOL opened = FALSE;
			LONG error = OK;
			BPTR oldOutput = NULL;
			BPTR newOutput = NULL;
			BPTR oldInput = NULL;
			BPTR newInput = NULL;
			struct MsgPort * oldConsoleTask = NULL;
			STRPTR saveFile;

			/* Fill in the save file name, we might need it later. */
			if(ShellArguments.SaveAs != NULL)
				saveFile = ShellArguments.SaveAs;
			else
				saveFile = "T:sashimi.out";

			/* Try to open the resource, and if that fails, create one. */
			resource = OpenSashimiResource();
			if(resource != NULL)
			{
				opened = TRUE;
			}
			else
			{
				ULONG bufferSize;

				/* The default buffer size is 32K. */
				bufferSize = 32 * 1024;

				/* Check for a specific buffer size (power of two). */
				if(ShellArguments.BufferK != NULL)
					bufferSize = 1024 * (*ShellArguments.BufferK);

				/* Check for a specific buffer size. */
				if(ShellArguments.BufferSize != NULL)
					bufferSize = (ULONG)(*ShellArguments.BufferSize);

				/* Don't make the buffer too small. */
				if(bufferSize < 4096)
					bufferSize = 4096;

				/* Add the resource to the public list. Note that
				 * the patches are not installed yet.
				 */
				error = AddSashimiResource(bufferSize,&resource);
				if(error == OK)
				{
					added = TRUE;

					/* Check if we should periodically check
					 * the ring buffer.
					 */
					if(ShellArguments.TimerOn)
					{
						error = ERROR_NO_FREE_STORE;

						/* Set up the timer.device interface. */
						timePort = CreateMsgPort();
						if(timePort != NULL)
						{
							timeRequest = (struct timerequest *)CreateIORequest(timePort,sizeof(*timeRequest));
							if(timeRequest != NULL)
							{
								if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)timeRequest,0) == OK)
									error = OK;
							}
						}
					}
				}
			}

			/* Did we get everything we wanted? */
			if(error != OK)
			{
				PrintFault(error,"Sashimi");
				result = RETURN_ERROR;
			}
			else
			{
				if(opened)
				{
					/* Save the current ring buffer contents? */
					if(ShellArguments.SaveAs != NULL || ShellArguments.Save)
					{
						LONG error;

						error = SaveBuffer(saveFile,resource);
						if(error == OK)
							Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
						else
							PrintFault(error,saveFile);
					}

					/* Empty the ring buffer? */
					if(ShellArguments.Empty)
					{
						EmptyBuffer(resource);

						Printf("Sashimi buffer cleared.\n");
					}

					/* Turn off Sashimi? */
					if(ShellArguments.Off)
					{
						struct Task * owner;

						Forbid();

						/* We cannot tell Sashimi to quit
						 * if there is a single customer
						 * left, such as us. This is why
						 * we close the resource and
						 * signal Sashimi to quit.
						 */
						owner = resource->sr_Owner;
						CloseSashimiResource(resource);
						resource = NULL;

						Signal(owner,SIGBREAKF_CTRL_C);

						Permit();
					}
				}

				if(added)
				{
					ULONG signals,signalMask;
					BOOL done;

					/* Open a console window? */
					if(ShellArguments.Console)
					{
						STRPTR consoleWindow;
						LONG error = OK;

						if(ShellArguments.Window != NULL)
							consoleWindow = ShellArguments.Window;
						else
							consoleWindow = "CON:0/20/640/100/Sashimi  [Ctrl]+E=Empty  [Ctrl]+F=File  [Ctrl]+D=Reset console/AUTO/CLOSE/WAIT/INACTIVE";

						/* Open the window and make it the default
						 * I/O stream.
						 */
						newInput = Open(consoleWindow,MODE_NEWFILE);
						if(newInput != NULL)
						{
							oldConsoleTask = SetConsoleTask(((struct FileHandle *)BADDR(newInput))->fh_Type);
							newOutput = Open("CONSOLE:",MODE_OLDFILE);
							if(newOutput != NULL)
							{
								oldInput = SelectInput(newInput);
								oldOutput = SelectOutput(newOutput);
							}
							else
							{
								error = IoErr();

								/* Return to the original console task. */
								SetConsoleTask(oldConsoleTask);
								oldConsoleTask = NULL;
							}
						}
						else
						{
							error = IoErr();
						}

						if(error != OK)
							PrintFault(error,consoleWindow);
					}

					/* Show the banner message. */
					if(NOT ShellArguments.NoPrompt && NOT ShellArguments.Quiet)
					{
						struct Process * cli = (struct Process *)FindTask(NULL);
						LONG maxCli,thisCli = 1,i;

						/* Find our current CLI process number. */
						maxCli = MaxCli();
						for(i = 1 ; i <= maxCli ; i++)
						{
							if(FindCliProc(i) == cli)
							{
								thisCli = i;
								break;
							}
						}

						Printf("Sashimi installed ([Ctrl]+C or \"Break %ld\" to remove)\n",thisCli);
					}

					SashimiResource = resource;
					InstallPatches();

					signalMask = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E |
					             SIGBREAKF_CTRL_F | resource->sr_OwnerSigMask;

					/* Start the timer. */
					if(timePort != NULL)
					{
						signalMask |= (1UL << timePort->mp_SigBit);

						timeRequest->tr_node.io_Command	= TR_ADDREQUEST;
						timeRequest->tr_time.tv_secs	= 0;
						timeRequest->tr_time.tv_micro	= MILLION / 10;

						SendIO((struct IORequest *)timeRequest);
					}

					done = FALSE;
					do
					{
						signals = Wait(signalMask);

						/* Check if we should test the buffer. */
						if(timePort != NULL)
						{
							if(signals & (1UL << timePort->mp_SigBit))
							{
								signals |= resource->sr_OwnerSigMask;

								WaitIO((struct IORequest *)timeRequest);

								/* Restart the timer. */
								timeRequest->tr_node.io_Command	= TR_ADDREQUEST;
								timeRequest->tr_time.tv_secs	= 0;
								timeRequest->tr_time.tv_micro	= MILLION / 10;

								SendIO((struct IORequest *)timeRequest);
							}
						}

						/* Check if we should test the buffer. */
						if(signals & resource->sr_OwnerSigMask)
						{
							if(NOT ShellArguments.Quiet)
							{
								struct SashimiResource * sr = SashimiResource;
								UBYTE localBuffer[256];
								LONG filled = 0;

								/* Try to empty the ring buffer. */
								while(sr->sr_FIFOBytesStored > 0)
								{
									/* Read the next byte. */
									localBuffer[filled++] = sr->sr_FIFO[sr->sr_FIFOReadIndex];

									sr->sr_FIFOBytesStored--;
									sr->sr_FIFOReadIndex = (sr->sr_FIFOReadIndex + 1) % sr->sr_FIFOTotalSize;

									/* Has the buffer been filled to the brim? */
									if(filled == sizeof(localBuffer))
									{
										ULONG moreSignals;

										/* Check if there is a message for us. */
										moreSignals = SetSignal(0,SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F);

										/* Save the ring buffer to a file? */
										if(moreSignals & SIGBREAKF_CTRL_F)
										{
											LONG error;

											error = SaveBuffer(saveFile,resource);
											if(error == OK)
												Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
											else
												PrintFault(error,saveFile);
										}

										/* Empty the ring buffer? */
										if(moreSignals & SIGBREAKF_CTRL_E)
										{
											EmptyBuffer(resource);

											Printf("Sashimi buffer cleared.\n");
										}

										/* Stop Sashimi? */
										if(moreSignals & SIGBREAKF_CTRL_C)
										{
											signals |= SIGBREAKF_CTRL_C;

											filled = 0;
											break;
										}

										/* Write the buffer to the file. */
										Write(Output(),localBuffer,filled);
										filled = 0;
									}
								}

								/* Store the remaining buffer contents. */
								if(filled > 0)
									Write(Output(),localBuffer,filled);
							}
						}

						/* Save current buffer to file. */
						if(signals & SIGBREAKF_CTRL_F)
						{
							LONG error;

							error = SaveBuffer(saveFile,resource);
							if(error == OK)
								Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
							else
								PrintFault(error,saveFile);
						}

						/* Empty the buffer. */
						if(signals & SIGBREAKF_CTRL_E)
						{
							EmptyBuffer(resource);

							Printf("Sashimi buffer cleared.\n");
						}

						/* Reset the terminal. */
						if(signals & SIGBREAKF_CTRL_D)
						{
							Printf("\033c");
							Flush(Output());
						}

						/* Terminate the program. */
						if(signals & SIGBREAKF_CTRL_C)
						{
							BOOL terminate = FALSE;

							if(ShellArguments.AskExit)
							{
								UBYTE buffer[4];

								Printf("\nSashimi: stop signal received -- really exit (y or n)? ");
								Flush(Output());

								buffer[0] = '\0';

								if(FGets(Input(),buffer,sizeof(buffer)-1) != NULL)
								{
									if(buffer[0] == 'y' || buffer[0] == 'Y')
										terminate = TRUE;
								}
							}
							else
							{
								terminate = TRUE;
							}

							if(terminate)
							{
								if(RemoveSashimiResource(resource) == OK)
								{
									Printf("Sashimi removed.\n");
									done = TRUE;
								}
							}
						}
					}
					while(NOT done);

					RemovePatches();

					/* Stop the timer. */
					if(timePort != NULL)
					{
						if(CheckIO((struct IORequest *)timeRequest) == BUSY)
							AbortIO((struct IORequest *)timeRequest);

						WaitIO((struct IORequest *)timeRequest);
					}

					/* Check if we should and could save the ring buffer. */
					if(ShellArguments.AskSave && (resource->sr_FIFOWrapped || resource->sr_FIFOWriteIndex > 0))
					{
						UBYTE name[256];

						Printf("Enter name to save the buffer, or hit [Return] to cancel: ");
						Flush(Output());

						name[0] = '\0';

						if(FGets(Input(),name,sizeof(name)-1) != NULL)
						{
							LONG error;
							int i;

							for(i = strlen(name)-1 ; i >= 0 ; i--)
							{
								if(name[i] == '\n')
									name[i] = '\0';
							}

							error = SaveBuffer(name,resource);
							if(error == OK)
								Printf("Sashimi buffer saved as \"%s\".\n",name);
							else
								PrintFault(error,name);
						}
					}

					FreeSashimiResource(resource);
					resource = NULL;
				}

				result = RETURN_OK;
			}

			/* Close the resource, if we opened it. */
			if(opened)
			{
				CloseSashimiResource(resource);
			}

			/* Remove and free the resource if we added it. */
			if(added)
			{
				RemoveSashimiResource(resource);
				FreeSashimiResource(resource);
			}

			/* Clean up the timer.device interface. */
			if(timeRequest != NULL)
			{
				if(timeRequest->tr_node.io_Device != NULL)
					CloseDevice((struct IORequest *)timeRequest);

				DeleteIORequest((struct IORequest *)timeRequest);
			}

			DeleteMsgPort(timePort);

			/* Reset and clean up the console I/O streams. */
			if(oldOutput != NULL)
				SelectOutput(oldOutput);

			if(oldInput != NULL)
				SelectInput(oldInput);

			if(newOutput != NULL)
				Close(newOutput);

			if(oldConsoleTask)
				SetConsoleTask(oldConsoleTask);

			if(newInput != NULL)
				Close(newInput);

			FreeArgs(rdargs);
		}
		else
		{
			PrintFault(IoErr(),"Sashimi");

			result = RETURN_ERROR;
		}
	}

	return(result);
}
