/*
 *  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*/ }
