/*
 *  clipboard.c 
 *  by Robert R. Burns
 *
 * Copyright (c) 1988 Commodore-Amiga, Inc.
 *
 * Executables based on this information may be used in software
 * for Commodore Amiga computers.  All other rights reserved.
 *
 * This information is provided "as is"; no warranties are made.
 * All use is at your own risk, and no liability or responsibility is assumed.
 *
 *  The devs:mountlist needed:
 * 
 *--------------------------------------------------------------------
 *  CLIP:       Handler   = l:clipboard-handler
 *              Stacksize = 5000
 *              Priority  = 5
 *              GlobVec   = 1
 *  #
 *--------------------------------------------------------------------
 *
 * I have done this under MANX, but I don't see too much trouble getting
 * it to work with Lattice...just don't use any startup code and
 * disable the stack checking on LC2 with '-v'. 
 */

#include	"exec/types.h"
#include	"exec/nodes.h"
#include	"exec/lists.h"
#include	"exec/ports.h"
#include	"exec/libraries.h"
#include	"exec/devices.h"
#include	"exec/io.h"
#include	"exec/memory.h"
#include	"intuition/intuition.h"
#include	"libraries/dos.h"
#include	"libraries/dosextens.h"
#include	"libraries/filehandler.h"
#include	"devices/clipboard.h"

#ifdef MANX
#include	"functions.h"

ULONG SysBase,		/* these are here to make the startup code happy */
    _savsp;		/* (this is unique to Manx Aztec C startup) */
#endif

struct Process *FindTask();
struct Message *GetMsg();
struct ClipArg *AllocMem();

/* A version of BADDR() has no problems with casting */
#undef  BADDR
#define BADDR(x)	((APTR)((long)x << 2))

#ifndef	ACTION_FIND_INPUT
#define ACTION_FIND_INPUT       1005L	/* please refer to DOS Tech. Ref. */
#endif
#ifndef	ACTION_FIND_OUTPUT
#define ACTION_FIND_OUTPUT      1006L
#endif
#ifndef	ACTION_END
#define ACTION_END              1007L
#endif
#ifndef	ACTION_SEEK
#define	ACTION_SEEK		1008L
#endif

struct ClipArg {
    struct IOClipReq ca_IOR;
    struct DosPacket *ca_Packet;	/* active dos packet */
    void (*ca_ReplyPacket)();		/* (*ca_ReplyPacket)(process, ca, ) */
    ULONG ca_Data;			/* (used to hold seek arguments) */
    ULONG ca_Argument;			/*  "  */
    ULONG ca_Offset;			/*  "  */
    UWORD ca_Direction;			/* read or write */
};

void 
returnPacket(process, packet, res1)
struct Process *process;
struct DosPacket *packet;
ULONG res1;
{
    struct Message *message;
    struct MsgPort *replyport;

    packet->dp_Res1 = res1;
    replyport = packet->dp_Port;
    packet->dp_Port = &process->pr_MsgPort;
    message = packet->dp_Link;
    message->mn_Node.ln_Name = (char *) packet;

#ifdef	DEBUG
kprintf("returnPacket: %ld %ld\t", packet->dp_Res1, packet->dp_Res2);
#endif

    PutMsg(replyport, message);
}


void
replyClip(process, ca, openCnt)
struct Process *process;
struct ClipArg *ca;
int *openCnt;
{
#ifdef	DEBUG
kprintf("replyClip... ");
#endif
    if (ca->ca_IOR.io_Error) {
	/* Return an indecipherable error */
	ca->ca_Packet->dp_Res2 = 1000+ca->ca_IOR.io_Error;
	returnPacket(process, ca->ca_Packet, -1);
    }
    else {
	returnPacket(process, ca->ca_Packet, ca->ca_IOR.io_Actual);
    }
}

void
seekClip(process, ca, openCnt)
struct Process *process;
struct ClipArg *ca;
int *openCnt;
{
#ifdef	DEBUG
kprintf("seekClip... ");
#endif
    if ((ca->ca_IOR.io_Error) || (ca->ca_IOR.io_Actual != 4L)) {
	/* cannot seek from end of file */
	ca->ca_Packet->dp_Res2 = ERROR_SEEK_ERROR;
	returnPacket(process, ca->ca_Packet, -1);
    }
    else {
	ca->ca_IOR.io_Offset = ca->ca_Data + ca->ca_Argument + 8;
	returnPacket(process, ca->ca_Packet, ca->ca_Offset);
    }
}


void
closeClip(process, ca, openCnt)
struct Process *process;
struct ClipArg *ca;
int *openCnt;
{
    struct DosPacket *packet;
#ifdef	DEBUG
kprintf("closeClip %ld... ", ca->ca_IOR.io_Error);
#endif

    /* cache the packet */
    packet = ca->ca_Packet;
    /* close the clipboard device */
    CloseDevice(&ca->ca_IOR);
    /* free the clipboard IO request */
    FreeMem(ca, sizeof(struct ClipArg));
    (*openCnt)--;
    returnPacket(process, packet, DOSTRUE);
}


_main()
{
    /* handler related data structures */
    struct Process *myProcess;		/* my process */
    struct DeviceNode *myDevNode;	/* our device node passed in Arg3 */
    struct DosPacket *packet;		/* a pointer to a dos packet sent */
    struct MsgPort clipReplyPort;	/* the reply port for clipboard IO */
    struct IOClipReq openIOR;		/* clipboard request to keep dev open */
    struct ClipArg *ca;			/* clipboard.device IO request, etc. */
    struct FileHandle *fh;		/* a pointer to a file handle */
    ULONG sigPacket, sigClipReply;	/* individual signal masks */
    ULONG sigWait;			/* combined signal mask */
    ULONG signal;			/* current signal mask */
    UWORD unit;				/* clipboard unit number */
    int openCnt;			/* access counter to this handler */
    char alive;				/* absence of ACTION_DIE */
    struct Message *message;		/* a message temporary */
    char *name;				/* filename temporary */
    int result, i;			/* some temporarys */

#ifdef DEBUG
    kprintf("\n********************\nStart CLIP:\n");
#endif

    /*
     * Initialization: initialize some things while the parameter packet
     *   gets here
     */
    myProcess = (struct Process *) FindTask(0L);	/* find myself */

    /* set up the message port for clipboard I/O requests */
    clipReplyPort.mp_Node.ln_Type = NT_MSGPORT;
    clipReplyPort.mp_Flags = 0;
    clipReplyPort.mp_SigBit = AllocSignal(-1);
    clipReplyPort.mp_SigTask = (struct Task *) myProcess;
    NewList(&clipReplyPort.mp_MsgList);

    /* set the signal masks */
    sigPacket = 1 << myProcess->pr_MsgPort.mp_SigBit;
    sigClipReply = 1 << clipReplyPort.mp_SigBit;
    sigWait = sigPacket | sigClipReply;

    alive = TRUE;					/* handler loop flag */
    openCnt = 0;					/* handler open count */
    /*
     * Get Parameters: since this is a non-BCPL module it gets sent the
     *   parameter pkt. (BCPL modules get it in D1)
     */

    while ((message = GetMsg(&myProcess->pr_MsgPort)) == NULL)
	/* wait for parameter packet */
	Wait(sigPacket);

#ifdef DEBUG
    kprintf("Got Parmeter Packet\n");
#endif

    /*
     * open the clipboard.device so it will stay open throughout the duration
     * of the life of the CLIP: handler.  Otherwise, the clip goes to disk
     * a lot.
     */
    openIOR.io_Message.mn_ReplyPort = &clipReplyPort;
    openIOR.io_Device = 0;
    OpenDevice("clipboard.device", 0, &openIOR, 0);

    /* get the pointer to the device node */
    packet = (struct DosPacket *) (message->mn_Node.ln_Name);
    myDevNode = (struct DeviceNode *) BADDR(packet->dp_Arg3);

    /*
     * install our taskid ... if we don't...for every reference
     * to our handler a NEW process will be created.  This is
     * fine for things like CON: (console handler) but if you
     * plan to be the only dude on block ( like the file-system
     * handler or SER: ) you should fill the task field with
     * your taskid (ie.  &(pr_MsgPort) ) Note:
     * remember that shared code has to be reentrant. (like
     * CON: handler) ( keep your variables on the stack
     * [autos], and allocate memory for larger data structures
     * and "FLAG" global data structures that need only be
     * intialized once )
     */
    myDevNode->dn_Task = &myProcess->pr_MsgPort;

#ifdef DEBUG
    kprintf("Returning parmeter packet\n");
#endif

    /* indicate initialization was successful */
    returnPacket(myProcess, packet, DOSTRUE);

    /*
     * Main Loop: look for things to do that are either
     *   1. New packets to this handler
     *   2. Completions of clipboard.device IO and thus of an associated packet
     */
    while (alive) {			/* start of the real work */
#ifdef DEBUG
    kprintf("Waiting... ");
#endif
	signal = Wait(sigWait);

	/*
	 * Packet handler: handle the packet
	 */
	if (signal & sigPacket) {
	    while (message = GetMsg(&myProcess->pr_MsgPort)) {
	    packet = (struct DosPacket *) (message->mn_Node.ln_Name);
	    switch (packet->dp_Type) {
		case ACTION_FIND_INPUT:
		case ACTION_FIND_OUTPUT:
		    /*
		     * Open: be flexible about opening for read or write here
		     */
#ifdef	DEBUG
	kprintf("Open: \"");
	for (i = 1; i <= *(char *) BADDR(packet->dp_Arg3); i++)
	    kprintf("%lc", ((char *) BADDR(packet->dp_Arg3))[i]);
	kprintf("\"\t");
#endif
		    fh = (struct FileHandle *) BADDR(packet->dp_Arg1);
		    /* allocate the clipboard IO request */
		    ca = (struct ClipArg *)
			    AllocMem(sizeof(struct ClipArg), MEMF_CLEAR);
		    fh->fh_Port = DOSFALSE;	/* not interactive */
		    if (fh->fh_Arg1 = (LONG) ca) {
			ca->ca_IOR.io_Message.mn_ReplyPort = &clipReplyPort;
			/* get the clip unit from the name, if it exists */
			unit = 0;
			name = (char *) BADDR(packet->dp_Arg3);
			/*   find colon of clip device name */
			for (i = 1; (i <= *name) && (name[i] != ':'); i++);
#ifdef	DEBUG
kprintf("u i %ld '%lc'\t", i, name[i]);
#endif
			for (i++; i <= *name; i++) {
			    if ((name[i] >= '0') && (name[i] <= '9')) {
				unit *= 10;
				unit += name[i] - '0';
			    }
			    else {
#ifdef	DEBUG
kprintf("\\ i %ld '%lc'\t", i, name[i]);
#endif
				unit = -1;
				break;
			    }
			}
#ifdef	DEBUG
kprintf("unit %ld\n", unit);
#endif
			if (unit >= 0) {
			    /* open the clipboard device */
			    result = OpenDevice("clipboard.device", unit,
				    &ca->ca_IOR, 0);
			    if (result != 0) {
				FreeMem(ca, sizeof(struct ClipArg));
				fh->fh_Arg1 = NULL;
				packet->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
			    }
			}
			else {
			    FreeMem(ca, sizeof(struct ClipArg));
			    fh->fh_Arg1 = NULL;
			    packet->dp_Res2 = ERROR_BAD_STREAM_NAME;
			}
		    }
		    else {
			packet->dp_Res2 = ERROR_NO_FREE_STORE;
		    }
		    ca->ca_Direction = 0L;	/* neither read nor write */
		    if (fh->fh_Arg1 == NULL) {
			returnPacket(myProcess, packet, DOSFALSE);
		    }
		    else {
			returnPacket(myProcess, packet, DOSTRUE);
			openCnt++;
		    }
		    break;

		case ACTION_END:
		    /*
		     * Close
		     */
#ifdef	DEBUG
    kprintf("Close\t");
#endif
		    ca = (struct ClipArg *) packet->dp_Arg1;
		    if (ca) {
			/* terminate the reading or writing */
			if (ca->ca_Direction != 0) {
			    ca->ca_Packet = packet;
			    ca->ca_ReplyPacket = closeClip;
			    if (ca->ca_Direction == ACTION_READ) {
				/* read past EOF to signal reading is done */
				ca->ca_IOR.io_Command = CMD_READ;
				ca->ca_IOR.io_Length = 1;
				ca->ca_IOR.io_Data = NULL;
				ca->ca_IOR.io_Offset = 0xfffffffe;
			    }
			    else {
				/* update to signal writing is done */
				ca->ca_IOR.io_Command = CMD_UPDATE;
			    }
			    SendIO(&ca->ca_IOR);
			    break;
			}
			else {
			    /* close the clipboard device */
			    CloseDevice(&ca->ca_IOR);
			    /* free the clipboard IO request */
			    FreeMem(ca, sizeof(struct ClipArg));
			    openCnt--;
			}
		    }
		    returnPacket(myProcess, packet, DOSTRUE);
		    break;

		case ACTION_READ:
		    /*
		     * Read
		     */
#ifdef DEBUG
    kprintf("Read: %ld\t", packet->dp_Arg3);
#endif
		    ca = (struct ClipArg *) packet->dp_Arg1;
		    if (ca->ca_Direction != ACTION_WRITE) {
			ca->ca_Direction = ACTION_READ;
			ca->ca_Packet = packet;
			ca->ca_ReplyPacket = replyClip;
			/* issue clip read */
			ca->ca_IOR.io_Command = CMD_READ;
			ca->ca_IOR.io_Length = packet->dp_Arg3;
			ca->ca_IOR.io_Data = (STRPTR) packet->dp_Arg2;
			SendIO(&ca->ca_IOR);
		    }
		    else {
			/* cannot read in the middle of a clip write */
			packet->dp_Res2 = ERROR_READ_PROTECTED;
			returnPacket(myProcess, packet, -1);
		    }
		    break;

		case ACTION_WRITE:
#ifdef DEBUG
    kprintf("Write: %ld\t", packet->dp_Arg3);
#endif
		    ca = (struct ClipArg *) packet->dp_Arg1;
		    if (ca->ca_Direction != ACTION_READ) {
			ca->ca_Direction = ACTION_WRITE;
			ca->ca_Packet = packet;
			ca->ca_ReplyPacket = replyClip;
			/* issue clip write */
			ca->ca_IOR.io_Command = CMD_WRITE;
			ca->ca_IOR.io_Length = packet->dp_Arg3;
			ca->ca_IOR.io_Data = (STRPTR) packet->dp_Arg2;
			SendIO(&ca->ca_IOR);
		    }
		    else {
			/* cannot write in the middle of a clip read */
			packet->dp_Res2 = ERROR_WRITE_PROTECTED;
			returnPacket(myProcess, packet, -1);
		    }
		    break;

		case ACTION_SEEK:
		    /*
		     * Seek
		     */
#ifdef DEBUG
    kprintf("Seek: %ld %ld\t", packet->dp_Arg2, packet->dp_Arg3);
#endif
		    ca = (struct ClipArg *) packet->dp_Arg1;
		    result = ca->ca_IOR.io_Offset;	/* cache old position */
		    if (packet->dp_Arg3 == OFFSET_BEGINNING) {
			ca->ca_IOR.io_Offset = packet->dp_Arg2;
			returnPacket(myProcess, packet, result);
		    }
		    else if (packet->dp_Arg3 == OFFSET_CURRENT) {
			ca->ca_IOR.io_Length += packet->dp_Arg2;
			returnPacket(myProcess, packet, result);
		    }
		    else {
			/* can only determine end by reading IFF length */
			if (ca->ca_Direction != ACTION_WRITE) {
			    ca->ca_Argument = packet->dp_Arg2;
			    ca->ca_Offset = result;
			    ca->ca_Direction = ACTION_READ;
			    ca->ca_Packet = packet;
			    ca->ca_ReplyPacket = seekClip;
			    /* issue clip read */
			    ca->ca_IOR.io_Command = CMD_READ;
			    ca->ca_IOR.io_Length = 4;
			    ca->ca_IOR.io_Data = (STRPTR) &ca->ca_Data;
			    ca->ca_IOR.io_Offset = 4;
			    SendIO(&ca->ca_IOR);
			}
			else {
			    /* cannot seek from end of file */
			    packet->dp_Res2 = ERROR_SEEK_ERROR;
			    returnPacket(myProcess, packet, -1);
			}
		    }
		    break;

		default:
		    /*
		     * Unsupported packet type
		     */
#ifdef DEBUG
    kprintf("Unknown packet type %ld\t", packet->dp_Type);
#endif
		    packet->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
		    returnPacket(myProcess, packet, DOSFALSE);
	    }				/* switch(packet->dp_Type) */
	    }
	}
	/*
	 * Clipboard IO Reply: reply the packet
	 */
	if (signal & sigClipReply) {
	    while (ca = (struct ClipArg *) GetMsg(&clipReplyPort)) {
		/*
		 * ReplyPacket: process this ClipArg by calling its
		 *   reply routine
		 */
		(*ca->ca_ReplyPacket)(myProcess, ca, &openCnt);
	    }
	}
    }					/* while() */

#ifdef	DEBUG
kprintf("Kill CLIP:\n");
#endif

    if (openIOR.io_Device != 0) CloseDevice(&openIOR);

    myDevNode->dn_Task = NULL;	/* zero the taskid field of device node */

    /*
     * Return: let whoever it was that spawned us provide the cleanup code
     */
}					/* _main() */
