/* LibHandler.c

   Copyright 1993 by Christopher A. Wichura (caw@miroc.chi.il.us)
   All Rights Reserved.
*/

struct SignalSemaphore HandleListSema;
struct MinList HandleList;

 /*
  * we need to serialize people calling our Open and Close routines so use
  * another semaphore for that
  */
static struct SignalSemaphore OpenCloseSema;

 /* pointers to a couple of librarys we open */
struct DosLibrary *DOSBase;
struct Library *UtilityBase;
struct Library *OwnDevUnitBase;

 /* pointer to SysBase used by the rest of the system */
struct ExecBase *SysBase;

APTR pool;
struct SignalSemaphore poolSema;

  struct MyLibrary {
	  struct Library ml_Lib;
	  BPTR ml_SegList;
  };

/***************************************************************************/
/***************************************************************************/
/**     These are the library base routines used to open/close us, etc    **/
/***************************************************************************/
/***************************************************************************/

struct MyLibrary *__saveds __asm LibInit(register __d0 struct MyLibrary *LibBase, register __a0 BPTR LibSegment)
{
	SysBase = *(struct ExecBase **)4L;
	LibBase->ml_SegList = LibSegment;

	NewList((struct List *)&HandleList);
	InitSemaphore(&HandleListSema);

	InitSemaphore(&OpenCloseSema);

	InitSemaphore(&poolSema);

#define POOLSIZE (4096)
	if (!(pool = CreatePool(MEMF_PUBLIC | MEMF_CLEAR, POOLSIZE, POOLSIZE / 2))) {
		Alert(AT_Recovery | AG_NoMemory);
		return 0L;
	}

	return LibBase;
}

ULONG __saveds __asm LibExpunge(register __a6 struct MyLibrary *LibraryBase)
{
	BOOL KillLib;
	LONG LibSize;
	BPTR LibrarySeg;

	KillLib = FALSE;

	/*
	 * we will refuse to expunge if we are currently tracking devices,
	 * regardless of our OpenCnt.  We AttemptSemaphore() the HandleList
	 * before checking it.  If someone else owns it then obviously we
	 * can't try to shut down.
	 */

	if (LibraryBase->ml_Lib.lib_OpenCnt == 0 && AttemptSemaphore(&HandleListSema)) {
		if (HandleList.mlh_TailPred == (struct MinNode *)&HandleList.mlh_Head) {
			Remove((struct Node *)LibraryBase);
			KillLib = TRUE;
		}

		ReleaseSemaphore(&HandleListSema);
	}

	if (KillLib) {
		DeletePool(pool);

		LibrarySeg = LibraryBase->ml_SegList;
		LibSize = LibraryBase->ml_Lib.lib_NegSize + LibraryBase->ml_Lib.lib_PosSize;
		FreeMem((char *)LibraryBase - LibraryBase->ml_Lib.lib_NegSize, LibSize);

		return (ULONG)LibrarySeg;
	}

	LibraryBase->ml_Lib.lib_Flags |= LIBF_DELEXP;
	return (ULONG)NULL;
}

struct MyLibrary *__saveds __asm LibOpen(register __a6 struct MyLibrary *LibraryBase)
{
	ObtainSemaphore(&OpenCloseSema);

	if (++LibraryBase->ml_Lib.lib_OpenCnt == 1) {
		/* when opencount = 1 then we need to open libraries */
		if (DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)) {
			if (UtilityBase = OpenLibrary("utility.library", 37)) {

				/*
				 * note that we really do care if we can open
				 * ODU or not.  This is because ODU is the
				 * principal mechanism used by the lock
				 * routines and if ODU isn't around then we
				 * can't insure that we have really locked a
				 * file and could potentially run into
				 * problems.
				 */

				if (OwnDevUnitBase = OpenLibrary(ODU_NAME, 0)) {

					/*
					 * we have opened everything we need
					 * successfully so clear the delayed
					 * expunge flag and return
					 * LibraryBase to indicate that the
					 * library opened ok.
					 */

					LibraryBase->ml_Lib.lib_Flags &= ~LIBF_DELEXP;
					ReleaseSemaphore(&OpenCloseSema);
					return LibraryBase;
				}
				else
					Alert(AT_Recovery | AG_OpenLib | AO_Unknown);

				CloseLibrary(UtilityBase);
				UtilityBase = NULL;
			}
			else
				Alert(AT_Recovery | AG_OpenLib | AO_UtilityLib);

			CloseLibrary((struct Library *)DOSBase);
			DOSBase = NULL;
		}
		else
			Alert(AT_Recovery | AG_OpenLib | AO_DOSLib);

		ReleaseSemaphore(&OpenCloseSema);
		return (struct MyLibrary *)NULL;
	}

	LibraryBase->ml_Lib.lib_Flags &= ~LIBF_DELEXP;
	ReleaseSemaphore(&OpenCloseSema);
	return LibraryBase;
}

ULONG __saveds __asm LibClose(register __a6 struct MyLibrary *LibraryBase)
{
	BOOL DoExpunge = FALSE;

	ObtainSemaphore(&OpenCloseSema);

	if (--LibraryBase->ml_Lib.lib_OpenCnt == 0) {
		CloseLibrary(OwnDevUnitBase);	/* relies on KS 2.0+ allowing
						 * one to call CloseLibrary()
						 * with a NULL base.  If
						 * making a library for user
						 * under 1.3 (ugh!) make sure
						 * that closing of library
						 * bases which are not
						 * mandatory have checks to
						 * make sure they are not
						 * NULL before calling
						 * CloseLibrary(). */
		OwnDevUnitBase = NULL;
		CloseLibrary(UtilityBase);
		UtilityBase = NULL;
		CloseLibrary((struct Library *)DOSBase);
		DOSBase = NULL;

		if (LibraryBase->ml_Lib.lib_Flags & LIBF_DELEXP) {
			DoExpunge = TRUE;
		}
	}

	ReleaseSemaphore(&OpenCloseSema);

	if (DoExpunge)
		return LibExpunge(LibraryBase);
	else
		return (ULONG)NULL;
}
