/*
 *  DOSDEVICE.C 	V1.10	2 November 1987
 *
 *  EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C   PUBLIC DOMAIN.
 *
 *  By Matthew Dillon.
 *
 *  Debugging routines are disabled by simply attempting to open the
 *  file "debugoff", turned on again with "debugon".  No prefix may be
 *  attached to these names (you must be CD'd to TEST:).
 *
 *  See Documentation for a detailed discussion.
 *
 *  BUGS:
 *	Currently the only known bug is with the implementation of the
 *	RAM disk itself.  Specifically, if filehandle A is at the end of
 *	the file, and somebody appends to the file with another filehandle,
 *	B, filehandle A will get confused as to it's current position in
 *	the file.
 *
 *	I am probably not updating all the right timestamps.  This is
 *	easy to fix... All you have to do is fool with the floppy and
 *	see which datestamps get updated for certain operations.
 */

#include "dos.h"

/*
 *  Since this code might be called several times in a row without being
 *  unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
 *  for any global/static assignments that might be changed by running the
 *  code.
 */

PORT	*DosPort;   /*  Our DOS port... this is slick... */
PROC	*DosProc;   /*	Our Process				    */
DEVNODE *DosNode;   /*	Our DOS node.. created by DOS for us	    */
DEVLIST *DevList;   /*	Device List structure for our volume node   */

void	*SysBase;   /*	EXEC library base			*/
DOSLIB	*DOSBase;   /*	DOS library base for debug process	*/
RAMFILE RFRoot;     /*	Directory/File structure    (root node) */
LIST	FHBase;     /*	Open Files				*/
LIST	LCBase;     /*	Open Locks				*/

long	TotalBytes; /*	total bytes of data in filesystem	*/


		    /*	DEBUGGING			*/
PORT *Dbport;	    /*	owned by the debug process	*/
PORT *Dback;	    /*	owned by the DOS device driver	*/
MSG DummyMsg;	    /*	Dummy message that debug proc can use	*/

RAMFILE xpath;	    /*  This is used in case of off device path */
char *buf1;	    /*  This holds the translated path names */
char *volname;	    /*  This is my volume name 		*/

RAMFILE * checkoutpath();
RAMFILE * vsearchpath();
void *DeviceProc();

/*
 *  Don't call the entry point main().  This way, if you make a mistake
 *  with the compile options you'll get a link error.
 */

void
noname()
{
    register PACKET *packet;
    register short   error;
    register ubyte *ptr;
    MSG     *msg;
    ubyte   notdone;
    ubyte   buf[256];
    void    *tmp;

    /*
     *	Initialize all global variables.  SysBase MUST be initialized before
     *	we can make Exec calls.  AbsExecBase is a library symbol
     *	referencing absolute memory location 4.  The DOS library is openned
     *	for the debug process only.
     */

    Dbport = Dback = NULL;
    TotalBytes = 0;
    SysBase = AbsExecBase;
    DOSBase = OpenLibrary("dos.library",0);
    DosProc = FindTask(NULL);
    DosPort  = (PORT *)AllocMem(sizeof(PORT), MEMF_CLEAR | MEMF_PUBLIC);
    DosPort->mp_Node.ln_Type = NT_MSGPORT;
    DosPort->mp_Node.ln_Name = "Dos Port";
    DosPort->mp_Flags   = PA_SIGNAL;
    DosPort->mp_SigBit  = AllocSignal(-1);
    DosPort->mp_SigTask = FindTask(NULL);
    NewList(&DosPort->mp_MsgList);
    buf1  = AllocMem(512, MEMF_PUBLIC);
    {
	WaitPort(&DosProc->pr_MsgPort); 	/*  Get Startup Packet	*/
	msg = GetMsg(&DosProc->pr_MsgPort);
	packet = (PACKET *)msg->mn_Node.ln_Name;

	/*
	 *  Loading DosNode->dn_Task causes DOS *NOT* to startup a new
	 *  instance of the device driver for every reference.	E.G. if
	 *  you were writing a CON device you would want this field to
	 *  be NULL.
	 */

	if (DOSBase) {
	    DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
	    register DEVLIST *dl = dosalloc(sizeof(DEVLIST));

	    DosNode = BTOC(PArg3);

	    /*
	     *	Create Volume node and add to the device list.	This will
	     *	cause the WORKBENCH to recognize us as a disk.	If we don't
	     *	create a Volume node, Wb will not recognize us.  However,
	     *	we are a RAM: disk, Volume node or not.
	     */

	    volname = AllocMem(32,MEMF_PUBLIC);
	    volname[0] = 11;
	    strcpy(volname+1,"Path Server");

	    DevList = dl;
	    dl->dl_Type = DLT_VOLUME;
	    dl->dl_Task = DosPort;
	    dl->dl_DiskType = ID_DOS_DISK;
	    dl->dl_Name = CTOB(volname); /* DosNode->dn_Name*/;
	    dl->dl_Next = di->di_DevInfo;
	    di->di_DevInfo = (long)CTOB(dl);

	    /*
	     *	Set dn_Task field which tells DOS not to startup a new
	     *	process on every reference.
	     */

	    DosNode->dn_Task = DosPort;
	    PRes1 = DOS_TRUE;
	    PRes2 = 0;
	} else {			    /*	couldn't open dos.library  */
	    PRes1 = DOS_FALSE;
	    returnpacket(packet);
	    return;			    /*	exit process	   */
	}
	returnpacket(packet);
    }

    /*
     *	Initialize debugging code
     */

/*DB*/  dbinit();

    /*	Initialize  RAM disk	*/

    {
	ubyte *ptr = BTOC(DosNode->dn_Name);
	short len = *ptr;

	NewList(&FHBase);			    /*	more globals	*/
	NewList(&LCBase);
	bzero(&RFRoot,sizeof(RFRoot));
	RFRoot.type = FILE_DIR; 		    /*	root directory	*/
	DateStamp(&RFRoot.date);		    /*	datestamp	*/
	NewList(&RFRoot.list);			    /*	sub dirs	*/
	RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /*	Root NAME	*/
	bmov(ptr+1,RFRoot.name,len);
	RFRoot.name[len] = 0;
/*DB*/	dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
    }

    /*
     *	Here begins the endless loop, waiting for requests over our
     *	message port and executing them.  Since requests are sent over
     *	our message port, this precludes being able to call DOS functions
     *	ourselves (that is why the debugging routines are a separate process)
     */

     WaitPort(&DosProc->pr_MsgPort); 	   /*  Get Startup Packet	*/
     msg = GetMsg(&DosProc->pr_MsgPort);
     notdone = 1;
     goto entry;

top:
    for (notdone = 1; notdone;) {
	WaitPort(DosPort);
	while (msg = GetMsg(DosPort)) {
entry:
	    packet = (PACKET *)msg->mn_Node.ln_Name;
	    PRes1 = DOS_TRUE;
	    PRes2 = 0;
	    error = 0;
/*DB*/	    dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
/*DB*/			PType, PArg1, PArg2, PArg3, typetostr(PType) );

	    switch(PType) {
	    case ACTION_DIE:	    /*	attempt to die?  */
		notdone = 0;	    /*	try to die	 */
		break;
	    case ACTION_OPENRW:     /*	FileHandle,Lock,Name	   Bool    */
	    case ACTION_OPENOLD:    /*	FileHandle,Lock,Name	   Bool    */
	    case ACTION_OPENNEW:    /*	FileHandle,Lock,Name	   Bool    */
		{
		    register RAMFILE *ramfile;
		    RAMFILE *parentdir = getlockfile(PArg2);
		    char    *ptr;

		    btos(PArg3,buf);
/*DB*/		    dbprintf("'%s' ", buf);
		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
		    	if (ramfile == &xpath) {
			    FH *p;
			    if (p = Open(buf1,PType)) {
			    	bmov(BTOC(p),BTOC(PArg1),sizeof(FH));
				FreeMem(BTOC(p),sizeof(FH));
			    }
			    else
			    	error = IoErr();
			    goto openbreak;
			}
			if (ramfile->type == FILE_DIR) {
			    error = ERROR_OBJECT_WRONG_TYPE;
			    goto openbreak;
			}
			if (ramfile->locks < 0) {
			    error = ERROR_OBJECT_IN_USE;
			    goto openbreak;
			}
			if (PType == ACTION_OPENOLD) {
			    ++ramfile->locks;
			} else {
			    if (ramfile->locks > 0) {
				error = ERROR_OBJECT_IN_USE;
			    } else {
				if (PType == ACTION_OPENNEW) {
				    freedata(ramfile);
				    ramfile->protection = 0;
				}
				--ramfile->locks;
			    }
			}
		    } else {
			if (!parentdir) {
			    error = ERROR_INVALID_COMPONENT_NAME;
			    goto openbreak;
			}
			if (PType == ACTION_OPENNEW) {
			   ramfile = createramfile(parentdir,FILE_FILE,ptr);
			   --ramfile->locks;
			} else {
			    error = ERROR_OBJECT_NOT_FOUND;
			}
		    }
		    if (!error) {
			register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR);
			((FH *)BTOC(PArg1))->fh_Arg1 = (long)mfh;
			mfh->file = ramfile;
			mfh->fentry = GetHead(&ramfile->list);
			AddHead(&FHBase,mfh);
		    }
		}
	      openbreak:
		if (!GetHead(&FHBase) && !GetHead(&LCBase))
		    notdone = 0;
		break;
	    case ACTION_READ:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
		{
		    register MYFH   *mfh = (MYFH *)PArg1;
		    register FENTRY *fen = mfh->fentry;
		    register ubyte  *ptr = (ubyte *)PArg2;
		    register long   left = PArg3;
		    register long   scr;

		    while (left && fen) {
			scr = fen->bytes - mfh->offset;
			if (left < scr) {
			    bmov(fen->buf + mfh->offset, ptr, left);
			    mfh->offset += left;
			    left = 0;
			} else {
			    bmov(fen->buf + mfh->offset, ptr, scr);
			    left -= scr;
			    ptr += scr;
			    mfh->base += fen->bytes;
			    mfh->offset = 0;
			    fen = NextNode(fen);
			}
		    }
		    mfh->fentry = fen;
		    PRes1 = PArg3 - left;
		}
		break;
	    case ACTION_WRITE:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
		{
		    register MYFH   *mfh = (MYFH *)PArg1;
		    register FENTRY *fen = (FENTRY *)mfh->fentry;
		    ubyte  *ptr = (ubyte *)PArg2;
		    long   left = PArg3;
		    long   scr;

		    /*
		     *	Doesn't work right if multiple readers/appenders.
		     */

		    while (left) {
			if (fen) {
/*DB*/			    dbprintf("FEN: %ld left: %ld\n",fen->bytes,left);
			    scr = fen->bytes - mfh->offset;
			    if (left < scr) {
				if (fen->bytes < mfh->offset + left) {
/*DB*/				    dbprintf("PANIC! AWR0\n");
				}
				else
				    bmov(ptr, fen->buf + mfh->offset, left);
				mfh->offset += left;
				left = 0;
			    } else {
				if (fen->bytes < mfh->offset + scr) {
/*DB*/				    dbprintf("PANIC! AWR1\n");
				}
				else
				    bmov(ptr, fen->buf + mfh->offset, scr);
				ptr += scr;
				left -= scr;
				mfh->base += fen->bytes;
				mfh->offset = 0;
				fen = NextNode(fen);
			    }
			} else {
			    fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
			    if (fen->buf = AllocMem(left, MEMF_PUBLIC)) {
				fen->bytes = left;
				mfh->file->bytes += left;
				mfh->base  += left;
				mfh->offset = 0;
				TotalBytes += left;
				AddTail(&mfh->file->list, fen);
/*DB*/				dbprintf("NEWFEN: (%ld)\n", fen->bytes);
				bmov(ptr, fen->buf, left);
				left = 0;
			    } else {
				FreeMem(fen, sizeof(FENTRY));
/*DB*/				dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left);
				mfh->offset = 0;
				break;
			    }
			    fen = NULL;     /*	cause append	*/
			}
		    }
		    PRes1 = PArg3 - left;
		    mfh->fentry = fen;
		}
		break;
	    case ACTION_CLOSE:	    /*	 FHArg1 		    Bool:TRUE  */
		{
		    register MYFH   *mfh = (MYFH *)PArg1;
		    register RAMFILE *file = mfh->file;

		    Remove(mfh);
		    FreeMem(mfh,sizeof(*mfh));
		    if (--file->locks < 0)
			file->locks = 0;
		}
		if (!GetHead(&FHBase) && !GetHead(&LCBase))
		    notdone = 0;
		break;
	    case ACTION_SEEK:	    /*	 FHArg1,Position,Mode	    OldPosition*/
		{
		    register MYFH *mfh = (MYFH *)PArg1;
		    register FENTRY *fen;
		    register long absseek;

		    PRes1 = mfh->base + mfh->offset;
		    absseek = PArg2;
		    if (PArg3 == 0)
			absseek += mfh->base + mfh->offset;
		    if (PArg3 == 1)
			absseek = mfh->file->bytes + absseek;
		    if (absseek < 0 || absseek > mfh->file->bytes) {
			error = ERROR_SEEK_ERROR;
			break;
		    }
		    mfh->base = mfh->offset = 0;

		    /*
		     *	Stupid way to do it but....
		     */

		    for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) {
			if (mfh->base + fen->bytes > absseek) {
			    mfh->offset = absseek - mfh->base;
			    break;
			}
			mfh->base += fen->bytes;
		    }
		    mfh->fentry = fen;
		}
		break;
	    /*
	     *	This implementation sucks.  The right way to do it is with
	     *	a hash table.  The directory must be searched for the file
	     *	name, then the next entry retrieved.  If the next entry is
	     *	NULL there are no more entries.  If the filename could not
	     *	be found we return the first entry, if any.
	     *
	     *	You can't simply keep a pointer around to the next node
	     *	because it can be moved or removed at any time.
	     */

	    case ACTION_EXAMINE_NEXT: /*   Lock,Fib		      Bool	 */
		{
		    register FIB *fib = BTOC(PArg2);
		    register RAMFILE *dir = getlockfile(PArg1);
		    register RAMFILE *file;

		    if (dir->type == FILE_FILE) {
			error = ERROR_OBJECT_WRONG_TYPE;
			break;
		    }
		    file = GetHead(&dir->list);
		    if (fib->fib_DiskKey) {
			register int len = *(ubyte *)fib->fib_FileName;
			for (; file; file = NextNode(file)) {
			    if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len))
				break;
			}
			if (file)
			    file = NextNode(file);
			else
			    file = GetHead(&dir->list);
		    }
		    fib->fib_DiskKey = 1;
		    error = -1;
		    if (!(tmp=file)) {
			error = ERROR_NO_MORE_ENTRIES;
			break;
		    }
		}
		/*  fall through    */
	    case ACTION_EXAMINE_OBJECT: /*   Lock,Fib			Bool	   */
		{
		    register FIB *fib;
		    register RAMFILE *file;
		    register RAMFILE *dummy;

		    fib = BTOC(PArg2);
		    if (error) {
			file = tmp;	/*  fall through from above */
		    } else {
			file = getlockfile(PArg1);
			fib->fib_DiskKey = 0;
		    }
		    error = 0;
		    fib->fib_DirEntryType = file->type;
		    strcpy(fib->fib_FileName+1, file->name);
		    fib->fib_FileName[0] = strlen(file->name);
		    fib->fib_Protection = file->protection;
		    fib->fib_EntryType = NULL;
		    fib->fib_Size = file->bytes;
		    fib->fib_NumBlocks = file->bytes >> 9;
		    fib->fib_Date = file->date;
		    if (file->comment) {
			strcpy(fib->fib_Comment+1, file->comment);
			fib->fib_Comment[0] = strlen(file->comment);
		    } else {
			fib->fib_Comment[0] = 0;
		    }
		}
		break;
	    case ACTION_INFO:	    /*	Lock, InfoData	  Bool:TRUE    */
		tmp = BTOC(PArg2);
		error = -1;
		/*  fall through    */
	    case ACTION_DISK_INFO:  /*	InfoData	  Bool:TRUE    */
		{
		    register INFODATA *id;

		    /*
		     *	Note:	id_NumBlocks is never 0, but only to get
		     *	around a bug I found in my shell (where I divide
		     *	by id_NumBlocks).  Other programs probably break
		     *	as well.
		     */

		    (error) ? (id = tmp) : (id = BTOC(PArg1));
		    error = 0;
		    bzero(id, sizeof(*id));
		    id->id_DiskState = ID_VALIDATED;
		    id->id_NumBlocks	 = (TotalBytes >> 9) + 1;
		    id->id_NumBlocksUsed = (TotalBytes >> 9) + 1;
		    id->id_BytesPerBlock = 512;
		    id->id_DiskType = ID_DOS_DISK;
		    id->id_VolumeNode = (long)CTOB(DosNode);
		    id->id_InUse = (long)GetHead(&LCBase);
		}
		break;
	    case ACTION_PARENT:     /*	 Lock			  ParentLock */
		{
		    register RAMFILE *file = getlockfile(PArg1);
		    /*
		    if (file->type == FILE_FILE) {
			error = ERROR_OBJECT_NOT_FOUND;
			break;
		    }
		    */
		    if (file->locks < 0) {
			error = ERROR_OBJECT_IN_USE;
			break;
		    }
		    if (file->parent)
			PRes1 = (long)CTOB(ramlock(file->parent, ACCESS_READ));
		    else
			error = ERROR_OBJECT_NOT_FOUND;
		}
		break;
	    case ACTION_DELETE_OBJECT: /*Lock,Name  Bool       */
		{
		    RAMFILE *parentdir = getlockfile(PArg1);
		    RAMFILE *ramfile;

		    btos(PArg2, buf);
		    if (ramfile = searchpath(&parentdir,buf,NULL)) {
		    	if (ramfile == &xpath) {
				if (!DeleteFile(buf1)) error = IoErr();
				break;
			}
			if (ramfile->locks || ramfile == &RFRoot) {
			    error = ERROR_OBJECT_IN_USE;
			    break;
			}
			if (ramfile->type == FILE_DIR) {
			    if (GetHead(&ramfile->list))
				error = ERROR_DIRECTORY_NOT_EMPTY;
			} else {
			    freedata(ramfile);
			}
			if (!error) {
			    freeramfile(ramfile);
			    DateStamp(&parentdir->date);
			}
		    } else {
			if (!parentdir)
			    error = ERROR_INVALID_COMPONENT_NAME;
			else
			    error = ERROR_OBJECT_NOT_FOUND;
		    }
		}
		if (!GetHead(&FHBase) && !GetHead(&LCBase))
		    notdone = 0;
		break;
	    case ACTION_CREATE_DIR: /*	 Lock,Name 	 Lock       */
		{
		    RAMFILE *parentdir = getlockfile(PArg1);
		    RAMFILE *ramfile;
		    char *ptr;

		    btos(PArg2, buf);
		    if (ramfile = vsearchpath(&parentdir,buf,&ptr)) {
			error = ERROR_OBJECT_EXISTS;
			break;
		    }
		    if (!parentdir) {
			error = ERROR_INVALID_COMPONENT_NAME;
			break;
		    }
		    ramfile = createramfile(parentdir, FILE_DIR, ptr);
		    PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE));
		}
		break;
	    case ACTION_LOCATE_OBJECT:	/*   Lock,Name,Mode	Lock	   */
		{
		    RAMFILE *parentdir = getlockfile(PArg1);
		    RAMFILE *ramfile;

		    btos(PArg2, buf);
/*DB*/		    dbprintf("'%s' %ld ", buf, PArg3);
		    if (ramfile = searchpath(&parentdir,buf,NULL)) {
		    	if (ramfile == &xpath) {
			   PRes1 = Lock(buf1,PArg3);
			   if (!PRes1) PRes2 = IoErr();
			   break;
			}
			if (ramfile->locks < 0 || (ramfile->locks && PArg3 == ACCESS_WRITE)) {
			    error = ERROR_OBJECT_IN_USE;
			    break;
			}
			PRes1 = (long)CTOB(ramlock(ramfile, PArg3));
		    } else {
			if (!parentdir)
			    error = ERROR_INVALID_COMPONENT_NAME;
			else
			    error = ERROR_OBJECT_NOT_FOUND;
		    }
		}
		break;
	    case ACTION_COPY_DIR:   /*	 Lock,			    Lock       */
		{
		    register RAMFILE *ramfile = getlockfile(PArg1);
		    if (ramfile->locks < 0)
			error = ERROR_OBJECT_IN_USE;
		    else
			PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_READ));
		}
		break;
	    case ACTION_FREE_LOCK:  /*	 Lock,			    Bool       */
		if (PArg1);
		    ramunlock(BTOC(PArg1));
		if (!GetHead(&FHBase) && !GetHead(&LCBase))
		    notdone = 0;
		break;
	    case ACTION_SET_PROTECT:/*	 -,Lock,Name,Mask	   Bool       */
		{
		    register RAMFILE *ramfile;
		    RAMFILE *parentdir = getlockfile(PArg2);
		    char *ptr;

		    btos(PArg3, buf);
		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
		    	if (ramfile == &xpath) {
			    if (!SetProtection(buf1,PArg4))
			    		error = IoErr();
			    break;
			}
			ramfile->protection = PArg4;
		    } else {
			if (parentdir)
			    error = ERROR_OBJECT_NOT_FOUND;
			else
			    error = ERROR_INVALID_COMPONENT_NAME;
		    }
		}
		break;
	    case ACTION_SET_COMMENT:/*	 -,Lock,Name,Comment	   Bool       */
		{
		    register RAMFILE *ramfile;
		    RAMFILE *parentdir = getlockfile(PArg2);
		    char *ptr;

		    btos(PArg3, buf);
		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
		    	if (ramfile == &xpath) {
				btos(PArg4,buf);
				if (!SetComment(buf1,buf))
					error = IoErr();
				break;
			}
			btos(PArg4, buf);
			if (ramfile->comment)
			    FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
			ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC);
			strcpy(ramfile->comment, buf);
		    } else {
			if (parentdir)
			    error = ERROR_OBJECT_NOT_FOUND;
			else
			    error = ERROR_INVALID_COMPONENT_NAME;
		    }
		}
		break;
	    case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName    Bool       */
		{
		    register RAMFILE *file1;
		    RAMFILE *sourcedir = getlockfile(PArg1);
		    RAMFILE *destdir   = getlockfile(PArg3);
		    char *ptr;

		    btos(PArg2,buf);
/*DB*/		    dbprintf("\nRENAME '%s' (%ld)  ", buf, strlen(buf));
		    if (file1 = vsearchpath(&sourcedir,buf,NULL)) {
			btos(PArg4,buf);
/*DB*/			dbprintf("TO '%s' (%ld)", buf, strlen(buf));
			if (vsearchpath(&destdir,buf,&ptr)) {
			    error = ERROR_OBJECT_EXISTS;
			} else {
			    if (destdir) {
				if (file1 == destdir) { 
				    /* moving inside self */
				    error = ERROR_OBJECT_IN_USE;
				    break;
				}
/*DB*/				dbprintf("REN '%s' %ld", ptr, strlen(ptr));
				DateStamp(&sourcedir->date);
				DateStamp(&destdir->date);
				Remove(file1);
				file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC);
				file1->parent = destdir;
				strcpy(file1->name, ptr);
				AddHead(&destdir->list, file1);
			    } else {
				error = ERROR_INVALID_COMPONENT_NAME;
			    }
			}
		    } else {
			if (sourcedir)
			    error = ERROR_OBJECT_NOT_FOUND;
			else
			    error = ERROR_INVALID_COMPONENT_NAME;
		    }
		}
		break;
	    /*
	     *	A few other packet types which we do not support
	     */
	    case ACTION_INHIBIT:    /*	 Bool		Bool       */
		/*  Return success for the hell of it	*/
		break;
	    case ACTION_RENAME_DISK:/*	 BSTR:NewName	Bool       */
	    case ACTION_MORECACHE:  /*	 #BufsToAdd	Bool       */
	    case ACTION_WAIT_CHAR:  /*	 Timeout, ticks Bool       */
	    case ACTION_FLUSH:	    /*	 writeout bufs, disk motor off	  */
	    case ACTION_RAWMODE:    /*	 Bool(-1:RAW 0:CON)    OldState   */
	    default:
		error = ERROR_ACTION_NOT_KNOWN;
		break;
	    }
	    if (packet) {
		if (error) {
/*DB*/		    dbprintf("ERR=%ld\n", error);
		    PRes1 = DOS_FALSE;
		    PRes2 = error;
		} else {
/*DB*/		    dbprintf("RES=%06lx\n", PRes1);
		}
		returnpacket(packet);
	    }
	    else {
/*DB*/		 dbprintf("NOREP\n");
	    }
	}
    }
/*DB*/  dbprintf("Can we remove ourselves? ");
/*DB*/  Delay(50);	    /*	I wanna even see the debug message! */
    Forbid();
    if (packetsqueued() || GetHead(&FHBase) || GetHead(&LCBase)
      || GetHead(&RFRoot.list)) {
	Permit();
/*DB*/	dbprintf(" ..  not yet!\n");
	goto top;		/*  sorry... can't exit     */
    }

    /*
     *	Causes a new process to be created on next reference
     */

    DosNode->dn_Task = FALSE;

    /*
     *	Remove Volume entry.  Since DOS uses singly linked lists, we
     *	must (ugg) search it manually to find the link before our
     *	Volume entry.
     */

    {
	DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
	register DEVLIST *dl;
	register void *dlp;

	dlp = &di->di_DevInfo;
	for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next))
	    dlp = &dl->dl_Next;
	if (dl == DevList) {
	    *(BPTR *)dlp = dl->dl_Next;
	    dosfree(dl);
	} else {
/*DB*/	    dbprintf("****PANIC: Unable to find volume node\n");
	}
    }

    /*
     *	Remove debug process, closedown, fall of the end of the world
     *	(which is how you kill yourself if a PROCESS.  A TASK would have
     *	had to RemTask(NULL) itself).
     */

/*DB*/  dbuninit();
        CloseLibrary(DOSBase);
}


/*DB*/ static FH *debugfh;
/*DB*/
/*DB*/ dbinit()
/*DB*/ {
/*DB*/     debugfh = Open("con:0/0/640/150/debugwindow", 1006);
/*DB*/
/*DB*/ }
/*DB*/
/*DB*/ dbuninit()
/*DB*/ {
/*DB*/    Close(debugfh);
/*DB*/ }
/*DB*/
/*DB*/ dbprintf(a,b,c,d,e,f,g,h,i,j)
/*DB*/ {
/*DB*/    static char buf[256];
/*DB*/
/*DB*/	  sprintf(buf,a,b,c,d,e,f,g,h,i,j);
/*DB*/	  Write(debugfh,buf,strlen(buf));
/*DB*/
/*DB*/ }
