/* Lib.c
 *
 * Basic Library Resource Handling
 *
 * This code is based on the example shared library,
 * found on the DICE 3.0 disks.
 *
 * This code doesn't use any form of startup code (the example does).
 * This is not something the novice C-programmer should attemt to do.
 * You need to know what you are doing. :)
 */

#include "async.h"
#include <exec/libraries.h>
#include <exec/resident.h>
#include "rev.h"

#include <clib/asyncio_protos.h>

LibCall struct Library *LibInit( _REG( d0 ) struct Library *, _REG( a0 ) APTR, _REG( a6 ) struct ExecBase * );
struct Library *LibOpen( _REG( a6 ) struct Library * );
APTR LibClose( _REG( a6 ) struct Library * );
APTR LibExpunge( _REG( a6 ) struct Library * );


/* Due to a bug in the linker, we must have some initialized data.
 * If not, the linker will not produce any smalldata section.
 * By giving one of the fields a value, this problems is avoided.
 *
 * The other variables will be zero, since "The memory used for
 * bss blocks is zeroed by the loader when it is allocated" (quote
 * from the AmigaDOS manual).
 */
struct Library		*AsyncIOBase = NULL;	/* Our library structure */
struct ExecBase		*SysBase;
struct Library		*UtilityBase;
struct DosLibrary	*DOSBase;
APTR			SegList;


/* In case the user tries to run us, simply exit immediately.
 * This code is also used for the reserved library function,
 * that all libraries currently must have.
 *
 * Note that this function *must* be placed before any const
 * data, or else the "exit if run" effect is lost.
 */
LONG
LibReserved( VOID )
{
	return( 0 );
}


/* The functions the library should have. Unfortunately, we can't
 * use the more compact format, using 16-bit relative entries,
 * due to the compiler (bug or limitation ;).
 */
const APTR FuncTable[] =
{
	LibOpen,	/* Standard library functions */
	LibClose,
	LibExpunge,
	LibReserved,

	OpenAsync,	/* Our functions start here */
	OpenAsyncFromFH,
	CloseAsync,
	SeekAsync,
	ReadAsync,
	WriteAsync,
	ReadCharAsync,
	WriteCharAsync,

	( APTR ) -1	/* Terminate the table */
};


/* Table describing the library. We need this, since we use the
 * autoinit feature. This means we don't need to call MakeLibrary()
 * etc. in LibInit.
 */
const ULONG InitTable[] =
{
	sizeof( struct Library ),	/* Size of our library base, excluding jump table. We have no extra data here */
	( ULONG ) FuncTable,		/* The functions we have */
	NULL,				/* InitStruct data. We init stuff ourselves instead */
	( ULONG ) LibInit		/* The library init function */
};


const TEXT LibId[] = "asyncio.library "VERSION" (" DATE ")\r\n";
const TEXT LibName[] = "asyncio.library";


/* And finaly the resident structure, used by InitResident(),
 * in order to initialize everything.
 */
const struct Resident RomTag =
{
	RTC_MATCHWORD,		/* rt_MatchWord */
	&RomTag,		/* rt_MatchTag */
	LibExpunge,		/* rt_EndSkip */
	RTF_AUTOINIT,		/* rt_Flags */
	VERNUM,			/* rt_Version */
	NT_LIBRARY,		/* rt_Type */
	0,			/* rt_Pri */
	LibName,		/* rt_Name */
	LibId,			/* rt_IDString */
	InitTable		/* rt_Init */
};


/* This small function is a frees the library structure,
 * and any other allocated resources.
 */
VOID
FreeLib( struct Library *lib )
{
	CloseLibrary( ( struct Library * ) DOSBase );
	CloseLibrary( UtilityBase );
	FreeMem( ( UBYTE * ) lib - lib->lib_NegSize, lib->lib_NegSize + lib->lib_PosSize );
}


/* This function is called when the library is loaded, and the library base
 * have been allocated. We are in a forbid section here, so don't do anything
 * time-consuming, Wait() or similar.
 *
 * If all ok, return the library base. If anything went wrong, deallocate
 * the library structure, and return NULL.
 */
LibCall struct Library *
LibInit( _REG( d0 ) struct Library *lib, _REG( a0 ) APTR seglist, _REG( a6 ) struct ExecBase *sysBase )
{
	SysBase = sysBase;

	if( ( DOSBase = ( struct DOSLibrary * ) OpenLibrary( "dos.library", 37 ) ) &&
		( UtilityBase = OpenLibrary( "utility.library", 37 ) ) )
	{
		lib->lib_Node.ln_Type	= NT_LIBRARY;
		lib->lib_Node.ln_Pri	= 0;
		lib->lib_Node.ln_Name	= LibName;
		lib->lib_Flags		= LIBF_CHANGED | LIBF_SUMUSED;
		lib->lib_Version	= VERNUM;
		lib->lib_Revision	= REVNUM;
		lib->lib_IdString	= ( APTR ) LibId;
		SegList = seglist;
		AsyncIOBase = lib;
	}
	else
	{
		FreeLib( lib );
		lib = NULL;
	}

	return( lib );
}


/* Open is given the library pointer. Either return the library pointer or NULL.
 * Remove the delayed-expunge flag. Exec has Forbid() for us during the call.
 *
 * Since we don't refer to any smalldata here (directly or indirectly),
 * we can safely skip "LibCall", to save a few bytes.
 */
struct Library *
LibOpen( _REG( a6 ) struct Library *lib )
{
	++lib->lib_OpenCnt;
	lib->lib_Flags &= ~LIBF_DELEXP;
	return( lib );
}


/* Close is given the library pointer. Be sure not to decrement the open
 * count if already zero. If the open count is or becomes zero AND there
 * is a LIBF_DELEXP, we expunge the library and return the seglist.
 * Otherwise we return NULL.
 *
 * Note that this routine never sets LIBF_DELEXP on its own.
 *
 * Exec has Forbid() for us during the call.
 *
 * Since we don't refer to any smalldata here (directly. Indirectly we refer via
 * LibExpunge, which is LibCall declared), we can safely skip "LibCall", to save
 * a few bytes.
 */
APTR
LibClose( _REG( a6 ) struct Library *lib )
{
	if( lib->lib_OpenCnt && --lib->lib_OpenCnt )
	{
		return( NULL );
	}

	if( lib->lib_Flags & LIBF_DELEXP )
	{
		return( LibExpunge( lib ) );
	}

	return( NULL );
}


/* We expunge the library and return the Seglist ONLY if the open count is zero.
 * If the open count is not zero we set the delayed-expunge flag and return NULL.
 *
 * Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be
 * called from the memory allocator and thus we CANNOT DO A Wait() or otherwise
 * take a long time to complete (straight from RKM).
 *
 * RemLibrary() calls our expunge routine and would therefore freeze if we called
 * it ourselves. LibExpunge() must remove the library itself as shown below.
 */
LibCall APTR
LibExpunge( _REG( a6 ) struct Library *lib )
{
	if( lib->lib_OpenCnt )
	{
		lib->lib_Flags |= LIBF_DELEXP;
		return( NULL );
	}

	Remove( &lib->lib_Node );
	FreeLib( lib );

	return( SegList );
}
