/*
   unmount.c --- unmount filesystems.

   (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.
*/

#include "shutdown_cmd.h"

typedef struct {
  struct Message   sp_Msg;
  struct DosPacket sp_Pkt;
  struct Node     *sp_Dev;
} MyStandardPacket;

static void
FreeList (List *list)
{
  Node *node, *next;

  for (next = node = list -> lh_Head; next = node -> ln_Succ; node = next)
  {
    if (node -> ln_Name)
      FreeMem (node -> ln_Name, node -> ln_Pri);
    FreeMem (node, sizeof (Node));
  }
}

static void
FreeStdPktList (List *list)
{
  Node *node, *next;

  for (next = node = list -> lh_Head; next = node -> ln_Succ; node = next)
    FreeMem (node, sizeof (MyStandardPacket));
}

static int
SendStandardPacket (MsgPort *mp, MyStandardPacket *sp, char *handler, ULONG action, ULONG arg1)
{
  DevProc *dp = NULL;

  dp = GetDeviceProc (handler, dp);
  if (dp)
  {
    sp -> sp_Pkt.dp_Type = action;
    sp -> sp_Pkt.dp_Arg1 = arg1;
    SendPkt (&sp -> sp_Pkt, dp -> dvp_Port, mp);
    FreeDeviceProc (dp);
    return 1;
  }
  return 0;
}

static int
AllocateStandardPackets (List *pktlist, int count)
{
  MyStandardPacket *sp;  
  int t;

  NewList (pktlist);

  for (t=0; t<count; t++)
  {
    if (!(sp = AllocMem (sizeof (MyStandardPacket), MEMF_PUBLIC | MEMF_CLEAR)))
      break;
    sp -> sp_Msg.mn_Length = sizeof (MyStandardPacket);
    sp -> sp_Msg.mn_Node.ln_Name = (char *) &sp -> sp_Pkt;
    sp -> sp_Pkt.dp_Link = &sp -> sp_Msg;
    AddTail (pktlist, &sp -> sp_Msg.mn_Node);
  }
  return t;
}

static int
SendToAll (MsgPort *mp, ULONG action, ULONG arg1)
{
  int len, count = 0, icount = 0, out_of_mem = 0;
  List devlist, pktlist;
  MyStandardPacket *sp;
  Node *node;
  DosList *dl;
  char *name;

  NewList (&devlist);
  NewList (&pktlist);

  dl = LockDosList ( LDF_DEVICES | LDF_READ );

  while (dl = NextDosEntry (dl, LDF_DEVICES))
  {
    if (node = (Node *) AllocMem (sizeof (Node), MEMF_CLEAR))
    {
      name = BADDR (dl -> dol_Name);
      len = (int) name[0];

      if (node -> ln_Name = AllocMem (len + 2, 0))
      {
        CopyMem (name+1, node -> ln_Name, len);
        node -> ln_Name[len] = ':';
        node -> ln_Name[len+1] = '\0';
        node -> ln_Pri = len + 2;
        AddTail (&devlist, node);
        count ++;
      }
      else
      {
        out_of_mem = 1;
        FreeMem (node, sizeof (Node));
        break;
      }
    }
  }

  UnLockDosList ( LDF_DEVICES | LDF_READ );

  if ((icount = AllocateStandardPackets (&pktlist, count)) == 0)
    out_of_mem = 1;
  count = icount;

  if (out_of_mem)
  {
    FreeList (&devlist);
    return 0;
  }

  node = devlist.lh_Head;
  for (;;)
  {
    while (node -> ln_Succ)
    {
      if (sp = (MyStandardPacket *) RemHead (&pktlist))
      {
        sp -> sp_Dev = node;
        SendStandardPacket (mp, sp, node -> ln_Name, ACTION_IS_FILESYSTEM, 0);
        icount --;
        node = node -> ln_Succ;
      }
    }
    WaitPort (mp);

    while (sp = (MyStandardPacket *) GetMsg (mp))
    {
      icount ++;

      if (sp -> sp_Pkt.dp_Type == ACTION_IS_FILESYSTEM &&
          sp -> sp_Pkt.dp_Res1 == DOSTRUE)
      {
        SendStandardPacket (mp, sp, sp -> sp_Dev -> ln_Name, action, arg1);
        icount --;
      }
      else
        AddTail (&pktlist, &sp -> sp_Msg.mn_Node);
    }
    if (!node -> ln_Succ && icount == count)
      break;
  }
  FreeStdPktList (&pktlist);
  FreeList (&devlist);
  return 1;
}

static int
FsAction (MsgPort *mp, char *filesystem, ULONG action, ULONG arg)
{
#if 0
  if (filesystem)
    return !SendToHandler (mp, action, filesystem, arg);
  else
#endif
  return !SendToAll (mp, action, arg);
}

/*** public functions ***/

int
Unmount (char *filesystem, int mode)
{
  MsgPort *mp;
  int status;

  if (!(mp = CreatePort (NULL, 0)))
    return 0;

  switch (mode)
  {
    case UMNT_INHIBIT:
      status  = FsAction (mp, filesystem, ACTION_INHIBIT, DOSTRUE);
      break;
    case UMNT_READONLY:
      status  = FsAction (mp, filesystem, ACTION_FLUSH, DOSTRUE);
      status |= FsAction (mp, filesystem, ACTION_WRITE_PROTECT, DOSTRUE);
      break;
    case UMNT_REMOUNT:
      /* status  = FsAction (mp, filesystem, ACTION_WRITE_PROTECT, DOSFALSE); */
      status  = FsAction (mp, filesystem, ACTION_INHIBIT, DOSTRUE);
      status |= FsAction (mp, filesystem, ACTION_INHIBIT, DOSFALSE);
      break;
  }
  DeletePort (mp);
  return !status;
}
