/*
**	clip-handler.c
**	DOS handler for Clipboard access
**	Copyright © 1995 Michael Letowski
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <libraries/iffparse.h>
#include <devices/clipboard.h>
#include <support/types.h>
#include <support/exec.h>
#include <support/dos.h>

#include <string.h>
#include <stdlib.h>

#include <proto/exec.h>
#include <proto/iffparse.h>

#include "clip-handler.rev.h"

#define DOS_NAME		"dos.library"
#define DOS_VERN 		37L
#define IFFP_NAME		"iffparse.library"
#define IFFP_VERN		37L
#define CLIP_NAME		"clipboard.device"

STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);

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

#define PATH_SIZE		256

/* Library bases */
struct ExecBase *SysBase;
struct Library *IFFParseBase;

STATIC struct DosPacket *WaitPacket(struct MsgPort *port);
STATIC VOID ReplyPacket(struct DosPacket *packet, struct MsgPort *port);
STATIC VOID ReplyPacketRes(struct DosPacket *packet, struct MsgPort *port,
														LONG res1, LONG res2);
STATIC struct IFFHandle *OpenCH(struct DosPacket *packet);
STATIC VOID CloseCH(struct IFFHandle *iff);
STATIC LONG OpenInputCH(struct DosPacket *packet);
STATIC LONG OpenOutputCH(struct DosPacket *packet);
STATIC LONG OpenUpdateCH(struct DosPacket *packet);
STATIC LONG CloseIOUCH(struct DosPacket *packet);
STATIC VOID ReadClipboard(struct DosPacket *packet);
STATIC VOID WriteClipboard(struct DosPacket *packet);
STATIC VOID SeekClipboard(struct DosPacket *packet);
STATIC LONG IFF2DOS(LONG iffError);

SAVEDS LONG ClipHandler(VOID)
{
	struct Library *LocalIFFParseBase;

	struct Process *PR;
	struct MsgPort *MP,*PacketPort;
	struct DosPacket *Pkt;
	struct DeviceNode *DevNode;
	LBOOL Done=FALSE;
	ULONG OpenCount=0;

	SysBase=*((struct ExecBase **)4);							/* Set up SysBase */
	PR=ThisProcessS;
	MP=&PR->pr_MsgPort;

	if(PR->pr_CLI)																/* Run from shell */
		return(RETURN_FAIL);												/* Failure */

	Pkt=WaitPacket(MP);														/* Get startup message */
	try(DevNode=(struct DeviceNode *)BADDR(Pkt->dp_Arg3),NO_DEV);

	/* Open IFFParse */
	unless(LocalIFFParseBase=OpenLibrary(IFFP_NAME,IFFP_VERN))
	{
		ReplyPacketRes(Pkt,MP,DOSFALSE,ERROR_INVALID_RESIDENT_LIBRARY);
		throw(NO_IFFPARSE);
	}
	/* Create port */
	unless(PacketPort=CreateMsgPort())
	{
		ReplyPacketRes(Pkt,MP,DOSFALSE,ERROR_NO_FREE_STORE);
		throw(NO_PORT);
	}
	/* Set up global bases */
	IFFParseBase=LocalIFFParseBase;								/* Set up global bases */
	DevNode->dn_Task=PacketPort;
	Pkt->dp_Arg4=(LONG)PacketPort;
	ReplyPacketRes(Pkt,PacketPort,DOSTRUE,Pkt->dp_Res2);

	while(!Done)
	{
		Pkt=WaitPacket(PacketPort);
		switch(Pkt->dp_Type)
		{
			case ACTION_FINDINPUT:
				OpenCount+=OpenInputCH(Pkt);
				break;
			case ACTION_FINDOUTPUT:
				OpenCount+=OpenOutputCH(Pkt);
				break;
/*			case ACTION_FINDUPDATE:
				OpenCount+=OpenUpdateCH(Pkt);*/
				break;
			case ACTION_END:
				OpenCount-=CloseIOUCH(Pkt);
				break;
			case ACTION_READ:
				ReadClipboard(Pkt);
				break;
			case ACTION_WRITE:
				WriteClipboard(Pkt);
				break;
/*			case ACTION_SEEK:
				SeekClipboard(Pkt);*/
				break;
			case ACTION_DIE:
				if(OpenCount)
				{
					Pkt->dp_Res1=DOSFALSE;
					Pkt->dp_Res2=ERROR_OBJECT_IN_USE;
				}
				else
				{
					Pkt->dp_Res1=DOSTRUE;
					Pkt->dp_Res2=0;
					Done=TRUE;
				}
			case ACTION_IS_FILESYSTEM:
				Pkt->dp_Res1=DOSFALSE;
				Pkt->dp_Res2=0;
				break;
			default:
				Pkt->dp_Res1=DOSFALSE;
				Pkt->dp_Res2=ERROR_ACTION_NOT_KNOWN;
				break;
		}
		ReplyPacket(Pkt,PacketPort);
	}
	DevNode->dn_Task=NULL;												/* Switch off device */

	except(NO_PORT,DeleteMsgPort(PacketPort));
	except(NO_IFFPARSE,CloseLibrary(LocalIFFParseBase));
	except(NO_DEV,);
	return(0);
}	/* ClipHandler */

STATIC struct DosPacket *WaitPacket(struct MsgPort *port)
{
	WaitPort(port);
	return((struct DosPacket *)GetMsg(port)->mn_Node.ln_Name);
}	/* WaitPacket */

STATIC VOID ReplyPacket(struct DosPacket *packet, struct MsgPort *port)
{
	struct MsgPort *ReplyPort=packet->dp_Port;

	packet->dp_Port=port;
	packet->dp_Link->mn_Node.ln_Name=(STRPTR)packet;
	PutMsg(ReplyPort,packet->dp_Link);
}	/* ReplyPacket */

STATIC VOID ReplyPacketRes(struct DosPacket *packet, struct MsgPort *port,
														LONG res1, LONG res2)
{
	packet->dp_Res1=res1;
	packet->dp_Res2=res2;
	ReplyPacket(packet,port);
}	/* ReplyPacketRes */

STATIC struct IFFHandle *OpenCH(struct DosPacket *packet)
{
	CHAR Name[PATH_SIZE];
	STRPTR Str=(STRPTR)BADDR(packet->dp_Arg3);
	struct FileHandle *Handle=FH(packet->dp_Arg1);
	struct IFFHandle *IFF;
	LONG Unit;

	packet->dp_Res1=DOSFALSE;
	packet->dp_Res2=0;
	Handle->fh_Arg1=0;
	Handle->fh_Port=NULL;													/* Non-interactive file */

	memcpy(Name,&Str[1],Str[0]);									/* Copy data */
	Name[Str[0]]='\0';														/* NULL-terminate */

	Str=Name;
	if(Str=strchr(Str,':'))												/* Device name given */
		Str++;																			/* Skip it */
	Unit=atol(Str);																/* Convert to unit number */

	unless(IFF=AllocIFF())
	{
		packet->dp_Res2=ERROR_NO_FREE_STORE;
		throw(NO_IFF);
	}
	unless(IFF->iff_Stream=(ULONG)OpenClipboard(Unit))
	{
		packet->dp_Res2=ERROR_NO_FREE_STORE;				/* FIX!!! */
		throw(NO_CLIPBOARD);
	}
	InitIFFasClip(IFF);
	Handle->fh_Arg1=(LONG)IFF;
	packet->dp_Res1=DOSTRUE;
	return(IFF);

	except(NO_CLIPBOARD,CloseClipboard((struct ClipboardHandle *)IFF->iff_Stream));
	except(NO_IFF,FreeIFF(IFF));
	return(NULL);
}	/* OpenCH */

STATIC VOID CloseCH(struct IFFHandle *iff)
{
	CloseClipboard((struct ClipboardHandle *)iff->iff_Stream);
	FreeIFF(iff);
}	/* CloseCH */

STATIC LONG OpenInputCH(struct DosPacket *packet)
{
	struct IFFHandle *IFF;
	LONG Error;

	try(IFF=OpenCH(packet),NO_HANDLE);
	try(!(Error=OpenIFF(IFF,IFFF_READ)),NO_IFF);
	try(!(Error=StopChunk(IFF,ID_FTXT,ID_CHRS)),IFF_ERROR);
	try(!(Error=ParseIFF(IFF,IFFPARSE_SCAN)),IFF_ERROR);
	return(1);																		/* Open succesfull */

	except(IFF_ERROR,);
	packet->dp_Res1=DOSFALSE;
	packet->dp_Res2=IFF2DOS(Error);								/* Set result */
	except(NO_IFF,CloseIFF(IFF));
	except(NO_HANDLE,CloseCH(IFF));
	return(0);																		/* Open failed */
}	/* OpenInputCH */

STATIC LONG OpenOutputCH(struct DosPacket *packet)
{
	struct IFFHandle *IFF;
	LONG Error;

	try(IFF=OpenCH(packet),NO_HANDLE);
	try(!(Error=OpenIFF(IFF,IFFF_WRITE)),NO_IFF);
	try(!(Error=PushChunk(IFF,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN)),IFF_ERROR);
	try(!(Error=PushChunk(IFF,0,ID_CHRS,IFFSIZE_UNKNOWN)),IFF_ERROR);
	return(1);																		/* Open succesfull */

	except(IFF_ERROR,);
	packet->dp_Res1=DOSFALSE;
	packet->dp_Res2=IFF2DOS(Error);								/* Set result */
	except(NO_IFF,CloseIFF(IFF));
	except(NO_HANDLE,CloseCH(IFF));
	return(0);																		/* Open failed */
}	/* OpenOutputCH */

STATIC LONG OpenUpdateCH(struct DosPacket *packet)
{
	struct IFFHandle *IFF;
	LONG Error;

	try(IFF=OpenCH(packet),NO_HANDLE);
	try(!(Error=OpenIFF(IFF,IFFF_WRITE)),NO_IFF);
	try(!(Error=StopChunk(IFF,ID_FTXT,ID_CHRS)),IFF_ERROR);
	try(!(Error=ParseIFF(IFF,IFFPARSE_SCAN)),IFF_ERROR);	/* FIX: Should use write mode here */
	return(1);																		/* Open succesfull */

	except(IFF_ERROR,);
	packet->dp_Res1=DOSFALSE;
	packet->dp_Res2=IFF2DOS(Error);								/* Set result */
	except(NO_IFF,CloseIFF(IFF));
	except(NO_HANDLE,CloseCH(IFF));
	return(0);																		/* Open failed */
}	/* OpenUpdateCH */

STATIC LONG CloseIOUCH(struct DosPacket *packet)
{
	struct IFFHandle *IFF=(struct IFFHandle *)packet->dp_Arg1;

#if 0
	/* FIX:??? Check PopChunk() for errors */
	if(ftst(IFF->iff_Flags,IFFF_WRITE))						/* Write mode */
		if(PopChunk(IFF)==0)												/* Close CHRS */
			PopChunk(IFF);														/* Close FTXT */
#endif
	CloseIFF(IFF);
	CloseCH(IFF);
	packet->dp_Res1=DOSTRUE;
	packet->dp_Res2=0;
	return(1);
}	/* CloseIOUCH */

STATIC VOID ReadClipboard(struct DosPacket *packet)
{
	struct IFFHandle *IFF=(struct IFFHandle *)packet->dp_Arg1;
	STRPTR Buffer=(STRPTR)packet->dp_Arg2;
	LONG Read=-1,Bytes=packet->dp_Arg3;

	packet->dp_Res1=-1;
	packet->dp_Res2=0;

	Read=ReadChunkBytes(IFF,Buffer,Bytes);				/* Read into buffer */
	if(Read<0)
		packet->dp_Res2=IFF2DOS(Read);
	else
		packet->dp_Res1=Read;
}	/* ReadClipboard */

STATIC VOID WriteClipboard(struct DosPacket *packet)
{
	struct IFFHandle *IFF=(struct IFFHandle *)packet->dp_Arg1;
	STRPTR Buffer=(STRPTR)packet->dp_Arg2;
	LONG Written=-1,Bytes=packet->dp_Arg3;

	packet->dp_Res1=-1;
	packet->dp_Res2=0;

	Written=WriteChunkBytes(IFF,Buffer,Bytes);		/* Write buffer */
	if(Written<0)
		packet->dp_Res2=IFF2DOS(Written);
	else
		packet->dp_Res1=Written;
}	/* WriteClipboard */

STATIC VOID SeekClipboard(struct DosPacket *packet)
{
	struct IFFHandle *IFF=(struct IFFHandle *)packet->dp_Arg1;
	struct ContextNode *CN;
	LONG Offset=packet->dp_Arg2,OffsetType=packet->dp_Arg3;
	LONG OldPos,NewPos=-1;

	packet->dp_Res1=-1;
	packet->dp_Res2=ERROR_SEEK_ERROR;

	unless(CN=CurrentChunk(IFF))
		return;
	unless(CN->cn_Type==ID_FTXT && CN->cn_ID==ID_CHRS)
		return;

	switch(OffsetType)
	{
		case OFFSET_BEGINNING:
			NewPos=Offset;
			break;
		case OFFSET_CURRENT:
			NewPos=CN->cn_Scan+Offset;
			break;
		case OFFSET_END:
			NewPos=CN->cn_Size-Offset;
			break;
	}
	if(NewPos>0 && NewPos<CN->cn_Size)						/* Can seek */
	{
		OldPos=CN->cn_Scan;
		CN->cn_Scan=NewPos;
		packet->dp_Res1=OldPos;
		packet->dp_Res2=0;
	}
}	/* SeekClipboard */

STATIC LONG IFF2DOS(LONG iffError)
{
	LONG DOSError;

	switch(iffError)
	{
		case IFFERR_EOF:
		case IFFERR_EOC:
		case IFFERR_NOSCOPE:
		case IFFERR_MANGLED:
		case IFFERR_SYNTAX:
		case IFFERR_NOTIFF:
			DOSError=ERROR_OBJECT_WRONG_TYPE;
			break;
		case IFFERR_NOMEM:
			DOSError=ERROR_NO_FREE_STORE;
			break;
		case IFFERR_SEEK:
			DOSError=ERROR_SEEK_ERROR;
			break;
		case IFFERR_NOHOOK:
			DOSError=ERROR_NOT_IMPLEMENTED;
			break;
		default:
			DOSError=ThisProcessS->pr_Result2;
			break;
	}
	return(DOSError);
}	/* IFF2DOS */
