/*
   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_library.h"

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

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

static int
PingPong (MsgPort *from, MsgPort *to, StandardPacket *sp,
	  ULONG action, ULONG arg)
{
  sp -> sp_Msg.mn_Node.ln_Name = (char *) &sp -> sp_Pkt;
  sp -> sp_Pkt.dp_Link = &sp -> sp_Msg;
  sp -> sp_Pkt.dp_Port = from;
  sp -> sp_Pkt.dp_Type = action;
  sp -> sp_Pkt.dp_Arg1 = arg;
  PutMsg (to, &sp -> sp_Msg);
  do
    WaitPort (from);
  while (!GetMsg (from));
  return 1;
}

static int
SendToHandler (MsgPort *mp, ULONG ptype, char *handler, ULONG arg)
{
  DevProc *dp = NULL;
  StandardPacket sp;
  int ret = 0;

  dp = GetDeviceProc (handler, dp);
  if (dp)
  {
    PingPong (mp, dp -> dvp_Port, &sp, ACTION_IS_FILESYSTEM, 0);
    if (sp.sp_Pkt.dp_Res1 == DOSTRUE)
    {
      PingPong (mp, dp -> dvp_Port, &sp, ptype, arg);
      ret = 1;
    }
    else
      ret = -1;
  }
  FreeDeviceProc (dp);
  return ret;
}

static int
SendToAll (MsgPort *mp, ULONG ptype, ULONG arg)
{
  List list;
  Node *node;
  DosInfo *di;
  DeviceNode *dn;
  int len, out_of_mem = 0;
  char *name;
  BPTR *pdn;
  int ret;

  NewList (&list);
  Forbid ();
  di = BADDR (((RootNode *) DOSBase -> dl_Root) -> rn_Info);
  for (pdn = &di -> di_DevInfo; dn = (DeviceNode *) BADDR (*pdn);
       pdn = &dn -> dn_Next)
  {
    if (dn -> dn_Type == DLT_DEVICE)
    {
      if (node = (Node *) AllocMem (sizeof (Node), MEMF_CLEAR))
      {
	name = BADDR (dn -> dn_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 (&list, node);
	}
	else
	{
	  out_of_mem = 1;
	  FreeMem (node, sizeof (Node));
	  break;
	}
      }
      else
      {
	out_of_mem = 1;
	break;
      }
    }
  }
  Permit ();

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

  len = 0;
  for (node = list.lh_Head; node -> ln_Succ; node = node -> ln_Succ)
    ret = SendToHandler (mp, ptype, node -> ln_Name, arg);
  FreeList (&list);
  return 1;
}

/*** public functions ***/

int
unmount (MsgPort *mp, char *filesystem)
{
  ULONG arg = DOSTRUE, ptype = ACTION_INHIBIT; 
  int free_port = 0, ret;

  if (!mp)
  {
    if (!(mp = CreatePort (NULL, 0)))
      return 0;
    free_port = 1;
  }
  if (filesystem)
  {
    ret = SendToHandler (mp, ptype, filesystem, arg);
  }
  else
  {
    ret = SendToAll (mp, ptype, arg);
  }
  if (free_port)
    DeletePort (mp);
  return ret;
}

