/*

File: commands.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"


const TEXT utility_name[]=UTILITYNAME;
const TEXT locale_name[]="locale.library";
const TEXT default_vol_name[]="Ram Disc";



/****i* ram.handler/CmdStartup *********************************************
*
*   NAME
*	CmdStartup --
*
*   SYNOPSIS
*	handler = CmdStartup(name,dev_node,
*	    proc_port)
*
*	struct Handler *CmdStartup(STRPTR,struct DeviceNode *,
*	    struct MsgPort *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Handler *CmdStartup(STRPTR name,struct DeviceNode *dev_node,
   struct MsgPort *proc_port)
{
   struct DosList *volume,*dos_list;
   struct Handler *handler;
   struct Object *root_dir;
   struct MsgPort *port;
   LONG error;
   APTR base;

   /* Open extra libraries */

   error=0;
   base=OpenLibrary(utility_name,UTILITY_VERSION);
   if(base!=NULL)
      UtilityBase=base;
   else
      error=1;

   base=OpenLibrary(locale_name,LOCALE_VERSION);
   if(base!=NULL)
      LocaleBase=base;
   else
      error=1;

   /* Initialise private handler structure */

   handler=AllocMem(sizeof(struct Handler),MEMF_CLEAR);

   if(handler==NULL)
      error=IoErr();

   if(error==0)
   {
      handler->proc_port=proc_port;
      handler->block_count=MEMBLOCKS(StrSize(default_vol_name))+
         MEMBLOCKS(sizeof(struct Handler));
      handler->min_block_size=MIN_BLOCK_SIZE;
      handler->max_block_size=MAX_BLOCK_SIZE;

      dev_node->dn_Task=proc_port;
   }

   /* Create a volume dos node */

   volume=MyMakeDosEntry(default_vol_name,DLT_VOLUME);
   if(volume==NULL)
      error=IoErr();

   if(error==0)
   {
      volume->dol_Task=proc_port;
      DateStamp(&volume->dol_misc.dol_volume.dol_VolumeDate);
      volume->dol_misc.dol_volume.dol_DiskType=ID_DOS_DISK;

      /* Put volume node into dos list */

      dos_list=LockDosList(LDF_WRITE|LDF_ALL);
      AddDosEntry(volume);
      handler->volume=volume;
      UnLockDosList(LDF_WRITE|LDF_ALL);
   }

   /* Open default locale */

   handler->locale=OpenLocale(NULL);

   /* Initialise notification handling */

   NewList((APTR)&handler->notifications);
   port=CreateMsgPort();
   handler->notify_port=port;
   if(port==NULL)
      error=ERROR_NO_FREE_STORE;

   /* Create the root directory and get a shared lock on it */

   root_dir=CreateObject(handler,default_vol_name,ST_ROOT,NULL);
   handler->root_dir=root_dir;
   if(root_dir!=NULL)
   {
      if(LockObject(handler,root_dir,ACCESS_READ)==NULL)
         error=IoErr();
   }
   else
      error=IoErr();

   /* Shut down handler if an error occurred */

   if(error!=0)
      CmdDie(handler);

   /* Return result */

   SetIoErr(error);
   return handler;
}



/****i* ram.handler/CmdDie *************************************************
*
*   NAME
*	CmdDie --
*
*   SYNOPSIS
*	success = CmdDie(handler)
*
*	BOOL CmdDie(struct Handler *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdDie(struct Handler *handler)
{
   struct DosList *volume;
   struct Object *root_dir;
   struct Lock *root_lock;
   LONG error;
   APTR base;

   error=0;
   if(handler!=NULL)
   {
      if(handler->lock_count>1)
         error=ERROR_OBJECT_IN_USE;

      if(error==0)
      {
         root_dir=handler->root_dir;
         if(root_dir!=NULL)
         {
            root_lock=root_dir->lock;
            if(root_lock!=NULL)
               CmdFreeLock(handler,root_lock);
            DeleteObject(handler,root_dir);
         }

         volume=handler->volume;
         if(volume!=NULL)
         {
            RemDosEntry(volume);
            MyFreeDosEntry(volume);
         }
         CloseLocale(handler->locale);
         DeleteMsgPort(handler->notify_port);
         FreeMem(handler,sizeof(struct Handler));
      }

   }

   /* Close libraries */

   base=LocaleBase;
   if(base!=NULL)
      CloseLibrary(base);

   base=UtilityBase;
   if(base!=NULL)
      CloseLibrary(base);

   /* Return success indicator */

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



/****i* ram.handler/CmdIsFileSystem ****************************************
*
*   NAME
*	CmdIsFileSystem --
*
*   SYNOPSIS
*	result = CmdIsFileSystem()
*
*	BOOL CmdIsFileSystem();
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdIsFileSystem()
{
   return TRUE;
}



/****i* ram.handler/CmdFind ************************************************
*
*   NAME
*	CmdFind --
*
*   SYNOPSIS
*	success = CmdFind(handler,handle,lock,
*	    name,type)
*
*	BOOL CmdFind(struct Handler *,struct FileHandle *,struct Lock *,
*	    TEXT *,ULONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdFind(struct Handler *handler,struct FileHandle *handle,
   struct Lock *lock,const TEXT *name,ULONG type)
{
   LONG error=0;
   ULONG mode;
   struct Object *file,*parent;

   /* Set access mode */

   if(type==MODE_OLDFILE)
      mode=ACCESS_READ;
   else
      mode=ACCESS_WRITE;

   /* Get file */

   if((type==MODE_NEWFILE)&&(handler->locked))
      error=ERROR_DISK_WRITE_PROTECTED;

   if(error==0)
   {
      file=GetObject(handler,lock,name,&parent);

/*#ifdef AMIGAOS*/
      if(type==MODE_READWRITE)
      {
         if(file==NULL)
            file=CreateObject(handler,name,ST_FILE,parent);
      }
/*#endif*/

      if(type==MODE_NEWFILE)
      {
         if(parent!=NULL)
         {
            if(DeleteObject(handler,file))
               file=CreateObject(handler,name,ST_FILE,parent);
            else
               file=NULL;
         }
      }

      if(file==NULL)
         error=IoErr();
   }

   /* Get a lock on the file */

   if(error==0)
   {
      lock=LockObject(handler,file,mode);
      if(lock==NULL)
         error=IoErr();
      else if(type==MODE_NEWFILE)
         lock->changed=TRUE;
   }

   /* Open file */

   if(error==0)
   {
      if(!CmdFHFromLock(handle,lock))
      {
         error=IoErr();
         CmdFreeLock(handler,lock);
      }
   }

   /* Set error and return success code */

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



/****i* ram.handler/CmdFHFromLock ******************************************
*
*   NAME
*	CmdFHFromLock --
*
*   SYNOPSIS
*	success = CmdFHFromLock(handle,lock)
*
*	BOOL CmdFHFromLock(struct FileHandle *,struct Lock *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdFHFromLock(struct FileHandle *handle,struct Lock *lock)
{
   LONG error=0;
   struct Opening *opening;
   struct Object *file;

   /* Check if access is allowed */

   file=(APTR)((struct FileLock *)lock)->fl_Key;
   if(((struct Node *)file)->ln_Pri!=ST_FILE)
      error=ERROR_OBJECT_WRONG_TYPE;

   /* Create and initialise "opening" */

   if(error==0)
   {
      opening=AllocMem(sizeof(struct Opening),MEMF_CLEAR);
      if(opening!=NULL)
      {
         opening->file=file;
         opening->block=(struct Block *)file->elements.mlh_Head;
      }
      else
         error=IoErr();
   }

   /* Put address of opening in file handle */

   handle->fh_Arg1=(PINT)opening;

   /* Set error and return success code */

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



/****i* ram.handler/CmdEnd *************************************************
*
*   NAME
*	CmdEnd --
*
*   SYNOPSIS
*	success = CmdEnd(handler,opening)
*
*	BOOL CmdEnd(struct Handler *,struct Opening *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdEnd(struct Handler *handler,struct Opening *opening)
{
   struct Object *file,*parent;
   struct Lock *lock;
   LONG error;

   /* Close file and update its date and flags */

   error=0;
   file=opening->file;
   lock=file->lock;
   if(lock->changed)
   {
      if(!handler->locked)
      {
         file->protection&=~FIBF_ARCHIVE;
         DateStamp(&file->date);
         parent=file->parent;
         if(parent!=NULL)
         {
            parent->protection&=~FIBF_ARCHIVE;
            CopyMem(&file->date,&parent->date,sizeof(struct DateStamp));
         }

         NotifyAll(handler,file,TRUE);
      }
      else
         error=ERROR_DISK_WRITE_PROTECTED;
   }
   if(error==0)
   {
      CmdFreeLock(handler,lock);
      FreeMem(opening,sizeof(struct Opening));
   }

   /* Return success indicator */

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



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

UPINT CmdRead(struct Opening *opening,UBYTE *buffer,UPINT length)
{
   LONG error;
   struct Object *file;
   struct Lock *lock;

   /* Read file if it isn't read protected */

   error=0;
   file=opening->file;
   lock=file->lock;

   if(file->protection&FIBF_READ)
      error=ERROR_READ_PROTECTED;

   if(error==0)
   {
      length=ReadData(opening,buffer,length);
      error=IoErr();
   }
   else
   {
      length=-1;
   }

   /* Return number of bytes read */

   SetIoErr(error);
   return length;
}



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

UPINT CmdWrite(struct Handler *handler,struct Opening *opening,
   UBYTE *buffer,UPINT length)
{
   LONG error;
   struct Object *file;
   struct Lock *lock;

   /* Write to file if it isn't write protected */

   error=0;
   file=opening->file;
   lock=file->lock;

#ifndef AMIGAOS
   if(((struct FileLock *)lock)->fl_Access==ACCESS_READ)
      error=ERROR_OBJECT_IN_USE;
                                 /* ERROR_WRONG_LOCK_TYPE would be better */
#endif

   if(file->protection&FIBF_WRITE)
      error=ERROR_WRITE_PROTECTED;

   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   if(error==0)
   {
      length=WriteData(handler,opening,buffer,length);
      error=IoErr();
      if(length>0)
         lock->changed=TRUE;
   }
   else
   {
      length=-1;
   }

   /* Return number of bytes written */

   SetIoErr(error);
   return length;
}



/****i* ram.handler/CmdSeek ************************************************
*
*   NAME
*	CmdSeek --
*
*   SYNOPSIS
*	old_pos = CmdSeek(opening,offset,mode)
*
*	PINT CmdSeek(struct Opening *,PINT,LONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
* Note: Being at EOF is represented by the current block being the dummy
* tail and the block position being zero.
*
*/

UPINT CmdSeek(struct Opening *opening,PINT offset,LONG mode)
{
   struct Block *block;
   struct Object *file;
   UPINT block_pos,block_length,remainder,old_pos,new_pos;

   /* Get starting point */

   file=opening->file;
   old_pos=opening->pos;

   if(mode==OFFSET_BEGINNING)
   {
      block=(APTR)file->elements.mlh_Head;
      new_pos=block_pos=0;
   }
   else if(mode==OFFSET_CURRENT)
   {
      block=opening->block;
      block_pos=opening->block_pos;
      new_pos=old_pos;
   }
   else
   {
      block=(APTR)file->elements.mlh_TailPred;
      block_pos=file->end_length;
      if(block_pos==0)
         block=(APTR)((struct MinNode *)block)->mln_Succ;
      new_pos=file->length;
   }

   new_pos+=offset;
   if(new_pos>file->length)
   {
      SetIoErr(ERROR_SEEK_ERROR);
      return -1;
   }

   if(offset>=0)
   {
      /* Go forwards */

      if(((struct MinNode *)block)->mln_Succ!=NULL)
         block_length=block->length;
      else
         block_length=1;
      remainder=offset+block_pos;
      while(remainder>=block_length)
      {
         remainder-=block_length;
         block=(APTR)((struct MinNode *)block)->mln_Succ;
         if(remainder!=0)
            block_length=block->length;
      }

      block_pos=remainder;
   }
   else
   {
      /* Go backwards */

      if(((struct MinNode *)block)->mln_Succ!=NULL)
         block_length=block->length;
      else
         block_length=0;
      remainder=-offset+(block_length-block_pos);
      while(remainder>block_length)
      {
         remainder-=block_length;
         block=(APTR)((struct MinNode *)block)->mln_Pred;
         block_length=block->length;
      }

      block_pos=block_length-remainder;
   }

   /* Record new position for next access */

   opening->block=block;
   opening->block_pos=block_pos;
   opening->pos=new_pos;

   /* Return old position */

   return old_pos;
}



/****i* ram.handler/CmdSetFileSize *****************************************
*
*   NAME
*	CmdSetFileSize --
*
*   SYNOPSIS
*	new_length = CmdSetFileSize(handler,opening,offset,mode)
*
*	PINT CmdSetFileSize(struct Handler *,struct Opening *,PINT,LONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

PINT CmdSetFileSize(struct Handler *handler,struct Opening *opening,
   PINT offset,LONG mode)
{
   LONG error,new_size;
   struct Object *file;
   struct Lock *lock;

   /* Change file size if it's open for writing */

   error=0;
   file=opening->file;
   lock=file->lock;

   if(((struct FileLock *)lock)->fl_Access==ACCESS_READ)
      error=ERROR_OBJECT_IN_USE;
   if(file->protection&FIBF_WRITE)
      error=ERROR_WRITE_PROTECTED;
   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   if(error==0)
   {
      new_size=ChangeFileSize(handler,opening,offset,mode);
      if(new_size==-1)
         error=IoErr();
      else
         lock->changed=TRUE;
   }
   else
      new_size=-1;

   /* Return new file size */

   SetIoErr(error);
   return new_size;
}



/****i* ram.handler/CmdLocateObject ****************************************
*
*   NAME
*	CmdLocateObject --
*
*   SYNOPSIS
*	lock = CmdLocateObject(handler,lock,name,
*	    mode)
*
*	struct Lock *CmdLocateObject(struct Handler *,struct Lock *,TEXT *,
*	    ULONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct Lock *CmdLocateObject(struct Handler *handler,
   struct Lock *lock,const TEXT *name,ULONG mode)
{
   LONG error=0;
   struct Object *object;

   /* Find the object and lock it */

   object=GetObject(handler,lock,name,NULL);

   if(object!=NULL)
      lock=LockObject(handler,object,mode);
   else
      lock=NULL;

   if(lock==NULL)
      error=IoErr();

   /* Return result */

   SetIoErr(error);
   return lock;
}



/****i* ram.handler/CmdFreeLock ********************************************
*
*   NAME
*	CmdFreeLock --
*
*   SYNOPSIS
*	success = CmdFreeLock(handler,lock)
*
*	BOOL CmdFreeLock(struct Handler *,struct Lock *);
*
*   FUNCTION
*
*   INPUTS
*	lock - May be NULL.
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdFreeLock(struct Handler *handler,struct Lock *lock)
{
   struct Object *object;

   if(lock!=NULL)
   {
      object=(APTR)((struct FileLock *)lock)->fl_Key;

      handler->lock_count--;
      if((--lock->lock_count)==0)
      {
         FreeMem(lock,sizeof(struct Lock));
         object->lock=NULL;
      }
   }

   return TRUE;
}



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

struct Lock *CmdCopyDir(struct Handler *handler,struct Lock *lock)
{
   lock=FixLock(handler,lock);
   lock=LockObject(handler,(APTR)((struct FileLock *)lock)->fl_Key,
      ACCESS_READ);

   return lock;
}



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

struct Lock *CmdCopyDirFH(struct Handler *handler,
   struct Opening *opening)
{
   return LockObject(handler,opening->file,ACCESS_READ);
}



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

struct Lock *CmdParent(struct Handler *handler,struct Lock *lock)
{
   struct Object *parent;
   LONG error;

   error=0;
   lock=FixLock(handler,lock);

   parent=((struct Object *)((struct FileLock *)lock)->fl_Key)->parent;
   if(parent!=NULL)
   {
      lock=LockObject(handler,parent,ACCESS_READ);
      if(lock==NULL)
         error=IoErr();
   }
   else
      lock=NULL;

   SetIoErr(error);
   return lock;
}



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

struct Lock *CmdParentFH(struct Handler *handler,
   struct Opening *opening)
{
   struct Object *parent;
   struct Lock *lock;
   LONG error;

   error=0;

   parent=opening->file->parent;
   if(parent!=NULL)
   {
      lock=LockObject(handler,parent,ACCESS_READ);
      if(lock==NULL)
         error=IoErr();
   }
   else
   {
      lock=NULL;
      error=ERROR_OBJECT_NOT_FOUND;
   }

   return lock;
}



/****i* ram.handler/CmdSameLock ********************************************
*
*   NAME
*	CmdSameLock --
*
*   SYNOPSIS
*	result = CmdSameLock(lock1,lock2)
*
*	BOOL CmdSameLock(struct Lock *,struct Lock *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdSameLock(struct Lock *lock1,struct Lock *lock2)
{
   BOOL same;

   if(lock1==lock2)
      same=TRUE;
   else
      same=FALSE;

   SetIoErr(0);
   return same;
}



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

struct Lock *CmdCreateDir(struct Handler *handler,
   struct Lock *lock,const TEXT *name)
{
   struct Object *dir,*parent;
   LONG error;

   /* Find parent directory and possible name clash */

   dir=GetObject(handler,lock,name,&parent);
   lock=NULL;
   error=0;

   /* Create a new directory */

   if(dir==NULL)
   {
      if(parent!=NULL)
      {
         if(!handler->locked)
         {
            dir=CreateObject(handler,name,ST_USERDIR,parent);
            if(dir!=NULL)
            {
               lock=LockObject(handler,dir,ACCESS_WRITE);
               if(lock!=NULL)
                  NotifyAll(handler,dir,FALSE);
               else
               {
                  error=IoErr();
                  DeleteObject(handler,dir);
               }
            }
            else
               error=IoErr();
         }
         else
            error=ERROR_DISK_WRITE_PROTECTED;
      }
      else
         error=ERROR_OBJECT_NOT_FOUND;
   }
   else
      error=ERROR_OBJECT_EXISTS;

   /* Set error code and return lock on new directory */

   SetIoErr(error);
   return lock;
}



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

BOOL CmdExamineObject(struct Handler *handler,struct Lock *lock,
   struct FileInfoBlock *info)
{
   lock=FixLock(handler,lock);
   return ExamineObject(handler,(APTR)((struct FileLock *)lock)->fl_Key,
      info);
}



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

BOOL CmdExamineFH(struct Handler *handler,struct Opening *opening,
   struct FileInfoBlock *info)
{
   return ExamineObject(handler,opening->file,info);
}



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

BOOL CmdExamineNext(struct Handler *handler,struct FileInfoBlock *info)
{
   return ExamineObject(handler,NULL,info);
}



/****i* ram.handler/CmdInfo ************************************************
*
*   NAME
*	CmdInfo --
*
*   SYNOPSIS
*	success = CmdInfo(handler,info_data)
*
*	BOOL CmdExamineObject(struct Handler *,struct InfoData *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdInfo(struct Handler *handler,struct InfoData *info_data)
{
   LONG disk_state;

   info_data->id_NumSoftErrors=0;
   info_data->id_UnitNumber=0;
   if(handler->locked)
      disk_state=ID_WRITE_PROTECTED;
   else
      disk_state=ID_VALIDATED;
   info_data->id_DiskState=disk_state;
   info_data->id_NumBlocks=handler->block_count
      +MEMBLOCKS(AvailMem(MEMF_ANY));
   info_data->id_NumBlocksUsed=handler->block_count;
   info_data->id_BytesPerBlock=MEM_BLOCKSIZE;
   info_data->id_DiskType=ID_DOS_DISK;
   info_data->id_VolumeNode=MKBADDR(handler->volume);
   if(handler->lock_count>1)
      info_data->id_InUse=DOSTRUE;
   else
      info_data->id_InUse=DOSFALSE;

   return TRUE;
}



/****i* ram.handler/CmdSetProtect ******************************************
*
*   NAME
*	CmdSetProtect --
*
*   SYNOPSIS
*	success = CmdSetProtect(handler,lock,name,flags)
*
*	BOOL CmdSetProtect(struct Handler *,struct Lock *,TEXT *,ULONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdSetProtect(struct Handler *handler,struct Lock *lock,
   const TEXT *name,ULONG flags)
{
   LONG error;
   struct Object *object;

   /* Set new protection flags if object isn't in use */

   error=0;
   object=GetObject(handler,lock,name,NULL);
   if(object!=NULL)
   {
      if(handler->locked)
         error=ERROR_DISK_WRITE_PROTECTED;

      if(error==0)
      {
         object=GetRealObject(object);
         if(object->lock==NULL)
         {
            object->protection=flags;
            NotifyAll(handler,object,TRUE);
         }
         else
            error=ERROR_OBJECT_IN_USE;
      }
   }
   else
      error=IoErr();

   /* Return result */

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



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

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

   /* Get object */

   error=0;
   object=GetObject(handler,lock,name,NULL);
   if(object==NULL)
      error=IoErr();

   /* Check comment isn't too long */

   if(StrSize(comment)>sizeof(((struct FileInfoBlock *)NULL)->fib_Comment))
      error=ERROR_COMMENT_TOO_BIG;

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

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

   /* Check volume isn't write-protected */

   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   /* Store new comment */

   if(error==0)
   {
      block_diff=SetString(&object->comment,comment);
      if(block_diff==-1)
         error=IoErr();
      else
      {
         object->block_count+=block_diff;
         handler->block_count+=block_diff;
      }
   }

   /* Notify interested parties */

   if(error==0)
   {
      NotifyAll(handler,object,FALSE);
   }

   /* Return result */

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



/****i* ram.handler/CmdRenameObject ****************************************
*
*   NAME
*	CmdRenameObject --
*
*   SYNOPSIS
*	success = CmdRenameObject(handler,old_lock,old_name
*	    new_lock,new_name)
*
*	BOOL CmdRenameObject(struct Handler *,struct Lock *,STRPTR,
*	    struct Lock *,STRPTR);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdRenameObject(struct Handler *handler,struct Lock *old_lock,
   STRPTR old_name,struct Lock *new_lock,STRPTR new_name)
{
   struct Object *parent,*object,*duplicate,*p;
   LONG error;

   /* Get object to be moved */

   error=0;
   object=GetObject(handler,old_lock,old_name,NULL);
   if(object==NULL)
      error=IoErr();

   /* Get destination directory and check if a different object already has
      the target name */

   duplicate=GetObject(handler,new_lock,new_name,&parent);
   if((duplicate!=NULL)&&(duplicate!=object))
      error=ERROR_OBJECT_EXISTS;
   if(parent==NULL)
      error=IoErr();

   /* Check for a circular rename */

   for(p=parent;p!=NULL;p=p->parent)
   {
      if(p==object)
         error=ERROR_OBJECT_NOT_FOUND;
   }

   /* Check volume isn't write-protected */

   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   /* Give the object its new name */

   if(error==0)
   {
      if(!SetName(handler,object,FilePart(new_name)))
         error=IoErr();
   }

   if(error==0)
   {
      /* Remove object from its old parent and place it in its new parent */

      Remove((struct Node *)object);
      AddTail((struct List *)&parent->elements,(struct Node *)object);

      /* Update notifications */

      if(object!=duplicate)
      {
         NotifyAll(handler,object,FALSE);
         MatchNotifyRequests(handler);
         NotifyAll(handler,object,FALSE);
      }
   }

   /* Return success indicator */

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



/****i* ram.handler/CmdRenameDisk ******************************************
*
*   NAME
*	CmdRenameDisk --
*
*   SYNOPSIS
*	success = CmdRenameDisk(handler,new_name)
*
*	BOOL CmdRenameDisk(struct Handler *,STRPTR);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdRenameDisk(struct Handler *handler,STRPTR new_name)
{
   LONG error=0;

   /* Check volume isn't write-protected */

   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   /* Rename volume's DOS entry and root directory */

   if(error==0)
      if(!SetName(handler,handler->root_dir,new_name))
         error=IoErr();

   if(error==0)
      if(!MyRenameDosEntry(handler->volume,new_name))
         error=IoErr();

   /* Return success indicator */

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



/****i* ram.handler/CmdSetDate *********************************************
*
*   NAME
*	CmdSetDate --
*
*   SYNOPSIS
*	success = CmdSetDate(handler,lock,name,
*	    date)
*
*	BOOL CmdSetDate(struct Handler *,struct Lock *,STRPTR,
*	    struct DateStamp *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdSetDate(struct Handler *handler,struct Lock *lock,STRPTR name,
   struct DateStamp *date)
{
   struct Object *object;
   LONG error;

   /* Check volume isn't write-protected */

   error=0;
   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   /* Get object and set its new date */

   if(error==0)
   {
      object=GetObject(handler,lock,name,NULL);
      if(object!=NULL)
         CopyMem(date,&object->date,sizeof(struct DateStamp));
      else
         error=IoErr();
   }

   /* Notify interested parties */

   if(error==0)
   {
      NotifyAll(handler,object,TRUE);
   }

   /* Return result */

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



/****i* ram.handler/CmdDeleteObject ****************************************
*
*   NAME
*	CmdDeleteObject --
*
*   SYNOPSIS
*	success = CmdDeleteObject(handler,lock,name)
*
*	BOOL CmdDeleteObject(struct Handler *,struct Lock *,STRPTR);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdDeleteObject(struct Handler *handler,struct Lock *lock,
   STRPTR name)
{
   LONG error;
   struct Object *object;

   /* Find object and check it can be deleted */

   error=0;
   object=GetObject(handler,lock,name,NULL);

   if(object==NULL)
      error=IoErr();

   if(handler->locked)
      error=ERROR_DISK_WRITE_PROTECTED;

   if(error==0)
   {
      if(GetRealObject(object)->protection&FIBF_DELETE)
         error=ERROR_DELETE_PROTECTED;
   }

   /* Attempt to delete object and notify anyone who's interested */

   if(error==0)
   {
      if(DeleteObject(handler,object))
      {
         NotifyAll(handler,object,FALSE);
         MatchNotifyRequests(handler);
      }
      else
         error=IoErr();
   }

   /* Return result */

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



/****i* ram.handler/CmdCurrentVolume ***************************************
*
*   NAME
*	CmdCurrentVolume --
*
*   SYNOPSIS
*	volume = CmdCurrentVolume(handler)
*
*	struct DosList *CmdCurrentVolume(struct Handler *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

struct DosList *CmdCurrentVolume(struct Handler *handler)
{
   SetIoErr(0);
   return handler->volume;
}



/****i* ram.handler/CmdChangeMode ******************************************
*
*   NAME
*	CmdChangeMode --
*
*   SYNOPSIS
*	success = CmdChangeMode(type,thing,new_mode)
*
*	BOOL CmdChangeMode(ULONG,APTR,ULONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdChangeMode(ULONG type,APTR thing,ULONG new_mode)
{
   struct Lock *lock;
   struct Opening *opening;
   ULONG old_mode;
   LONG error=0;

   /* Get the lock */

   if(type==CHANGE_FH)
   {
      opening=(APTR)((struct FileHandle *)thing)->fh_Arg1;
      lock=opening->file->lock;
   }
   else
      lock=thing;

   /* Change mode if possible */

   old_mode=((struct FileLock *)lock)->fl_Access;

   if((new_mode==ACCESS_WRITE)&&(lock->lock_count>1))
   {
      error=ERROR_OBJECT_IN_USE;
   }
   else
   {
      ((struct FileLock *)lock)->fl_Access=new_mode;
   }

   /* Set error code and return result */

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



/****i* ram.handler/CmdMakeLink ********************************************
*
*   NAME
*	CmdMakeLink --
*
*   SYNOPSIS
*	success = CmdMakeLink(handler,lock,name,reference,
*	    link_type)
*
*	BOOL CmdMakeLink(struct Handler *,struct Lock *,STRPTR,APTR,
*	    LONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdMakeLink(struct Handler *handler,struct Lock *lock,STRPTR name,
   APTR reference,LONG link_type)
{
   struct Object *link,*parent,*target,*master_link;
   LONG error,object_type;
   PINT block_diff;
   struct MinNode *node;

   /* Find parent directory and possible name clash */

   error=0;
   link=GetObject(handler,lock,name,&parent);
   if(link!=NULL)
      error=ERROR_OBJECT_EXISTS;

   /* Determine link type */

   if(link_type==LINK_HARD)
   {
      target=(APTR)((struct FileLock *)reference)->fl_Key;
      if(((struct Node *)target)->ln_Pri==ST_FILE)
         object_type=ST_LINKFILE;
      else
         object_type=ST_LINKDIR;
   }
   else
   {
      object_type=ST_SOFTLINK;
      error=ERROR_NOT_IMPLEMENTED;
   }

   /* Check volume isn't write-protected */

   if(error==0)
   {
      if(handler->locked)
         error=ERROR_DISK_WRITE_PROTECTED;
   }

   /* Create a new link */

   if(error==0)
   {
      if(parent!=NULL)
      {
         link=CreateObject(handler,name,object_type,parent);
         if(link==NULL)
            error=IoErr();
      }
      else
         error=ERROR_OBJECT_NOT_FOUND;
   }

   /* Store link target */

   if(error==0)
   {
      if(link_type==LINK_HARD)
      {
         node=target->hard_link.mln_Succ;
         if(node==NULL)
         {
            master_link=link;
            AddTail((APTR)&master_link->elements,(APTR)&target->hard_link);
         }
         else
            master_link=HARDLINK(node);
         AddTail((APTR)&master_link->elements,(APTR)&link->hard_link);
      }
      else
      {
         block_diff=SetString((APTR)&link->lock,reference);
         if(block_diff==-1)
            error=IoErr();
         else
         {
            link->block_count+=block_diff;
            handler->block_count+=block_diff;
         }
      }
   }

   /* Update notifications */

   if(error==0)
   {
      MatchNotifyRequests(handler);
      NotifyAll(handler,link,FALSE);
   }

   /* Return success indicator */

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



/****i* ram.handler/CmdWriteProtect ****************************************
*
*   NAME
*	CmdWriteProtect --
*
*   SYNOPSIS
*	success = CmdWriteProtect(handler,on,key)
*
*	BOOL CmdWriteProtect(struct Handler *,BOOL,ULONG);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdWriteProtect(struct Handler *handler,BOOL on,ULONG key)
{
   LONG error;
   struct Object *root_dir;

   error=0;
   root_dir=handler->root_dir;

   if(on)
   {
      if(!handler->locked)
         root_dir->length=key;
      else
         error=ERROR_DISK_WRITE_PROTECTED;
   }
   else
   {
      if((root_dir->length!=0)&&(root_dir->length!=key))
         error=ERROR_INVALID_COMPONENT_NAME;
      else
         root_dir->length=0;
   }

   if(error==0)
      handler->locked=on;

   /* Return success indicator */

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



/****i* ram.handler/CmdFlush ***********************************************
*
*   NAME
*	CmdFlush --
*
*   SYNOPSIS
*	success = CmdFlush()
*
*	BOOL CmdFlush();
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdFlush()
{
   return TRUE;
}



/****i* ram.handler/CmdAddNotify *******************************************
*
*   NAME
*	CmdAddNotify --
*
*   SYNOPSIS
*	success = CmdAddNotify(handler,request)
*
*	BOOL CmdAddNotify(struct Handler *,struct NotifyRequest *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdAddNotify(struct Handler *handler,struct NotifyRequest *request)
{
   LONG error;
   struct Notification *notification;
   struct Object *object;

   error=0;
   notification=AllocMem(sizeof(struct Notification),MEMF_CLEAR);
   if(notification==NULL)
      error=IoErr();

   if(error==0)
   {
      AddTail((APTR)&handler->notifications,(APTR)notification);

      notification->request=request;
      request->nr_Flags&=~NRF_MAGIC;

      object=GetObject(handler,NULL,request->nr_FullName,NULL);
      notification->object=object;

      if((object!=NULL)&&(request->nr_Flags&NRF_NOTIFY_INITIAL))
         Notify(handler,notification);
   }

   /* Return success indicator */

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



/****i* ram.handler/CmdRemoveNotify ****************************************
*
*   NAME
*	CmdRemoveNotify --
*
*   SYNOPSIS
*	success = CmdRemoveNotify(handler,request)
*
*	BOOL CmdRemoveNotify(struct Handler *,struct NotifyRequest *);
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
****************************************************************************
*
*/

BOOL CmdRemoveNotify(struct Handler *handler,struct NotifyRequest *request)
{
   struct Notification *notification;

   notification=FindNotification(handler,request);
   if(notification!=NULL)
   {
      Remove((APTR)notification);
      FreeMem(notification,sizeof(struct Notification));

   }

   return TRUE;
}



