#define __USE_SYSBASE
#include <stdio.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/iffparse.h>

static const char Version[]="$VER: Clip 1.0 (30.07.00)";

#define ID_FTXT MAKE_ID('F','T','X','T')
#define ID_CHRS MAKE_ID('C','H','R','S')

#define OUTPUTSIZE 256
#define BUFFERSIZE 4096

typedef struct {
	BPTR	Input;
	BPTR	Output;
	BPTR	StdIn;
	BPTR	StdOut;

	STRPTR	Buffer;
	ULONG	BufLen;
	STRPTR	File;
	STRPTR	String;
	ULONG	ClipUnit;

	BOOL	Store;
	BOOL	IsStdIn;
	BOOL	IsStdOut;
} MyArgs;


static int myfprintf(BPTR file, char *ctl, ...)
{
	long *arg1;
	char *buffer;
	ULONG buflen;

	/* Try to be as safe as possible without too much hassle */
	buflen = strlen(ctl)+1024;
	if (buffer = AllocMem(buflen, MEMF_ANY))
	{
		arg1 = (long *)(&ctl + 1);
		RawDoFmt(ctl, arg1, (void (*))"\x16\xc0\x4e\x75", buffer);
		Write(file, buffer, strlen(buffer));

		FreeMem(buffer, buflen);
	}

	return 0;
}

int main(int argc, char *argv[])
{
	struct Library *IFFParseBase = NULL;
	struct ContextNode *cn;	
	struct IFFHandle *iffh = NULL;
	struct ClipboardHandle *ClipHandle = NULL;

	BPTR StdErr;
	MyArgs *args = NULL;
	struct RDArgs *rdargs = NULL;
	STRPTR Template = "FILE,U=UNIT/K/N,STR=STRING/K,STORE/S,HELP/S";
	enum { TEM_FILE, TEM_UNIT, TEM_STRING, TEM_STORE, TEM_HELP, TEM_NUMARGS };
	LONG ArgArray[TEM_NUMARGS];
	LONG len = 0, i;

	StdErr = Open("CONSOLE:", MODE_NEWFILE);
	if (!(args = AllocMem(sizeof(MyArgs), MEMF_ANY | MEMF_CLEAR))) { myfprintf(StdErr, "Unable to allocate needed memory!\n"); goto exit; }

	args->StdIn = Input();
	args->StdOut = Output();

	if (IsInteractive(args->StdIn)) args->IsStdIn = TRUE;
	if (IsInteractive(args->StdOut)) args->IsStdOut = TRUE;

	for (i=0;i<TEM_NUMARGS;ArgArray[i++]=NULL);
	rdargs = ReadArgs(Template,ArgArray,NULL);

	if (ArgArray[TEM_HELP] || !rdargs)
	{
		myfprintf(StdErr,"%s by Sigbjørn (CISC) Skjæret\n\n", &Version[6]);
		myfprintf(StdErr,"Usage: %s ([<file>] | [STRING=<string>]) [UNIT=<unit>] [STORE]\n", argv[0]);
		goto exit;
	}
	if (ArgArray[TEM_FILE]) args->File = (STRPTR)ArgArray[TEM_FILE];
	if (ArgArray[TEM_STRING] && !args->File && args->IsStdIn) args->String = (STRPTR)ArgArray[TEM_STRING];
	if (ArgArray[TEM_STORE] || !args->IsStdIn || args->String) args->Store = TRUE;
	if (ArgArray[TEM_UNIT]) args->ClipUnit = *((ULONG *)ArgArray[TEM_UNIT]);
	if (args->ClipUnit>255) { myfprintf(StdErr, "Illegal unit number: %ld!\n", args->ClipUnit); goto exit; }

	if (args->Store)
	{
		if (!args->IsStdIn)
		{
			args->Input = args->StdIn;
			if (args->File)
			{
				if (!(args->Output = Open(args->File, MODE_NEWFILE))) { myfprintf(StdErr, "Unable to open file \"%s\".\n", args->File); goto exit; }
			}
		}
		else if (args->File)
		{
			if (!(args->Input = Open(args->File, MODE_OLDFILE))) { myfprintf(StdErr, "Unable to open file \"%s\".\n", args->File); goto exit; }
		}
		else args->Input = args->StdIn;
	} else {
		if (args->File)
		{
			if (!(args->Output = Open(args->File, MODE_NEWFILE))) { myfprintf(StdErr, "Unable to open file\"%s\".\n", args->File); goto exit; }
		}
		else args->Output = args->StdOut;
	}

	if (!(IFFParseBase = OpenLibrary("iffparse.library",39))) { myfprintf(StdErr, "Couldn't open iffparse.library v39+!\n"); goto exit; }
	if (!(iffh = AllocIFF())) { myfprintf(StdErr, "Unable to allocate IFF structure!\n"); goto exit; }
	InitIFFasClip(iffh);
	if (!(ClipHandle = OpenClipboard(args->ClipUnit))) { myfprintf(StdErr, "Couldn't open clipboard.device!\n"); goto exit; }
	iffh->iff_Stream = (ULONG)ClipHandle;

	if (args->Store)
	{
		if (OpenIFF(iffh, IFFF_WRITE)) { myfprintf(StdErr, "Couldn't open clipboard for write.\n"); goto exit; }
		if (PushChunk(iffh, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)) { myfprintf(StdErr, "Unable to write to clipboard.\n"); goto exit; }
		if (PushChunk(iffh, ID_FTXT, ID_CHRS, IFFSIZE_UNKNOWN)) { myfprintf(StdErr, "Unable to write to clipboard.\n"); goto exit; }

		if (args->String)
		{
			i = strlen(args->String);
			if ((len = WriteChunkBytes(iffh, args->String, i)) < 0) { myfprintf(StdErr, "Unable to write to clipboard.\n"); goto exit; }
			if (args->Output) Write(args->Output, args->String, i);
			if (!args->IsStdOut) Write(args->StdOut, args->String, i);
		} else {
			if (!(args->Buffer = AllocMem((args->BufLen = BUFFERSIZE), MEMF_ANY))) { myfprintf(StdErr, "Unable to allocate needed memory!\n"); goto exit; }
			while ((i = Read(args->Input, args->Buffer, args->BufLen)) > 0)
			{
				if ((len += WriteChunkBytes(iffh, args->Buffer, i)) < 0) { myfprintf(StdErr, "Unable to write to clipboard.\n"); goto exit; }
				if (args->Output) Write(args->Output, args->Buffer, i);
				if (!args->IsStdOut) Write(args->StdOut, args->Buffer, i);
			}
		}
		if (PopChunk(iffh)) { myfprintf(StdErr, "Unable to finalize clipboard entry.\n"); goto exit; }
	} else {
		if (OpenIFF(iffh, IFFF_READ)) { myfprintf(StdErr, "Couldn't open clipboard for read."); goto exit; }
		if (StopChunk(iffh, ID_FTXT, ID_CHRS)) { myfprintf(StdErr, "Unable to register IFF chunk.\n"); goto exit; }

		if (ParseIFF(iffh, IFFPARSE_SCAN)) { myfprintf(StdErr, "Clipboard unit %ld is empty or contains invalid data.\n", args->ClipUnit); goto exit; }
		if (!(cn = CurrentChunk(iffh))) { myfprintf(StdErr, "Clipboard unit %ld contains invalid data.\n", args->ClipUnit); goto exit; }

		if ((cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS))
		{
			if (!(args->Buffer = AllocMem((args->BufLen = cn->cn_Size+1), MEMF_ANY))) { myfprintf(StdErr, "Unable to allocate needed memory!\n"); goto exit; }
			if ((len = ReadChunkBytes(iffh, args->Buffer, cn->cn_Size)) < 0) { myfprintf(StdErr, "Unable to read from clipboard.\n"); goto exit; }

			if (!args->File && args->IsStdOut && args->Buffer[len-1] != 0x0A) args->Buffer[len++] = 0x0A;

			if (args->File || !args->IsStdOut)
			{
				if (args->File && !args->IsStdOut) Write(args->StdOut, args->Buffer, len);
				if (args->Output) Write(args->Output, args->Buffer, len);
			} else if (args->Output) {
				for (i=0;i<=len-OUTPUTSIZE;i+=OUTPUTSIZE)
				{
					if (SetSignal(0,0) & SIGBREAKF_CTRL_C) goto exit;
					Write(args->Output, args->Buffer+i, OUTPUTSIZE);
				}
				if (i < len) Write(args->Output, args->Buffer+i, len-i);
			}
		}
	}

exit:
	if (args)
	{
		if (args->Input && (args->Input != args->StdIn))
			Close(args->Input);

		if (args->Output && (args->Output != args->StdOut))
			Close(args->Output);

		if (args->Buffer)
			FreeMem(args->Buffer, args->BufLen);

		FreeMem(args, sizeof(MyArgs));
	}

	if (iffh)
	{
		CloseIFF(iffh);

		if (ClipHandle)
			CloseClipboard(ClipHandle);

		FreeIFF(iffh);
	}
	if (IFFParseBase)
		CloseLibrary(IFFParseBase);

	if (rdargs)
		FreeArgs(rdargs);

	Close(StdErr);
	return 0;
}
