RCS_ID_C "$Id: clip.c,v 1.6 1993/07/12 22:51:10 ppessi Exp $";
/* 
 * Clipboard routines for the Amiga version of niftyterm.
 * This code borrows heavily from the Clipboard code given
 * in the AmigaMail tech notes, page XIV - 9.
 * 
 * Original code by Todd Williamson.
 *
 * $Author: ppessi $ $Revision: 1.6 $ $Date: 1993/07/12 22:51:10 $
 */

#include "nifty.h"
#include "amiga.h"
#include "exec/io.h"

#include <devices/clipboard.h>

static char clipopen = FALSE;
static struct MsgPort *cport = NULL;
static struct IOClipReq *creq = NULL;

/* Saves lots of assignment statements */
#define SETIO(iob, com, buf, len) \
    (iob)->io_Command = (com); \ 
    (iob)->io_Data = (STRPTR)(buf); (iob)->io_Length = (len)

/* Used internally by the other clipboard routines.  Clipboard device is 
 * automatically opened on the first read or write.  Must be closed
 * explicitly.
 */
static int
ClipOpen()
{
    clipopen = clipopen ||
	((cport = CreateMsgPort()) &&
	 (creq = (struct IOClipReq *)CreateIORequest(cport, sizeof(*creq))) &&
	 !OpenDevice("clipboard.device", 0, (struct IORequest *)creq, 0));
    /* Free resources if necessary */
    if (!clipopen) {
	ClipClose();
	puts("Could not open clipboard");
	return -1;
    }

    return 0;
}

/* This will close the clipboard device if it is open.  Although the
 * device is automatically opened on the first read or write, it must
 * be closed explicitly.
 */
void
ClipClose()
{
    if (clipopen) 
        CloseDevice((struct IORequest *)creq);
    clipopen = FALSE;

    if (creq)
	DeleteIORequest((struct IORequest *)creq);
    creq = NULL;

    if (cport)
	DeleteMsgPort(cport);
    cport = NULL;
}

static void
ClipLong(lword)
long *lword;
{
    SETIO(creq, CMD_WRITE, lword, 4);
    DoIO((struct IORequest *)creq);
}

/* Implement cutting of a string.  Cuts the string to the clipboard.
 */
int
ClipCut(string)
char *string;
{
    long length, slen;
    int odd;

    if (ClipOpen() < 0) 
	return -1;

    slen = strlen(string);
    odd = (slen & 1);
    length = slen + odd + 3 * sizeof(LONG);

    creq->io_ClipID = 0;
    creq->io_Offset = 0;
    ClipLong((LONG *)"FORM");
    ClipLong(&length);
    ClipLong((LONG *)"FTXT");
    ClipLong((LONG *)"CHRS");
    ClipLong(&slen);

    /* We make use of the last NUL at the end of the string */ 
    SETIO(creq, CMD_WRITE, string, slen + odd);
    DoIO((struct IORequest *)creq);
    
    creq->io_Command = CMD_UPDATE;
    DoIO((struct IORequest *)creq);
    return 0;
}
    
/* Pass a buffer and a max size, and it gets filled with the current contents
 * of the clipboard (if the contents is textual). Returns -1 on error, length
 * if successful.  The buffer must be at least 8 characters.
 */
long
ClipPaste(buf, max)
char *buf;
long max;
{
    long length = 0, slen = 0, status = 0;
    
    if (ClipOpen() < 0) 
	return 0;

    max--;
    SETIO(creq, CMD_READ, buf, 4);
    creq->io_Offset = 0;
    creq->io_ClipID = 0;
    status -= DoIO((struct IORequest *)creq);

    if(!strncmp(buf, "FORM", 4)) {
        SETIO(creq, CMD_READ, &length, 4);
	status -= DoIO((struct IORequest *)creq);
	SETIO(creq, CMD_READ, buf, 8);
	status -= DoIO((struct IORequest *)creq);
	
	if(!strncmp(buf, "FTXTCHRS", 8)) {
	    SETIO(creq, CMD_READ, &slen, 4);
	    status -= DoIO((struct IORequest *)creq);
	    
	    slen = MIN(slen, max);
	    SETIO(creq, CMD_READ, buf, slen);
	    status -= DoIO((struct IORequest *)creq);
	    
	    /* Force EOF on read */
	    SETIO(creq, CMD_READ, 0, 1);
	    creq->io_Offset += length;
	    do {
	      status -= DoIO((struct IORequest *)creq);
	    } while (creq->io_Actual);
	}
    }
    if(status) return 0;
    buf[slen] = '\0';
    return slen;
}
