/*

File: filesystem.c
Author: Neil Cafferkey
Copyright (C) 2001-2002 Neil Cafferkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.

*/


#include "handler_protos.h"



/****i* ram.handler/CreateObject *******************************************
*
*   NAME
*	CreateObject --
*
*   SYNOPSIS
*	object = CreateObject(handler,name,type,
*	    parent)
*
*	struct Object *CreateObject(struct Handler *,TEXT *,BYTE,
*	    struct Object *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Object *CreateObject(struct Handler *handler,const TEXT *name,
   BYTE type,struct Object *parent)
{
   struct Object *object;
   LONG error;
   UPINT block_count;

   /* Check for an existing object with the same name */

   error=0;
   object=NULL;
   if(parent!=NULL)
   {
      parent=GetRealObject(parent);
      object=(APTR)FindNameNoCase((struct List *)&parent->elements,name);
   }

   /* Create a new object structure */

   if(object==NULL)
   {
      object=AllocMem(sizeof(struct Object),MEMF_CLEAR);

      if(object!=NULL)
      {
         block_count=MEMBLOCKS(sizeof(struct Object));
         object->block_count=MEMBLOCKS(sizeof(struct Object));
         handler->block_count+=MEMBLOCKS(sizeof(struct Object));

         NewList((struct List *)&object->elements);
         ((struct Node *)object)->ln_Pri=type;
         object->parent=parent;
         DateStamp(&object->date);

         if(name!=NULL)
         {
            name=FilePart(name);
            if(!SetName(handler,object,name))
               error=IoErr();
         }

         if(parent!=NULL)
         {
            AddTail((struct List *)&parent->elements,(struct Node *)object);
            CopyMem(&object->date,&parent->date,sizeof(struct DateStamp));
         }
      }
      else
         error=IoErr();
   }
   else
   {
      error=ERROR_OBJECT_EXISTS;
   }

   if(error==0)
      MatchNotifyRequests(handler);

   if(error!=0)
   {
      DeleteObject(handler,object);
      object=NULL;
   }

   /* Return the new object */

   SetIoErr(error);
   return object;
}



/****i* ram.handler/DeleteObject *******************************************
*
*   NAME
*	DeleteObject --
*
*   SYNOPSIS
*	success = DeleteObject(handler,object)
*
*	BOOL DeleteObject(struct Handler *,struct Object *);
*
*   FUNCTION
*
*   INPUTS
*	object - the object to be deleted, or NULL for no action.
*
*   RESULT
*	success - success indicator.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL DeleteObject(struct Handler *handler,struct Object *object)
{
   LONG error=0;
   BYTE object_type;
   struct Lock *lock;
   struct Block *block;
   struct MinNode *node;
   struct Object *real_object,*master_link,*heir,*dir;
   PINT block_diff;

   if(object!=NULL)
   {
      real_object=GetRealObject(object);

      /* Check for a non-empty, unlinked directory */

      object_type=((struct Node *)object)->ln_Pri;
      if((object_type==ST_USERDIR)&&(object->hard_link.mln_Succ==NULL)
         &&!IsListEmpty((struct List *)&object->elements))
         error=ERROR_DIRECTORY_NOT_EMPTY;

      /* Ensure the object isn't in use */

      lock=LockObject(handler,object,ACCESS_WRITE);
      if(lock==NULL)
         error=IoErr();
      CmdFreeLock(handler,lock);

      if(error==0)
      {
         /* Remove the object from its directory */

         if(object->parent!=NULL)
            Remove((struct Node *)object);

         /* Delete a hard link */

         if((object_type==ST_LINKFILE)||(object_type==ST_LINKDIR))
         {
            node=real_object->hard_link.mln_Succ;
            master_link=HARDLINK(node);
            Remove((APTR)&object->hard_link);
            if(object==master_link)
            {
               node=node->mln_Succ;
               if(node->mln_Succ!=NULL)
               {
                  master_link=HARDLINK(node);
                  while((node=(APTR)RemHead((APTR)&object->elements))!=NULL)
                     AddTail((APTR)&master_link->elements,(APTR)node);
               }
               else
               {
                  real_object->hard_link.mln_Succ=NULL;
                  real_object->hard_link.mln_Pred=NULL;
               }
            }
         }

         /* Delete a linked object */

         else if((node=object->hard_link.mln_Succ)!=NULL)
         {
            master_link=HARDLINK(node);
            heir=HARDLINK(RemTail((APTR)&master_link->elements));

            /* Swap names and comments */

            block_diff=SwapStrings(&((struct Node *)heir)->ln_Name,
               &((struct Node *)object)->ln_Name);
            block_diff+=SwapStrings(&heir->comment,&object->comment);
            object->block_count+=block_diff;
            heir->block_count-=block_diff;

            /* Put object in its new directory */

            object->parent=heir->parent;
            AddTail((APTR)&object->parent->elements,(APTR)object);

            if(heir==master_link)
            {
               real_object->hard_link.mln_Succ=NULL;
               real_object->hard_link.mln_Pred=NULL;
            }

            /* Prepare to destroy "heir" link */

            Remove((APTR)heir);
            object=heir;
         }

         /* Delete the root directory and all other objects */

         else if(object_type==ST_ROOT)
         {
            dir=object;

            while(dir!=NULL)
            {
               object=(APTR)RemHead((struct List *)&dir->elements);

               if(object==NULL)
               {
                  object=dir;
                  dir=dir->parent;
                  if(dir!=NULL)
                     DeleteObject(handler,object);
               }
               else if(((struct Node *)object)->ln_Pri==ST_USERDIR)
               {
                  dir=object;
               }
               else
               {
                  DeleteObject(handler,object);
               }
            }
         }

         /* Delete an unlinked object */

         else
         {
            /* Remove all blocks if the object is a file */

            while((block=(APTR)RemTail((struct List *)&object->elements))
               !=NULL)
               FreeMem(block,sizeof(struct Block)+block->length);
         }

         /* Free object's memory */

         SetString(&((struct Node *)object)->ln_Name,NULL);
         SetString(&object->comment,NULL);
         handler->block_count-=object->block_count;
         FreeMem(object,sizeof(struct Object));
      }
   }

   /* Return result */

   SetIoErr(error);
   return error==0;
}



/****i* ram.handler/GetObject **********************************************
*
*   NAME
*	GetObject --
*
*   SYNOPSIS
*	object = GetObject(handler,lock,name,
*	    parent)
*
*	struct Object *GetObject(struct Handler *,struct Lock *,TEXT *,
*	    struct Object **);
*
*   FUNCTION
*
*   INPUTS
*	lock - .
*	name - .
*	parent - .
*
*   RESULT
*	object - The located object.
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Object *GetObject(struct Handler *handler,
   struct Lock *lock,const TEXT *name,struct Object **parent)
{
   const TEXT *p;
   TEXT ch,buffer[256];
   PINT pos;
   struct Object *object,*old_object;

   old_object=NULL;
   for(ch=*(p=name);(ch!=':')&&(ch!='\0');ch=*(++p));
   if(ch==':')
      name=p+1;

   /* Get object referenced by lock */

   lock=FixLock(handler,lock);
   object=(APTR)((struct FileLock *)lock)->fl_Key;

   /* Traverse textual portion of path */

   pos=0;
   while((pos!=-1)&&(object!=NULL))
   {
      pos=SplitName(name,'/',buffer,pos,256);
      if(*buffer!='\0')
      {
         old_object=object;
         if(((struct Node *)object)->ln_Pri>0)
         {
            object=GetRealObject(object);
            object=(struct Object *)
               FindNameNoCase((struct List *)&object->elements,buffer);
         }
         else
         {
            object=NULL;
            old_object=NULL;
         }
      }
      else if(pos!=-1)
      {
         object=object->parent;
      }
   }

   /* Record the parent directory of the supplied path, if it exists */

   if(parent!=NULL)
   {
      if(pos==-1)
         *parent=old_object;
      else
         *parent=NULL;
   }

   /* Return the located object */

   SetIoErr(ERROR_OBJECT_NOT_FOUND);
   return object;
}



/****i* ram.handler/ChangeFileSize *****************************************
*
*   NAME
*	ChangeFileSize --
*
*   SYNOPSIS
*	new_size = ChangeFileSize(handler,opening,offset,
*	   mode)
*
*	PINT ChangeFileSize(struct Handler *,struct Opening *,PINT,
*	   LONG);
*
*   FUNCTION
*	If there is not enough space for the requested size, the file size
*	will remain at its initial value and -1 will be returned.
*
*
*
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

PINT ChangeFileSize(struct Handler *handler,struct Opening *opening,
   PINT offset,LONG mode)
{
   PINT length,new_length,remainder,end_length,full_length;
   UPINT diff,old_pos,block_count;
   struct Block *block,*end_block;
   struct Object *file;
   LONG error=0;

   /* Get origin */

   file=opening->file;

   if(mode==OFFSET_BEGINNING)
      new_length=0;
   else if(mode==OFFSET_CURRENT)
      new_length=opening->pos;
   else
      new_length=file->length;

   if(-offset>new_length)
   {
      SetIoErr(ERROR_SEEK_ERROR);
      return -1;
   }

   new_length+=offset;
   length=file->length;
   block_count=file->block_count;

   old_pos=opening->pos;

   full_length=length;
   end_length=file->end_length;
   block=(struct Block *)file->elements.mlh_TailPred;
   if(end_length!=0)
   {
      full_length+=block->length-end_length;
   }

   if(new_length>length)
   {
      /* Add the required number of data bytes */

      remainder=new_length-full_length;
      end_length-=remainder;

      while(remainder>0)
      {
         block=AddDataBlock(handler,file,remainder);
         if(block!=NULL)
         {
            end_length=remainder;
            remainder-=block->length;
         }
         else
            remainder=0;
      }

      /* Remove new blocks upon failure */

      if(block==NULL)
      {
         new_length=-1;
         error=IoErr();
         ChangeFileSize(handler,opening,length,OFFSET_BEGINNING);
      }
      else
         file->end_length=end_length;
   }
   else if(length!=new_length)
   {
      /* Remove surplus blocks from the file */

      block=NULL;
      while(full_length>new_length)
      {
         FreeDataBlock(file,block);
         block=(APTR)RemTail((struct List *)&file->elements);
         full_length-=block->length;
      }
      end_block=(struct Block *)file->elements.mlh_TailPred;
      if((APTR)end_block!=(APTR)&file->elements)
         end_length=end_block->length;
      else
         end_length=0;
      file->end_length=end_length;

      /* Allocate a suitably sized end block and copy old data to it */

      diff=new_length-full_length;
      file->length=full_length;
      CmdSeek(opening,0,OFFSET_END);

      if(ChangeFileSize(handler,opening,diff,OFFSET_END)!=-1)
      {
         WriteData(handler,opening,((UBYTE *)block)+sizeof(struct Block),
            diff);
         FreeDataBlock(file,block);
      }
      else
         AddTail((struct List *)&file->elements,(APTR)block);

      /* Ensure opening's position isn't past new EOF */

      if(opening->pos>new_length)
      {
         opening->block=(APTR)&file->elements.mlh_Tail;
         opening->block_pos=0;
         opening->pos=new_length;
      }
   }

   /* Re-establish old seek position */

   CmdSeek(opening,old_pos,OFFSET_BEGINNING);

   /* Store new file size */

   if(new_length!=-1)
      file->length=new_length;
   handler->block_count+=file->block_count-block_count;

   SetIoErr(error);
   return new_length;
}



/****i* ram.handler/ReadData ***********************************************
*
*   NAME
*	ReadData --
*
*   SYNOPSIS
*	read_length = ReadData(opening,buffer,length)
*
*	UPINT ReadData(struct Opening *,UBYTE *,UPINT);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

UPINT ReadData(struct Opening *opening,UBYTE *buffer,UPINT length)
{
   struct Block *block,*end_block;
   UPINT block_pos,block_length,read_length,remainder=length;

   /* Fill buffer until request has been fulfilled or EOF is reached */

   block=opening->block;
   block_pos=opening->block_pos;
   end_block=(struct Block *)opening->file->elements.mlh_TailPred;

   while((remainder>0)&&(((struct MinNode *)block)->mln_Succ!=NULL))
   {
      /* Get number of remaining valid bytes in this block */

      if(block==end_block)
         block_length=opening->file->end_length;
      else
         block_length=block->length;
      block_length-=block_pos;

      /* Copy block contents into the caller's buffer */

      read_length=MIN(remainder,block_length);
      CopyMem(((UBYTE *)block)+sizeof(struct Block)+block_pos,buffer,
         read_length);
      remainder-=read_length;
      buffer+=read_length;

      /* Get next block */

      if(read_length==block_length)
      {
         block=(struct Block *)((struct MinNode *)block)->mln_Succ;
         block_length=block->length;
         block_pos=0;
         read_length=0;
      }
   }

   /* Record new position for next access */

   opening->block=block;
   opening->block_pos=block_pos+read_length;
   length-=remainder;
   opening->pos+=length;

   /* Return number of bytes read */

   return length;
}



/****i* ram.handler/WriteData **********************************************
*
*   NAME
*	WriteData --
*
*   SYNOPSIS
*	write_length = WriteData(handler,opening,buffer,length)
*
*	UPINT WriteData(struct Handler *,struct Opening *,UBYTE *,UPINT);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

UPINT WriteData(struct Handler *handler,struct Opening *opening,
   UBYTE *buffer,UPINT length)
{
   struct Block *block,*end_block;
   struct Object *file;
   UPINT block_pos,block_length,write_length,remainder=length,file_length,
      block_count,pos;
   LONG error=0;

   file=opening->file;
   block_count=file->block_count;
   file_length=file->length;
   block=opening->block;
   block_pos=opening->block_pos;

   while((remainder>0)&&(block!=NULL))
   {
      /* Add another block to the file if required */

      if(((struct MinNode *)block)->mln_Succ==NULL)
      {
         block=AddDataBlock(handler,opening->file,remainder);
         file->end_length=0;
      }

      /* Write as much as possible to the current block */

      if(block!=NULL)
      {
         block_length=block->length-block_pos;
         write_length=MIN(remainder,block_length);
         CopyMem(buffer,((UBYTE *)block)+sizeof(struct Block)+block_pos,
            write_length);
         remainder-=write_length;
         buffer+=write_length;
         block_pos+=write_length;

         /* Move on to next block if end of current block has been reached */

         if(write_length==block_length)
         {
            block=(struct Block *)((struct MinNode *)block)->mln_Succ;
            block_length=block->length;
            block_pos=0;
         }
      }
      else
         error=IoErr();
   }

   /* Recalculate length of used portion of last block */

   end_block=(struct Block *)file->elements.mlh_TailPred;
   if((block==end_block)&&(block_pos>file->end_length))
      file->end_length=block_pos;
   if((struct Block *)((struct MinNode *)block)->mln_Pred==end_block)
      file->end_length=end_block->length;

   /* Update file size, volume size and current position */

   pos=opening->pos;
   length-=remainder;
   pos+=length;
   if(pos>file_length)
      file->length=pos;
   opening->pos=pos;
   opening->block=block;
   opening->block_pos=block_pos;
   handler->block_count+=file->block_count-block_count;

   /* Return number of bytes written */

   SetIoErr(error);
   return length;
}



/****i* ram.handler/LockObject *********************************************
*
*   NAME
*	LockObject --
*
*   SYNOPSIS
*	lock = LockObject(handler,object,
*	    access)
*
*	struct Lock *LockObject(struct Handler *handler,struct Object *,
*	    LONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Lock *LockObject(struct Handler *handler,struct Object *object,
   LONG access)
{
   struct Lock *lock;
   LONG error,lock_access;

   object=GetRealObject(object);
   lock=object->lock;

   if(lock==NULL)
   {
      lock=AllocMem(sizeof(struct Lock),MEMF_PUBLIC|MEMF_CLEAR);

      if(lock!=NULL)
      {
         object->lock=lock;

         ((struct FileLock *)lock)->fl_Key=(PINT)object;
         ((struct FileLock *)lock)->fl_Access=access;
         ((struct FileLock *)lock)->fl_Task=handler->proc_port;
         ((struct FileLock *)lock)->fl_Volume=MKBADDR(handler->volume);
      }
      else
         error=IoErr();
   }
#ifndef AMIGAOS
   else
   {
      lock_access=((struct FileLock *)lock)->fl_Access;
      if((access==ACCESS_WRITE)||(lock_access==ACCESS_WRITE))
      {
         lock=NULL;
         error=ERROR_OBJECT_IN_USE;
      }
   }
#endif

   if(lock!=NULL)
   {
      lock->lock_count++;
      handler->lock_count++;
   }

   SetIoErr(error);

   return lock;
}



/****i* ram.handler/ExamineObject ******************************************
*
*   NAME
*	ExamineObject --
*
*   SYNOPSIS
*	success = ExamineObject(handler,object,
*	    info)
*
*	BOOL ExamineObject(struct Handler *,struct Object *,
*	    struct FileInfoBlock *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL ExamineObject(struct Handler *handler,struct Object *object,
   struct FileInfoBlock *info)
{
   BOOL success;
   struct Object *next_object;
   STRPTR s;
   LONG entry_type;

   if(object==NULL)
   {
      object=(struct Object *)info->fib_DiskKey;
      next_object=(APTR)((struct Node *)object)->ln_Succ;
   }
   else
      next_object=(APTR)object->elements.mlh_Head;

   if(next_object!=NULL)
   {
      /* Fill in information from object */

      entry_type=((struct Node *)object)->ln_Pri;
      info->fib_DirEntryType=entry_type;
      info->fib_EntryType=entry_type;
      s=((struct Node *)object)->ln_Name;
      CopyMem(MkBStr(s),&info->fib_FileName,StrSize(s));
      s=object->comment;
      if(s!=NULL)
         CopyMem(MkBStr(s),&info->fib_Comment,StrLen(s)+1);
      else
         info->fib_Comment[0]='\0';
      info->fib_NumBlocks=object->block_count;

      /* Fill in information from real object */

      object=GetRealObject(object);

      info->fib_Protection=object->protection;
      info->fib_Size=object->length;
      CopyMem(&object->date,&info->fib_Date,sizeof(struct DateStamp));

      /* Prepare for next examination */

      success=TRUE;
      info->fib_DiskKey=(PINT)next_object;
   }
   else
   {
      success=FALSE;
      SetIoErr(ERROR_NO_MORE_ENTRIES);
   }

   return success;
}



/****i* ram.handler/SetName ************************************************
*
*   NAME
*	SetName --
*
*   SYNOPSIS
*	success = SetName(handler,object,name)
*
*	BOOL SetName(struct Handler *,struct Object *,TEXT *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL SetName(struct Handler *handler,struct Object *object,
   const TEXT *name)
{
   LONG error;
   const TEXT *p;
   TEXT ch;
   struct Locale *locale;
   PINT block_diff;

   error=0;

   /* Check name isn't too long */

   if(StrSize(name)>sizeof(((struct FileInfoBlock *)NULL)->fib_FileName))
      error=ERROR_INVALID_COMPONENT_NAME;

   /* Check name doesn't have any strange characters in it */

   locale=handler->locale;
   for(p=name;(ch=*p)!='\0';p++)
      if(!IsPrint(locale,ch)||IsCntrl(locale,ch)||(ch==':'))
         error=ERROR_INVALID_COMPONENT_NAME;

   /* Store new name */

   if(error==0)
   {
      block_diff=SetString(&((struct Node *)object)->ln_Name,name);
      if(block_diff==-1)
         error=IoErr();
      else
      {
         object->block_count+=block_diff;
         handler->block_count+=block_diff;
      }
   }

   /* Return success indicator */

   SetIoErr(error);
   return error==0;
}



/****i* ram.handler/FixLock ************************************************
*
*   NAME
*	FixLock --
*
*   SYNOPSIS
*	fixed_lock = FixLock(handler,lock)
*
*	struct Lock *FixLock(struct Handler *,struct Lock *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Lock *FixLock(struct Handler *handler,struct Lock *lock)
{
   if(lock==NULL)
      lock=handler->root_dir->lock;

   return lock;
}



/****i* ram.handler/AddDataBlock *******************************************
*
*   NAME
*	AddDataBlock --
*
*   SYNOPSIS
*	block = AddDataBlock(file,length)
*
*	struct Block *AddDataBlock(struct Object *,UPINT);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*	Attempts to allocate a smaller block if the requested size cannot be
*	obtained.
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Block *AddDataBlock(struct Handler *handler,struct Object *file,
   UPINT length)
{
   struct Block *block;
   UPINT alloc_size;
   ULONG limit;

   /* Ensure block size is within limits */

   alloc_size=sizeof(struct Block)+length;

   limit=handler->max_block_size;
   if(alloc_size>limit)
      alloc_size=limit;

   limit=handler->min_block_size;
   if(alloc_size<limit)
      alloc_size=limit;

   /* Allocate a multiple of the memory block size */

   block=NULL;
   while((block==NULL)&&(alloc_size>=limit))
   {
      alloc_size=((alloc_size-1)&(~MEM_BLOCKMASK))+MEM_BLOCKSIZE;
      block=AllocMem(alloc_size,MEMF_ANY);
      if(block==NULL)
         alloc_size>>=1;
   }

   /* Add the block to the end of the file */

   if(block!=NULL)
   {
      AddTail((struct List *)&file->elements,(struct Node *)block);
      block->length=alloc_size-sizeof(struct Block);
      file->block_count+=alloc_size>>MEM_BLOCKSHIFT;
   }
   else
      SetIoErr(ERROR_DISK_FULL);

   /* Return the new block */

   return block;
}



/****i* ram.handler/FreeDataBlock ******************************************
*
*   NAME
*	FreeDataBlock --
*
*   SYNOPSIS
*	FreeDataBlock(file,block)
*
*	VOID FreeDataBlock(struct Object *,struct Block *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

VOID FreeDataBlock(struct Object *file,struct Block *block)
{
   UPINT alloc_size;

   if(block!=NULL)
   {
      alloc_size=sizeof(struct Block)+block->length;
      file->block_count-=alloc_size>>MEM_BLOCKSHIFT;
      FreeMem(block,alloc_size);
   }

   return;
}



/****i* ram.handler/GetRealObject ******************************************
*
*   NAME
*	GetRealObject --
*
*   SYNOPSIS
*	real_object = GetRealObject(object)
*
*	struct Object *GetRealObject(struct Object *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Object *GetRealObject(struct Object *object)
{
   struct MinNode *node,*pred;

   /* Get first node in list */

   node=&object->hard_link;
   if(node->mln_Pred!=NULL)
   {
      while((pred=node->mln_Pred)!=NULL)
         node=pred;
      node=node->mln_Succ;
   }

   /* Get object from node address */

   object=HARDLINK(node);
   return object;
}



