/*
   halt_library.c --- halt library interface.

   (c) Copyright 1995 SHW Wabnitz
   Written by Bernhard Fastenrath (fasten@shw.com)

   This file may be distributed under the terms
   of the GNU General Public License.
*/

#if defined (__GNUC__)
#include <stabs.h>
#endif

#include "halt_library.h"

struct ExecBase *SysBase = NULL;
struct DosLibrary *DOSBase = NULL;
struct Library *HaltBase = NULL;
struct List Clients;
ULONG ClientsListBusy = 0;

#if defined (__GNUC__)
const BYTE LibName[] = "halt.library";
const BYTE LibIdString[] = "$VER: halt.library 2.0 (10-6-95)";
const UWORD LibVersion = 2;
const UWORD LibRevision = 0;
#endif

#if defined (__GNUC__)
#define LIBRT
#define REG_a6
#define REG_d1
#define STRUCT_MYLIB struct Library
#else
#define LIBRT __saveds __asm
#define REG_a6 register __a6
#define REG_d1 register __d1
#define ADDTABL_1(name,arg1);
#define ADDTABL_END();
#define STRUCT_MYLIB struct MyLibrary
#endif

static void
cleanup (void)
{
  if (DOSBase)
    CloseLibrary ((struct Library *) DOSBase);
  DOSBase = NULL;
}

int LIBRT
__UserLibInit (REG_a6 STRUCT_MYLIB *libbase)
{
  SysBase = *(struct ExecBase **)4;
  HaltBase = (struct Library *) libbase;

  if (!(DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", 37)))
    return 1;
  NewList (&Clients);
  return 0; /* success */
}

void LIBRT
__UserLibCleanup (REG_a6 STRUCT_MYLIB *libbase)
{
  cleanup ();
}

ADDTABL_1(LIBReboot,d1);

int LIBRT
LIBReboot (REG_d1 int flags)
{
  if (flags)
  {
    Disable ();
    while (1);
  }
  ColdReboot ();
  return 1;
}

ADDTABL_1(LIBUnmount,d1);

int LIBRT
LIBUnmount (REG_d1 char *filesystem)
{
  return unmount (NULL, filesystem);
}

ADDTABL_1(LIBAddShutdownPort,d1);

int LIBRT
LIBAddShutdownPort (REG_d1 MsgPort *prt)
{
  ShutdownClient *scl;

  if (((struct Process *) prt -> mp_SigTask) -> pr_WindowPtr != (void *) -1)
    return 0;

  if (!(scl = (ShutdownClient *) AllocMem (sizeof (ShutdownClient), MEMF_PUBLIC)))
    return 0;

  scl -> port = prt;
  scl -> done = 0;
  Forbid ();
  AddHead (&Clients, (Node *) scl);
  Permit ();
  return 1;
}

ADDTABL_1(LIBRemShutdownPort,d1);

int LIBRT
LIBRemShutdownPort (REG_d1 MsgPort *prt)
{
  MinNode *mln;
  int ret = 0;

  Forbid ();
  for (mln = (MinNode *) Clients.lh_Head; mln -> mln_Succ; mln = mln -> mln_Succ)
    if (((ShutdownClient *) mln) -> port == prt)
    {
      if (ClientsListBusy)
	((ShutdownClient *) mln) -> done = 1;
      else
      {
        Remove ((Node *) mln);
	FreeMem (mln, sizeof (ShutdownClient));
      }
      ret = 1;
      break;
    }
  Permit ();
  return ret;
}

ADDTABL_1(LIBShutdownStatus,d1);

int LIBRT
LIBShutdownStatus (REG_d1 ShutdownMessage *smsg)
{
  ShutdownClient *scl, *next;
  ULONG abortable = 0;
  MsgPort *prt;
  Task *me;
  ULONG mp_mask, mask, rmask;

  smsg -> sm_Context -> sc_rMask = 0;
  smsg -> sm_Context -> sc_Port = NULL;

  if (IsListEmpty (&Clients))
    return 1;

  me = FindTask (0);
  prt = smsg -> sm_Msg.mn_ReplyPort;
  if (smsg -> sm_Status != SHUTDOWN_HALT &&
      smsg -> sm_Status != SHUTDOWN_ABORT &&
      smsg -> sm_Status != SHUTDOWN_UMOUNT)
  {
    abortable = smsg -> sm_Flags & SDMF_ABORTABLE;
  }
  Forbid ();
  ClientsListBusy ++;
  Permit ();
  mp_mask = 1 << prt -> mp_SigBit;
  mask = mp_mask | SIGBREAKF_CTRL_C | smsg -> sm_Context -> sc_Mask;

  scl = (ShutdownClient *) Clients.lh_Head;
  while (next = (ShutdownClient *) scl -> node.mln_Succ)
  {
    if (scl -> done)
    {
      Forbid ();
      if (ClientsListBusy == 1)
      {
        Remove ((Node *) scl);
        FreeMem (scl, sizeof (ShutdownClient));
      }
      Permit ();
    }
    else
    {
      if (scl -> port -> mp_SigTask != me)
      {
        PutMsg (scl -> port, (Message *) smsg);
	for (;;)
        {
	  if ((rmask = Wait (mask)) & mp_mask)
	  {
	    if (GetMsg (prt) == (Message *) smsg)
	      break;
	    else
	      smsg -> sm_Context -> sc_TimeOuts --;
          }
          if (rmask & SIGBREAKF_CTRL_C)
	  {
	    smsg -> sm_Flags |= (SDMF_CANCEL | SDMF_CTRL_C);
	    smsg -> sm_Context -> sc_Port = scl -> port;
	    smsg -> sm_Context -> sc_TimeOuts ++;
	    break;
          }
          if (rmask & smsg -> sm_Context -> sc_Mask)
	  {
	    /* Usually this is a timeout so we put the nasty
	       program at the end of the list.
	    */
	    Remove ((Node *) scl);
	    AddTail (&Clients, (Node *) scl);
	    smsg -> sm_Flags |= SDMF_SIG_MASK;
	    smsg -> sm_Context -> sc_Port = scl -> port;
	    smsg -> sm_Context -> sc_TimeOuts ++;
	    break;
          }
        }
	if (abortable && smsg -> sm_Flags & (SDMF_CANCEL | SDMF_SIG_MASK))
          break;
      }
    }
    scl = next;
  }
  smsg -> sm_Context -> sc_rMask = rmask;
  if (!abortable)
    smsg -> sm_Flags &= ~(SDMF_CANCEL | SDMF_CTRL_C);

  Forbid ();
  ClientsListBusy --;
  Permit ();
  return 1;
}

ADDTABL_END();

