/*************************************************************************
  *                                                                      *
  *                            Preliminary                               *
  *                        Amiga AppShell (tm)                           *
  *                                                                      *
  *  Copyright (c) 1990,1991 Commodore-Amiga, Inc. All Rights Reserved.  *
  *                                                                      *
  *   This software and information is proprietary, preliminary, and     *
  *   subject to change without notice.                                  *
  *                                                                      *
  *                            DISCLAIMER                                *
  *                                                                      *
  *   THIS SOFTWARE IS PROVIDED "AS IS".                                 *
  *   NO REPRESENTATIONS OR WARRANTIES ARE MADE WITH RESPECT TO THE      *
  *   ACCURACY, RELIABILITY, PERFORMANCE, CURRENTNESS, OR OPERATION      *
  *   OF THIS SOFTWARE, AND ALL USE IS AT YOUR OWN RISK.                 *
  *   NEITHER COMMODORE NOR THE AUTHORS ASSUME ANY RESPONSIBILITY OR     *
  *   LIABILITY WHATSOEVER WITH RESPECT TO YOUR USE OF THIS SOFTWARE.    *
  *                                                                      *
  *                          Non-Disclosure                              *
  *                                                                      *
  *   This information is not to be disclosed to any other company,      *
  *   individual or party.  Discussion is to be restricted to CBM        *
  *   approved discussion areas, such as the closed conferences on bix;  *
  *   amiga.cert, amiga.com, amiga.beta/appshell.                        *
  *                                                                      *
  ************************************************************************
  */

/* clipiff.c
 * Simple IFF FTXT clipboard routines, requires iffparse.library version 36.
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * written by David N. Junod, 26-Aug-90
 *
 * Compiled with Lattice 'C' version 5.05.
 *
 *    LC -cqfist -ms -v clipiff
 */

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <devices/clipboard.h>
#include <libraries/iffparse.h>
#include <utility/hooks.h>
#include <clib/exec_protos.h>
#include <clib/iffparse_protos.h>
#include <pragmas/exec.h>
#include <pragmas/iffparse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clipiff.h"

extern struct Library *SysBase, *UtilityBase;

#define CLIP_ERROR	FALSE

struct ClipManager *__asm AllocClip (VOID)
{
    struct ClipManager *cm;
    ULONG hookEntry();

    /* Attempt to allocate the ClipManager structure */
    if (cm = (struct ClipManager *)
	AllocMem (sizeof (struct ClipManager), MEMF_CLEAR))
    {
	/* Are we using 2.0 or greater? */
	if (SysBase->lib_Version >= 36)
	{
	    /* Initialize the hook */
	    cm->cm_Hook.h_Entry = hookEntry;
	    cm->cm_Hook.h_Data = (VOID *) 0xFACE;
	}

	/* Indicate that there isn't a hook installed */
	cm->cm_HookUnit = (-1L);
    }

    /* Return the initialized ClipManager */
    return (cm);
}

VOID __asm FreeClip (register __a1 struct ClipManager * cm)
{

    /* Make sure we at least have a pointer */
    if (cm)
    {
	WORD i;

	/* See if there is a hook installed */
	if (cm->cm_HookUnit >= 0L)
	{
	    /* Stop the hook */
	    StopClipChangeHook (cm);
	}

	/* Shutdown each open clipboard unit */
	for (i = 0; i < MAX_UNITS; i++)
	    CloseClip (cm->cm_Unit[i]);

	/* Free each open clipboard buffer */
	for (i = 0; i < MAX_UNITS; i++)
	    FreeBuffer (cm->cm_Buff[i]);

	/* Free the clipboard manager */
	FreeMem (cm, sizeof (struct ClipManager));

    }				/* End of if ClipManager */
}

struct ClipBuff * __asm ReadClip (
	register __a1 struct ClipManager * cm,
	register __d0 LONG iff_type,
	register __d1 LONG id)
{
    struct ClipBuff *buffer = NULL;
    struct IFFHandle *iff = NULL;

    /* make sure we have a pointer at least */
    if (cm)
    {
	/* Make sure we have a valid clipboard unit */
	if ((id >= 0L) && (id < MAX_UNITS))
	{
	    /* Open the requested clipboard unit if it isn't already */
	    if (!(cm->cm_Unit[id]))
	    {
		cm->cm_Unit[id] = OpenClip (id);
	    }

	    /* Free the buffer */
	    FreeBuffer (cm->cm_Buff[id]);
	    cm->cm_Buff[id] = NULL;

	    /* Make sure we have a clipboard unit to work with */
	    if (iff = cm->cm_Unit[id])
	    {
		switch (iff_type)
		{
		    case 0L:	/* Default to FTXT */
		    case ID_FTXT:
			buffer = ReadFTXT (iff);
			break;

		    default:
			break;
		}

		/* assign the buffer */
		cm->cm_Buff[id] = buffer;

	    }			/* End of if unit available */
	}			/* End of if >= 0 and < MAX_UNITS */
    }				/* End of if ClipManager pointer */

    return (buffer);
}

LONG __asm QueryClip (
	register __a1 struct ClipManager * cm,
	register __d0 LONG unit,
	register __a2 ULONG *ctype,
	register __a3 ULONG *clipid)
{
    LONG retval = FALSE;

    if (cm)
    {
	struct IFFHandle *iff;

	/* Open the requested clipboard unit if it isn't already */
	if (!(cm->cm_Unit[unit]))
	{
	    cm->cm_Unit[unit] = OpenClip (unit);
	}

	/* Make sure we have a clipboard unit to work with */
	if (iff = cm->cm_Unit[unit])
	{
	    struct ClipboardHandle *cbh = (struct ClipboardHandle *) iff->iff_Stream;
	    struct IOClipReq *clipIO = &(cbh->cbh_Req);
	    LONG cbuff[5] = {NULL};

	    /* Read clipboard header */
	    clipIO->io_Command = CMD_READ;
	    clipIO->io_Error = 0;
	    clipIO->io_ClipID = 0;
	    clipIO->io_Offset = 0;
	    clipIO->io_Data = (char *) cbuff;
	    clipIO->io_Length = 20;
	    DoIO (clipIO);

	    /* Fill in the clip ID */
	    *clipid = (ULONG) clipIO->io_ClipID;

	    /* Clear the type field */
	    *ctype = 0L;

	    /* See if the read was successful */
	    if ((clipIO->io_Actual >= 8L) && (cbuff[2] != ID_SPACE))
	    {
		/* Fill in the type field */
		*ctype = (ULONG) cbuff[2];
	    }

	    /* indicate that we're done reading */
	    clipIO->io_Offset = 0x7FFFFFF0;
	    clipIO->io_Length = 1;
	    clipIO->io_Data = NULL;
	    DoIO (clipIO);

	    /* Indicate success */
	    retval = TRUE;
	}
    }

    return (retval);
}

/* This hook must do the least amount of work as possible. It is done on the
 * clipboard.device task frame */
ULONG clipHook (struct Hook *h, VOID *o, struct ClipHookMsg *msg)
{
    if (h->h_Data)
    {
	Signal ((struct Task *)h->h_Data, SIGBREAKF_CTRL_E);
    }

    return (0L);
}

LONG __asm StartClipChangeHook (
	register __a1 struct ClipManager * cm,
	register __d0 LONG unit)
{
    LONG retval = FALSE;

    if (cm)
    {
	struct IFFHandle *iff;

	/* Open the requested clipboard unit if it isn't already */
	if (!(cm->cm_Unit[unit]))
	{
	    cm->cm_Unit[unit] = OpenClip (unit);
	}

	/* Make sure we don't have any outstanding hooks */
	StopClipChangeHook (cm);

	/* Make sure we have a clipboard unit to work with */
	if (iff = cm->cm_Unit[unit])
	{
	    struct ClipboardHandle *cbh = (struct ClipboardHandle *) iff->iff_Stream;
	    struct IOClipReq *clipIO = &(cbh->cbh_Req);
	    extern ULONG hookEntry();

	    /* Fill out the IO request */
	    clipIO->io_Command = CBD_CHANGEHOOK;
	    clipIO->io_Data = (char *) &(cm->cm_Hook);
	    clipIO->io_Length = 1;

	    /* Prepare the hook */
	    cm->cm_Hook.h_Entry = hookEntry;
	    cm->cm_Hook.h_SubEntry = clipHook;
	    cm->cm_Hook.h_Data = FindTask (NULL);

	    /* Start the hook */
	    if (DoIO (clipIO))
	    {
		/* printf ("unable to set hook\n"); */
	    }
	    else
	    {
		/* Indicate success */
		cm->cm_HookUnit = unit;

		retval = TRUE;
	    }
	}
    }

    return (retval);
}

LONG __asm StopClipChangeHook (register __a1 struct ClipManager * cm)
{
    LONG retval = FALSE;

    if (cm && (cm->cm_HookUnit >= 0L))
    {
	LONG unit = cm->cm_HookUnit;
	struct IFFHandle *iff;

	/* Open the requested clipboard unit if it isn't already */
	if (!(cm->cm_Unit[unit]))
	{
	    cm->cm_Unit[unit] = OpenClip (unit);
	}

	/* Make sure we have a clipboard unit to work with */
	if (iff = cm->cm_Unit[unit])
	{
	    struct ClipboardHandle *cbh = (struct ClipboardHandle *) iff->iff_Stream;
	    struct IOClipReq *clipIO = &(cbh->cbh_Req);

	    /* Fill out the IO request */
	    clipIO->io_Command = CBD_CHANGEHOOK;
	    clipIO->io_Data = (char *) &(cm->cm_Hook);
	    clipIO->io_Length = 0;

	    /* Start the hook */
	    if (DoIO (clipIO))
	    {
		/* printf ("unable to stop hook\n"); */
	    }
	    else
	    {
		/* Indicate success */
		retval = TRUE;
	    }
	}

	/* Indicate that the hook has been halted */
	cm->cm_HookUnit = (-1L);
    }

    return (retval);
}

LONG __asm WriteClip (
	register __a1 struct ClipManager * cm,
	register __a2 struct ClipBuff * buff,
	register __d0 LONG id)
{
    LONG retval = 0L;

    /* make sure we have a pointer at least */
    if (cm && buff)
    {
	struct IFFHandle *iff = NULL;

	/* Make sure we have a valid clipboard unit */
	if ((id >= 0L) && (id < MAX_UNITS))
	{
	    /* Open the requested clipboard unit if it isn't already */
	    if (!(cm->cm_Unit[id]))
		cm->cm_Unit[id] = OpenClip (id);

	    /* Make sure we have a clipboard unit to work with */
	    if (iff = cm->cm_Unit[id])
	    {
		switch (buff->b_Type)
		{
		    case 0L:	/* Default to FTXT */
		    case ID_FTXT:
			retval = WriteFTXT (iff, buff->b_Buff);
			break;

		    default:
			break;
		}
	    }			/* End of if unit available */
	}			/* End of if >= 0 and < MAX_UNITS */

	/* Assign the clip ID assigned to this write */
	buff->b_ClipID = retval;
    }				/* End of if ClipManager pointer */

    return (retval);
}

/****** clip/OpenClip *********************************************************
*
*   NAME
*	OpenClip - Obtain an IFF handle on the desired clipboard unit.
*
*   SYNOPSIS
*	handle = OpenClip ( clip_id );
*	d0		    d0
*
*	struct IFFHandle *handle;
*	LONG clip_id;
*
*   FUNCTION
*	Obtain a handle on a particular clipboard unit for reading and
*	writing of data.
*
*   INPUTS
*	clip_id	- A number from 0 to 255, used to indicate the clipboard
*		  unit to use.  For normal useage, PRIMARY_CLIP should
*		  be used.
*
*   RETURNS
*	handle	- Returns a valid IFFHandle to use for further access
*		  or NULL if unable to access the requested unit.
*
*   SEE ALSO
*	CloseClip()
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

struct IFFHandle *__asm OpenClip (register __d0 LONG id)
{
    struct IFFHandle *iff = NULL;

    /* Allocate an IFF handle */
    if (iff = AllocIFF ())
    {
	/* Open the clipboard for access */
	if (iff->iff_Stream = (ULONG) OpenClipboard (id))
	{
	    /* Initialize the handle as clipboard access */
	    InitIFFasClip (iff);
	}
	else
	{
	    /* Free the IFF handle */
	    FreeIFF (iff);
	    iff = NULL;
	}
    }

    return (iff);
}

/****** clip/CloseClip ********************************************************
*
*   NAME
*	CloseClip - Close an IFF handle obtained by OpenClip().
*
*   SYNOPSIS
*	CloseClip ( handle );
*		    a0
*
*	struct IFFHandle *handle;
*
*   FUNCTION
*	Used to close a IFF handle opened by OpenClip() only.
*
*   INPUTS
*	handle	- A valid IFFHandle obtained by calling OpenClip().
*
*   SEE ALSO
*	OpenClip()
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

VOID __asm CloseClip (register __a1 struct IFFHandle * iff)
{

    /* Make sure that the handle points to something */
    if (iff)
    {
	/* Close the clipboard */
	if (iff->iff_Stream)
	    CloseClipboard ((struct ClipboardHandle *) iff->iff_Stream);

	/* Free the IFF handle */
	FreeIFF (iff);
    }
}

/****** clip/FreeBuffer *******************************************************
*
*   NAME
*	FreeBuffer - Used to free a buffer returned by ReadClip().
*
*   SYNOPSIS
*	FreeBuffer ( buffer );
*		     a0
*
*	struct ClipBuff *buffer;
*
*   FUNCTION
*	This function is used to free the memory allocated by a call to
*	ReadClip().
*
*   INPUTS
*	buffer	- A valid buffer pointer returned by a call to ReadClip().
*
*   SEE ALSO
*	ReadFTXT()
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

VOID __asm FreeBuffer (register __a1 struct ClipBuff * buff)
{

    /* Make sure that buffer is a pointer, not an error */
    if ((LONG) buff > 0L)
    {
	/* Free the entire buffer */
	FreeMem ((APTR) buff, buff->b_Size);
    }
}

/****** clip/ReadFTXT *********************************************************
*
*   NAME
*	ReadFTXT - Read the IFF handle for FTXT.
*
*   SYNOPSIS
*	buffer = ReadFTXT ( handle );
*	d0		    a0
*
*	struct ClipBuff *buffer;
*	struct IFFHandle *handle;
*
*   FUNCTION
*	Read the passed IFF handle for the first occurrance of FTXT CHRS
*	and returns the characters in a buffer.
*
*   INPUTS
*	handle	- A valid IFF handle, returned by OpenClip().
*
*   RETURNS
*	buffer	- If greater than zero, then it is a pointer to a ClipBuff
*		  structure that contains the contents of the FTXT CHRS
*		  chunk first encountered.
*
*		  If buffer is less than zero, then an error occurred and
*		  the following macros are used to indicate the error.
*
*		  RC_EMPTY_CLIP		\* clipboard is empty *\
*		  RC_NOT_ENOUGH_MEMORY	\* not enough memory *\
*                 RC_COULDNT_READ	\* couldn't read a chunk *\
*                 RC_BAD_IFF		\* clipboard contains a bad IFF *\
*                 RC_INVALID_TYPE	\* don't understand FORM type *\
*
*   SEE ALSO
*	FreeBuffer()
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

LONG ftxtprops[] = {ID_FTXT, ID_AUTH, ID_FTXT, ID_NAME};

struct ClipBuff *__asm
ReadFTXT (register __a1 struct IFFHandle * iff)
{
    struct ClipBuff *b = NULL;
    LONG error = 0L;

    /* Make sure that the handle points to something */
    if (iff)
    {
	struct ClipboardHandle *cbh = (struct ClipboardHandle *) iff->iff_Stream;
	struct IOClipReq *clipIO = &(cbh->cbh_Req);

	if (((struct Library *) IFFParseBase)->lib_Version < 36)
	{
	    /* Bug workaround: clear io_Error field */
	    clipIO->io_Error = 0;

	    /* Bug workaround: Turn off the write flag */
	    iff->iff_Flags &= ~IFFF_WRITE;
	}

	/* Open the IFF handle for reading */
	if (!(error = OpenIFF (iff, IFFF_READ)))
	{
	    /* Declare the properties to search for */
	    PropChunks (iff, ftxtprops, 2);

	    /* Register the stop chunk */
	    if ((error = StopChunk (iff, ID_FTXT, ID_CHRS)) == 0L)
	    {
		/* Parse the file, stopping at a character chunk */
		error = ParseIFF (iff, IFFPARSE_SCAN);
	    }

	    /* Check the return value */
	    if (error == IFFERR_EOF || error == IFFERR_NOTIFF)
	    {
		b = (struct ClipBuff *) RC_EMPTY_CLIP;
	    }
	    else if (error != 0L)
	    {
		b = (struct ClipBuff *) RC_BAD_IFF;
	    }
	    else
	    {
		struct ContextNode *cn;
		LONG msize;

		/* Get information on the current chunk */
		if ((cn = CurrentChunk (iff)) && (cn->cn_Size > 0L))
		{
		    /* Calculate the size of our buffer */
		    msize = sizeof (struct ClipBuff) + cn->cn_Size + 1;

		    /* Allocate a buffer to place our text in */
		    if (b = (struct ClipBuff *) AllocMem (msize, MEMF_CLEAR))
		    {
			/* Point the contents buffer at the right place */
			b->b_Buff = (VOID *) ((b) + 1);

			/* Read in the characters */
			if ((error = ReadChunkBytes (iff, b->b_Buff, cn->cn_Size))
			    == cn->cn_Size)
			{
			    /* Remember the buffer size */
			    b->b_Size = msize;

			    /* Remember the type of buffer */
			    b->b_Type = ID_FTXT;

			    /* Get the current clip ID */
			    b->b_ClipID = clipIO->io_ClipID;

#if 0
			    struct StoredProperty *sp;

			    /* Get the author information */
			    if (sp = FindProp (iff, ID_FTXT, ID_AUTH))
			    {
				msize = sp->sp_Size + 1L;

				if (b->b_Author = (STRPTR) AllocMem (msize, MEMF_CLEAR))
				{
				    b->b_ASize = msize;
				    strcpy (b->b_Author, sp->sp_Data);
				}
			    }

			    /* Get the project information */
			    if (sp = FindProp (iff, ID_FTXT, ID_NAME))
			    {
				msize = sp->sp_Size + 1L;

				if (b->b_Project = (STRPTR) AllocMem (msize, MEMF_CLEAR))
				{
				    b->b_PSize = msize;
				    strcpy (b->b_Project, sp->sp_Data);
				}
			    }
#endif
			}
			else
			{
			    /* Free the buffer */
			    FreeMem ((APTR) b, msize);
			    b = (struct ClipBuff *) RC_COULDNT_READ;
			}
		    }		/* End of if allocate buffer */
		    else
		    {
			b = (struct ClipBuff *) RC_NOT_ENOUGH_MEMORY;
		    }

		}		/* End of get current chunk info */
		else
		{
		    b = (struct ClipBuff *) RC_COULDNT_READ;
		}

	    }			/* End of handle parse */

	    /* Say that we're done reading */
	    CloseIFF (iff);
	}
	else
	{
	    b = (struct ClipBuff *) error;
	}

    }				/* End of if there is a handle */

    return (b);
}

/****** clip/WriteChunk *******************************************************
*
*   NAME
*	WriteChunk - Used to write a text chunk.
*
*   SYNOPSIS
*	error = WriteChunk ( handle, chunk, text );
*	d0		     a0      d0     a1
*
*	LONG error;
*	struct IFFHandle *handle;
*	LONG chunk;
*	STRPTR text;
*
*   FUNCTION
*	This function is used to write out a text chunk, such as ANNO, AUTH
*	or CHRS.
*
*   INPUTS
*	handle	- A valid IFF handle, returned by OpenClip()
*
*	chunk	- A chunk type that supports text, such as ID_ANNO, ID_AUTH,
*		  ID_CHRS, ID_(c) or ID_NAME.
*
*	text	- A pointer to a NULL terminated buffer that contains the
*		  text to write.
*
*   RETURNS
*	error	- A value of zero indicates that no errors occurred. A non-
*		  zero value indicates an error.  Values are defined in
*		  <libraries/iffparse.h>.
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

LONG __asm WriteChunk (
				    register __a1 struct IFFHandle * iff,
				    register __d0 LONG id,
				    register __a2 STRPTR buffer)
{
    LONG error = 0L;
    LONG clen = (LONG) strlen (buffer) + 1;

    if (buffer && (clen > 0L))
    {
	if ((error = PushChunk (iff, 0, id, -1L)) == 0L)
	{
	    if ((error = WriteChunkBytes (iff, buffer, clen)) > 0L)
	    {
		error = PopChunk (iff);
	    }
	}
    }

    return (error);
}

/****** clip/WriteFTXT ********************************************************
*
*   NAME
*	WriteFTXT - Write an IFF form that contains text.
*
*   SYNOPSIS
*	error = WriteFTXT ( handle, text );
*	d0		    a0	    a1
*
*	LONG error;
*	struct IFFHandle *handle;
*	STRPTR text;
*
*   FUNCTION
*	This function is used to write an IFF form that contains text.
*
*   INPUTS
*	handle	- A valid IFF handle, returned by OpenClip()
*	text	- A pointer to a NULL terminated buffer that contains the
*		  text to write.
*
*    RETURNS
*	error	- A value of zero indicates that no errors occurred. A
*		  negative number indicates an error (values are defined
*		  in <libraries/iffparse.h>.  A positive number indicates
*		  the clip ID assigned to the clip.
*
*   SEE ALSO
*	ReadFTXT()
*
**********************************************************************
*
* Created:  26-Aug-90, David N. Junod
*
*/

LONG __asm
WriteFTXT (register __a1 struct IFFHandle * iff, register __a2 STRPTR buffer)
{
    LONG retval = 0L;

    if (iff)
    {
	struct ClipboardHandle *cbh = (struct ClipboardHandle *) iff->iff_Stream;
	struct IOClipReq *clipIO = &(cbh->cbh_Req);

	if (((struct Library *) IFFParseBase)->lib_Version < 36)
	{
	    /* Bug workaround: clear io_Error field */
	    clipIO->io_Error = 0;
	}

	/* Open the IFF handle for writing */
	if ((retval = OpenIFF (iff, IFFF_WRITE)) == 0L)
	{
	    /* Start writing the text ... */
	    if ((retval = PushChunk (iff, ID_FTXT, ID_FORM, -1L)) == 0)
	    {
		/* Write out the buffer as characters */
		WriteChunk (iff, ID_CHRS, buffer);

		/* Finish out the entire write */
		PopChunk (iff);

		/* Assign the clip ID */
		retval = clipIO->io_ClipID;
	    }

	    /* Say that we're done writing */
	    CloseIFF (iff);
	}
    }

    return (retval);
}
