/* This is just plain clipboard handling */
#include <setjmp.h>

#define ID(a,b,c,d) ((a << 24L) | (b << 16L) | (c << 8L) | (d))

#define ID_CAT  ID('C','A','T',' ')
#define ID_FORM ID('F','O','R','M')
#define ID_FTXT ID('F','T','X','T')
#define ID_CHRS ID('C','H','R','S')

#define SAFE(cond, jmp_buf)  { if (!(cond)) longjmp(jmp_buf,-1); }

IMPORT struct IOClipReq *ClipReq;

VOID CBFInit(struct CBFHandle *CBFH)
{
    if (CBFH->Type == CBFCLIP) {
        CBFH->Handle.ClipReq->io_Error = 0;
        CBFH->Handle.ClipReq->io_Offset = 0L;
        CBFH->Handle.ClipReq->io_ClipID = 0L;
    }
}

VOID CBFEndWrite(struct CBFHandle *CBFH)
{
    if (CBFH->Type == CBFCLIP) {
        CBFH->Handle.ClipReq->io_Command = CMD_UPDATE;
        DoIO((struct IORequest *)CBFH->Handle.ClipReq);
    }
}

VOID CBFEndRead(struct CBFHandle *CBFH)
{
    if (CBFH->Type == CBFCLIP) {
        CBFH->Handle.ClipReq->io_Command = CMD_READ;
        CBFH->Handle.ClipReq->io_Offset = 2000000000;
        CBFH->Handle.ClipReq->io_Length = 1;
        CBFH->Handle.ClipReq->io_Data = (STRPTR) 0;
        DoIO((struct IORequest *)CBFH->Handle.ClipReq);
    }
}

ULONG CBFSeek(struct CBFHandle *CBFH, LONG Offset, LONG Origin)
{
    if (CBFH->Type == CBFCLIP) {
        switch (Origin) {
            case OFFSET_CURRENT: {
                CBFH->Handle.ClipReq->io_Offset += Offset;
                return CBFH->Handle.ClipReq->io_Offset;
            }
            case OFFSET_BEGINNING: {
                CBFH->Handle.ClipReq->io_Offset = Offset;
                return CBFH->Handle.ClipReq->io_Offset;
            }
            default: {
                return 2000000000;
            }
        }
    } else {
        return (ULONG)Seek(CBFH->Handle.File, Offset, Origin);
    }
    return 0;
}

LONG CBFWrite(struct CBFHandle *CBFH, STRPTR Buf, ULONG BufSize)
{
    if (CBFH->Type == CBFCLIP) {
        CBFH->Handle.ClipReq->io_Command = CMD_WRITE;
        CBFH->Handle.ClipReq->io_Data = Buf;
        CBFH->Handle.ClipReq->io_Length = BufSize;
        DoIO((struct IORequest *)CBFH->Handle.ClipReq);
        return (CBFH->Handle.ClipReq->io_Error ? -1 : 0);
    } else {
        return Write(CBFH->Handle.File, (char *)Buf, BufSize);
    }
}

LONG CBFRead(struct CBFHandle *CBFH, STRPTR Buf, ULONG BufSize)
{
    if (CBFH->Type == CBFCLIP) {
        CBFH->Handle.ClipReq->io_Command = CMD_READ;
        CBFH->Handle.ClipReq->io_Data = Buf;
        CBFH->Handle.ClipReq->io_Length = BufSize;
        DoIO((struct IORequest *)CBFH->Handle.ClipReq);
        return (CBFH->Handle.ClipReq->io_Error ? -1 : 0);
    } else {
        return Read(CBFH->Handle.File, (char *)Buf, BufSize);
    }
}

jmp_buf failure;

VOID SaveClip(UBYTE *SnapSpace, ULONG SnapSize)
{
    ULONG Len;
    struct CBFHandle CBFH;

    CBFH.Type = CBFCLIP;
    CBFH.Handle.ClipReq = ClipReq;

    CBFInit(&CBFH);
    CBFWrite(&CBFH, (STRPTR)"FORM", 4L);
    Len = (SnapSize + 13) & ~1;
    CBFWrite(&CBFH, (STRPTR)&Len, 4L);
    CBFWrite(&CBFH, (STRPTR)"FTXT", 4L);
    CBFWrite(&CBFH, (STRPTR)"CHRS", 4L);
    Len = SnapSize;
    CBFWrite(&CBFH, (STRPTR)&Len, 4L);
    CBFWrite(&CBFH, SnapSpace, Len);
    if (SnapSize & 1) {
       CBFWrite(&CBFH, (STRPTR)"\0", 1L);
    }
    CBFEndWrite(&CBFH);
}

struct Snap *FetchClip()
{
    struct Snap *Snap = NULL;
    ULONG ID[3];
    struct CBFHandle CBFH;
    WORD done = 0;

    CBFH.Type = CBFCLIP;
    CBFH.Handle.ClipReq = ClipReq;

    if (setjmp(failure)) {
        if (Snap) {
            FreeMem(Snap, Snap->Size);
        }
        CBFEndRead(&CBFH);
        return NULL;
    }

    CBFInit(&CBFH);
    SAFE(CBFRead(&CBFH, (STRPTR)&ID[0], 12) != -1L, failure);
    if (ID[0] != ID_CAT) {
        CBFSeek(&CBFH, -12, OFFSET_CURRENT);
    }
    while (!done) {
        SAFE(CBFRead(&CBFH, (STRPTR)&ID[0], 12) != -1L, failure);
        switch (ID[2]) {
            case ID_FTXT: {
                SAFE(CBFRead(&CBFH, (STRPTR)&ID[0], 8) != -1L, failure);
                SAFE(ID[0] == ID_CHRS, failure);
                Snap = AllocMem(sizeof(struct Snap) + ID[1],
                                MEMF_PUBLIC | MEMF_CLEAR);
                SAFE(Snap, failure);
                Snap->Size = sizeof(struct Snap) + ID[1];
                SAFE(CBFRead(&CBFH, (STRPTR)&Snap->Chars[0], Snap->Size) != -1L,
                     failure);
                done = 1;
                break;
            }
            default: {
                SAFE(CBFSeek(&CBFH, ID[1] + (ID[1] & 1) - 4, OFFSET_CURRENT),
                     failure);
                break;
            }
        }
    }
    CBFEndRead(&CBFH);
    return(Snap);
}
