From: dillon@CORY.BERKELEY.EDU (Matt Dillon)
Newsgroups: comp.sys.amiga
Subject: Revised Example Dos Device Driver
Date: 22 Oct 87 23:25:09 GMT
Sender: kupfer@ucbvax.BERKELEY.EDU
Lines: 1652
 
 
 
        Thanks to Steve Beats and Andy Finkel, I've gotten ACTION_INFO
working properly.  Here's the update... plus the docs which somehow got
left out of the original distribution.
 
                                -Matt
 
[[ Mods note:  I did split original file into two parts, but still
               other part is  nearly 1100 lines long! (device.c)
   Testing:    So far none.  Thou source seems to be ok, albeit
               tabs are lost with these IBM system.
                           /Matti Aarnio  8-Jul-88               ]]
 
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#       device.c
# This archive created: Thu Oct 22 16:16:12 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'device.c'" '(27017 characters)'
if test -f 'device.c'
then
        echo shar: "will not over-write existing file 'device.c'"
else
cat << \!Funky!Stuff! > 'device.c'
/*
 *  DOSDEVICE.C
 *
 *  EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C   PUBLIC DOMAIN.
 *
 *  By Matthew Dillon.
 *
 *  Debugging routines are disabled by simply attempting to access the
 *  file "debugoff"
 */
 
#include "dos.h"
 
/*
 *  Since this code might be called as a processes multiple times, or
 *  might exit and be recalled later by DOS, you CANNOT ASSUME GLOBALS
 *  HAVE BEEN ZERO'D!!!.    This also goes for static assignments that
 *  running the code might change.
 */
 
PROC    *DosProc;
DEVNODE *DosNode;
 
long    SysBase;
long    DOSBase;
RAMFILE RFRoot;     /*  Directory/File structure    (root node) */
LIST    FHBase;     /*  Open Files                              */
LIST    LCBase;     /*  Open Locks                              */
 
long    TotalBytes;
 
 
                    /*  DEBUGGING                       */
PORT *Dbport;       /*  owned by the debug process      */
PORT *Dback;        /*  owned by the DOS device driver  */
short DBDisable;
MSG DummyMsg;
 
void
noname()
{
    register PACKET *packet;
    register short   error;
    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.
     */
 
    DBDisable = 0;
    Dbport = Dback = NULL;
    TotalBytes = 0;
    SysBase = AbsExecBase;
    DOSBase = OpenLibrary("dos.library",0);
    DosProc = FindTask(NULL);
    {
        WaitPort(&DosProc->pr_MsgPort);         /*  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) {
            DosNode = BTOC(packet->dp_Arg3);
            DosNode->dn_Task = &DosProc->pr_MsgPort;
            packet->dp_Res1 = DOS_TRUE;
            packet->dp_Res2 = 0;
        } else {
            packet->dp_Res1 = DOS_FALSE;
            returnpacket(packet);
            return;
        }
        returnpacket(packet);
    }
 
    /*  DEBUGGING   */
 
    dbinit();       /* this can be removed  */
 
    /*  Initialize  RAM disk    */
 
    {
        ubyte *ptr = BTOC(DosNode->dn_Name);
        short len = *ptr;
 
        NewList(&FHBase);
        NewList(&LCBase);
        bzero(&RFRoot,sizeof(RFRoot));
        RFRoot.type = FILE_DIR;
        NewList(&RFRoot.list);
        RFRoot.name = AllocMem(len+1, MEMF_PUBLIC);
        bmov(ptr+1,RFRoot.name,len);
        RFRoot.name[len] = 0;
        dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
    }
 
top:
    for (notdone = 1; notdone;) {
        WaitPort(&DosProc->pr_MsgPort);
        stats();
        while (msg = GetMsg(&DosProc->pr_MsgPort)) {
            register ubyte *ptr;
            packet = (PACKET *)msg->mn_Node.ln_Name;
            packet->dp_Res1 = DOS_TRUE;
            packet->dp_Res2 = 0;
            error = 0;
            dbprintf("Packet: %3ld %08lx %08lx %08lx %08lx\n",
                packet->dp_Type, packet->dp_Arg1, packet->dp_Arg2,
                 packet->dp_Arg3, packet->dp_Arg4
            );
 
            switch(packet->dp_Type) {
            case ACTION_DIE:        /*  attempt to die?                     */
                notdone = 0;
                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(packet->dp_Arg2);
                    char    *ptr;
 
                    btos(packet->dp_Arg3,buf);
                    if (strcmp(buf,"debugoff") == 0)
                        DBDisable = 1;
                    if (strcmp(buf,"debugon") == 0)
                        DBDisable = 0;
                    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
                        dbprintf("Found: parentdir %08lx  tail '%s'\n",
                         parentdir, ptr);
                        if (ramfile->type == FILE_DIR) {
                            error = ERROR_OBJECT_WRONG_TYPE;
                            goto openbreak;
                        }
                        if (ramfile->locks < 0) {
                            error = ERROR_OBJECT_IN_USE;
                            goto openbreak;
                        }
                        if (packet->dp_Type == ACTION_OPENOLD) {
                            ++ramfile->locks;
                        } else {
                            if (ramfile->locks > 0) {
                                error = ERROR_OBJECT_IN_USE;
                            } else {
                                if (packet->dp_Type == ACTION_OPENNEW)
                                    freedata(ramfile);
                                --ramfile->locks;
                            }
                        }
                    } else {
                        dbprintf("Not found..");
                        if (!parentdir) {
                            error = ERROR_INVALID_COMPONENT_NAME;
                            goto openbreak;
                        }
                        if (packet->dp_Type == ACTION_OPENNEW) {
                            ramfile = createramfile(parentdir, FILE_FILE, ptr);
                            --ramfile->locks;
                            dbprintf("adding file %08lx '%s'\n", ramfile,
                             ramfile->name);
                        } else {
                            error = ERROR_OBJECT_NOT_FOUND;
                        }
                    }
                    if (!error) {
                        register MYFH *mfh = AllocMem(sizeof(MYFH),
                         MEMF_PUBLIC|MEMF_CLEAR);
                        ((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)mfh;
                        dbprintf("SET FH-ARG1 to %08lx\n", 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 *)packet->dp_Arg1;
                    register FENTRY *fen = mfh->fentry;
                    register ubyte  *ptr = (ubyte *)packet->dp_Arg2;
                    register long   left = packet->dp_Arg3;
                    register long   scr;
 
                    dbprintf("READ Seek:%ld MFH:%08lx FEN:%08lx\n", mfh->base +
                     mfh->offset, mfh, fen);
                    while (left && fen) {
                        scr = fen->bytes - mfh->offset;
                        dbprintf("%ld bytes avail to read. req %ld\n",scr,left);
                        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;
                    packet->dp_Res1 = packet->dp_Arg3 - left;
                }
                break;
            case ACTION_WRITE:      /*   FHArg1,CPTRBuffer,Length   ActLength */
                {
                    register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
                    register FENTRY *fen = (FENTRY *)mfh->fentry;
                    register ubyte  *ptr = (ubyte *)packet->dp_Arg2;
                    register long   left = packet->dp_Arg3;
                    register long   scr;
 
                    dbprintf("MFH: %08lx\n", mfh);
                    while (left) {
                        if (fen) {
                            dbprintf("WRITE: %ld FEN %ld\n", left, fen->bytes);
                            scr = fen->bytes - mfh->offset;
                            if (left < scr) {
                                bmov(ptr, fen->buf + mfh->offset, left);
                                mfh->offset += left;
                                left = 0;
                            } else {
                                bmov(ptr, fen->buf + mfh->offset, scr);
                                ptr += scr;
                                left -= scr;
                                mfh->base += fen->bytes;
                                mfh->offset = 0;
                                fen = NextNode(fen);
                            }
                        } else {
                            dbprintf("WRITE: %ld NOFEN\n", left);
                            fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
                            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);
                            bmov(ptr, fen->buf, left);
                            left = 0;
                            fen = NULL;     /*  cause append    */
                        }
                    }
                    packet->dp_Res1 = packet->dp_Arg3 - left;
                    mfh->fentry = fen;
                }
                break;
            case ACTION_CLOSE:      /*   FHArg1                     Bool:TRUE */
                {
                    register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
                    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 *)packet->dp_Arg1;
                    register FENTRY *fen;
                    register long absseek;
 
                    packet->dp_Res1 = mfh->base + mfh->offset;
                    absseek = packet->dp_Arg2;
                    if (packet->dp_Arg3 == 0)
                        absseek += mfh->base + mfh->offset;
                    if (packet->dp_Arg3 == 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;
            case ACTION_EXAMINE_NEXT: /*   Lock,Fib                   Bool    */
                {
                    register FIB *fib = BTOC(packet->dp_Arg2);
                    register RAMFILE *file = getlockfile(packet->dp_Arg1);
 
                    if (file->type == FILE_FILE) {
                        error = ERROR_OBJECT_WRONG_TYPE;
                        break;
                    }
                    if (fib->fib_DiskKey == NULL) {
                        error = ERROR_NO_MORE_ENTRIES;
                        break;
                    }
                    file = (RAMFILE *)fib->fib_DiskKey;
                    fib->fib_DiskKey = (long)NextNode(file);
                    tmp = file;
                    error = -1;
                }
                /*  fall through    */
            case ACTION_EXAMINE_OBJECT: /*   Lock,Fib                   Bool  */
                {
                    register FIB *fib;
                    register RAMFILE *file;
 
                    fib = BTOC(packet->dp_Arg2);
                    if (error) {
                        file = tmp;
                    } else {
                        file = getlockfile(packet->dp_Arg1);
                        fib->fib_DiskKey = (long)GetHead(&file->list);
                    }
                    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(packet->dp_Arg2);
                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)
                     */
 
                    (error) ? (id = tmp) : (id = BTOC(packet->dp_Arg1));
                    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(packet->dp_Arg1);
                    if (file->type == FILE_FILE) {
                        error = ERROR_OBJECT_NOT_FOUND;
                        break;
                    }
                    if (file->locks < 0) {
                        error = ERROR_OBJECT_IN_USE;
                        break;
                    }
                    if (file->parent)
                        packet->dp_Res1 = (long)CTOB(ramlock(file->parent,
                         ACCESS_READ));
                    else
                        error = ERROR_OBJECT_NOT_FOUND;
                }
                break;
            case ACTION_DELETE_OBJECT: /*Lock,Name                  Bool      */
                {
                    RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
                    RAMFILE *ramfile;
 
                    btos(packet->dp_Arg2, buf);
                    if (ramfile = searchpath(&parentdir,buf,NULL)) {
                        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);
                    } 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(packet->dp_Arg1);
                    RAMFILE *ramfile;
                    char *ptr;
 
                    btos(packet->dp_Arg2, buf);
                    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
                        error = ERROR_OBJECT_EXISTS;
                        break;
                    }
                    if (!parentdir) {
                        error = ERROR_INVALID_COMPONENT_NAME;
                        break;
                    }
                    ramfile = createramfile(parentdir, FILE_DIR, ptr);
                    packet->dp_Res1 =(long)CTOB(ramlock(ramfile, ACCESS_WRITE));
                }
                break;
            case ACTION_LOCATE_OBJECT:  /*   Lock,Name,Mode             Lock  */
                {
                    RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
                    RAMFILE *ramfile;
 
                    btos(packet->dp_Arg2, buf);
                    if (ramfile = searchpath(&parentdir,buf,NULL)) {
                        if (ramfile->locks < 0 || (ramfile->locks &&
                         packet->dp_Arg3 == ACCESS_WRITE)) {
                            error = ERROR_OBJECT_IN_USE;
                            break;
                        }
                        packet->dp_Res1 = (long)CTOB(ramlock(ramfile,
                         packet->dp_Arg3));
                    } 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(packet->dp_Arg1);
                    if (ramfile->locks < 0)
                        error = ERROR_OBJECT_IN_USE;
                    else
                        packet->dp_Res1 = (long)CTOB(ramlock(ramfile,
                         ACCESS_READ));
                }
                break;
            case ACTION_FREE_LOCK:  /*   Lock,                      Bool     */
                if (packet->dp_Arg1);
                    ramunlock(BTOC(packet->dp_Arg1));
                if (!GetHead(&FHBase) && !GetHead(&LCBase))
                    notdone = 0;
                break;
            case ACTION_SET_PROTECT:/*   -,Lock,Name,Mask          Bool     */
                {
                    register RAMFILE *ramfile;
                    RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
                    char *ptr;
 
                    btos(packet->dp_Arg3, buf);
                    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
                        ramfile->protection = packet->dp_Arg4;
                    } 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(packet->dp_Arg2);
                    char *ptr;
 
                    btos(packet->dp_Arg3, buf);
                    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
                        btos(packet->dp_Arg4, 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, *file2;
                    RAMFILE *sourcedir = getlockfile(packet->dp_Arg1);
                    RAMFILE *destdir   = getlockfile(packet->dp_Arg3);
                    char *ptr;
 
                    btos(packet->dp_Arg2,buf);
                    if (file1 = searchpath(&sourcedir,buf,NULL)) {
                        btos(packet->dp_Arg4,buf);
                        if (file2 = searchpath(&destdir,buf,&ptr)) {
                            error = ERROR_OBJECT_EXISTS;
                        } else {
                            if (destdir) {
                                Remove(file1);
                                AddTail(&destdir->list, file1);
                                FreeMem(file1->name,strlen(file1->name)+1);
                                file1->name = AllocMem(strlen(ptr)+1,
                                                       MEMF_PUBLIC);
                                strcpy(file1->name,ptr);
                            } 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_RENAME_DISK:/*   BSTR:NewName               Bool      */
            case ACTION_MORECACHE:  /*   #BufsToAdd                 Bool      */
            case ACTION_INHIBIT:    /*   Bool                       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) {
                    packet->dp_Res1 = DOS_FALSE;
                    packet->dp_Res2 = error;
                }
                returnpacket(packet);
            }
        }
    }
    dbprintf("REMOVING DOS DEVICE");
    Delay(50);      /*  I wanna even see the debug message! */
    Forbid();
    if (packetsqueued(DosProc) || GetHead(&FHBase) || GetHead(&LCBase)
      || GetHead(&RFRoot.list)) {
        Permit();
        dbprintf("Not ready to die yet!\n");
        goto top;               /*  sorry... can't exit     */
    }
    DosNode->dn_Task = FALSE;
    dbuninit();
    CloseLibrary(DOSBase);
}
 
/*
 *  PACKET ROUTINES.    Dos Packets are in a rather strange format as you
 *  can see by this and how the PACKET structure is extracted in the
 *  GetMsg() of the main routine.
 */
 
void
returnpacket(packet)
register struct DosPacket *packet;
{
    register struct Message *mess;
    register struct MsgPort *replyport;
 
    replyport                = packet->dp_Port;
    mess                     = packet->dp_Link;
    packet->dp_Port          = &DosProc->pr_MsgPort;
    mess->mn_Node.ln_Name    = (char *)packet;
    mess->mn_Node.ln_Succ    = NULL;
    mess->mn_Node.ln_Pred    = NULL;
    PutMsg(replyport, mess);
}
 
/*
 *  Are there any packets queued to our device?
 */
 
packetsqueued()
{
    return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
            (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
}
 
/*
 *  DOS MEMORY ROUTINES
 *
 *  DOS makes certain assumptions about LOCKS.  A lock must minimally be
 *  a FileLock structure, with additional private information after the
 *  FileLock structure.  The longword before the beginning of the structure
 *  must contain the length of structure + 4.
 */
 
void *
dosalloc(bytes)
register ulong bytes;
{
    register ulong *ptr;
 
    bytes += 4;
    ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
    *ptr = bytes;
    return(ptr+1);
}
 
dosfree(ptr)
register ulong *ptr;
{
    --ptr;
    FreeMem(ptr, *ptr);
}
 
/*
 *  Convert a BSTR into a normal string.. copying the string into buf
 */
 
void
btos(bstr,buf)
ubyte *bstr;
ubyte *buf;
{
    bstr = BTOC(bstr);
    bmov(bstr+1,buf,*bstr);
    buf[*bstr] = 0;
    dbprintf("btos: %ld %s\n", *bstr, buf);
}
 
void *
NextNode(node)
NODE *node;
{
    node = node->mln_Succ;
    if (node->mln_Succ == NULL)
        return(NULL);
    return(node);
}
 
void *
GetHead(list)
LIST *list;
{
    if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
        return(list->mlh_Head);
    return(NULL);
}
 
nccmp(p1,p2,n)
register ubyte *p1, *p2;
register short n;
{
    while (--n >= 0) {
        if ((p1[n]|0x20) != (p2[n]|0x20))
            return(0);
    }
    return(1);
}
 
/*
 *  Create a file or directory and link it into it's parent directory.
 */
 
RAMFILE *
createramfile(parentdir, type, name)
RAMFILE *parentdir;
char *name;
{
    register RAMFILE *ramfile;
 
    ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
    AddTail(&parentdir->list, ramfile);
    ramfile->parent = parentdir;
    ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
    strcpy(ramfile->name, name);
    ramfile->type = type;
    ramfile->protection = 0;
    NewList(&ramfile->list);
    /*  SET DATE!   */
    return(ramfile);
}
 
/*
 *  Free all data associated with a file
 */
 
void
freedata(ramfile)
RAMFILE *ramfile;
{
    FENTRY *fen;
 
    TotalBytes -= ramfile->bytes;
    while (fen = RemHead(&ramfile->list)) {
        FreeMem(fen->buf, fen->bytes);
        FreeMem(fen, sizeof(*fen));
    }
    ramfile->bytes = 0;
}
 
/*
 *  Any data associated with the file or directory has already been
 *  freed up.
 */
 
void
freeramfile(ramfile)
RAMFILE *ramfile;
{
    Remove(ramfile);            /*  unlink from parent directory    */
    FreeMem(ramfile->name,strlen(ramfile->name)+1);
    if (ramfile->comment)
        FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
    FreeMem(ramfile,sizeof(*ramfile));
}
 
MYLOCK *
ramlock(ramfile, mode)
RAMFILE *ramfile;
{
    MYLOCK *lock = dosalloc(sizeof(MYLOCK));
    AddHead(&LCBase,&lock->node);
    lock->lock.fl_Access = mode;
    lock->lock.fl_Task = &DosProc->pr_MsgPort;
    lock->lock.fl_Volume = (BPTR)CTOB(DosNode);
    lock->file = ramfile;
    if (mode == ACCESS_READ)
        ++ramfile->locks;
    else
        ramfile->locks = -1;
    return(lock);
}
 
void
ramunlock(lock)
MYLOCK *lock;
{
    RAMFILE *file = lock->file;
    Remove(&lock->node);
    if (lock->lock.fl_Access == ACCESS_READ)
        --file->locks;
    else
        file->locks = 0;
    dosfree(lock);
}
 
/*
 *  GETLOCKFILE(bptrlock)
 *
 *  Return the RAMFILE entry (file or directory) associated with the
 *  given lock, which is passed as a BPTR.
 *
 *  According to the DOS spec, the only way a NULL lock will ever be
 *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
 *  In anycase, If a NULL lock is passed to me I simply assume it means
 *  the root directory of the RAM disk.
 */
 
RAMFILE *
getlockfile(lock)
void *lock;             /*  actually BPTR to MYLOCK */
{
    if (lock)
        return(((MYLOCK *)BTOC(lock))->file);
    return(&RFRoot);
}
 
/*
 *  Search the specified path beginning at the specified directory.
 *  The directory pointer is updated to the directory containing the
 *  actual file.  Return the file node or NULL if not found.  If the
 *  path is illegal (an intermediate directory was not found), set *ppar
 *  to NULL and return NULL.
 *
 *  *ppar may also be set to NULL if the search path IS the root.
 *
 *  If pptr not NULL, Set *pptr to the final component in the path.
 */
 
RAMFILE *
searchpath(ppar,buf,pptr)
RAMFILE **ppar;
char *buf;
char **pptr;
{
    RAMFILE *file = *ppar;
    RAMFILE *srch;
    short len;
    char *ptr;
 
    dbprintf("searchpath: '%s'\n", buf);
    *ppar = NULL;
    for (;*buf && file;) {
        ptr = getpathelement(&buf,&len);
        if (buf[0] == ':') {    /*  go to root          */
            dbprintf("colon element\n");
            ++buf;
            file = &RFRoot;
            continue;
        }
        if (*ptr == '/') {          /*  go back a directory */
            if (!file->parent) {    /*  no parent directory */
                dbprintf("Backdir.. no parent\n");
                return(NULL);
            }
            file = file->parent;
            dbprintf("backdir: file %08lx (%s)\n", file, file->name);
            continue;
        }
        if (file->type == FILE_FILE)
            return(NULL);
        for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
            if (strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
                file = srch;        /*  element found       */
                break;
            }
        }
        if (srch == NULL) {
            if (*buf == 0) {    /*  Element not found.  If it was the final */
                *ppar = file;   /*  element the parent directory is valid   */
                dbprintf("element not found.. ok\n");
            } else {
                dbprintf("element not found.. path error\n");
            }
            if (pptr)
                *pptr = ptr;
            return(NULL);
        }
    }
    if (pptr)
        *pptr = ptr;
    *ppar = file->parent;
    dbprintf("DONE. File %08lx (%s) parent %08lx\n", file, file->name,
     file->parent);
    return(file);
}
 
/*
 *  Return the next path element in the string.  The routine effectively
 *  removes any trailing '/'s, but treats ':' as part of the next component
 *  (i.e. ':' is checked and skipped in SEARCHPATH()).
 */
 
char *
getpathelement(pstr,plen)
char **pstr;
short *plen;
{
    char *base;
    register char *ptr = *pstr;
    register short len = 0;
 
    if (*(base = ptr)) {
        if (*ptr == '/') {
            ++ptr;
            ++len;
        } else {
            while (*ptr && *ptr != '/' && *ptr != ':') {
                ++ptr;
                ++len;
            }
            if (*ptr == '/')
                ++ptr;
        }
    }
    *pstr = ptr;
    *plen = len;
    dbprintf("PATH ELEMENT: %ld '%s' next@ '%s'\n", len, base, ptr);
    return(base);
}
 
 
stats()
{
    register RAMFILE *file;
    register MYFH *mfh;
 
    for (file = GetHead(&RFRoot.list); file; file = NextNode(file)) {
        dbprintf("Root entry: %08lx %s %ld\n", file, file->name, file->bytes);
    }
    for (mfh = GetHead(&FHBase); mfh; mfh = NextNode(mfh)) {
        dbprintf("Openfile: %08lx %s @%ld %ld\n", mfh->file, mfh->file->name,
         mfh->base, mfh->offset);
    }
    /*
     *  Note that the NODE entry in the MYLOCK structure is not (cannot) be
     *  at the beginning of the structure and thus if you want to traverse
     *  the active lock list you must subtract it's position in the
     *  structure from any list pointers.
     */
    if (GetHead(&LCBase)) {
        dbprintf("ACTIVE LOCKS DO EXIST\n");
    }
    dbprintf("\n");
}
 
/*
 *  DEBUGGING CODE.     You cannot make DOS library calls that access other
 *  devices from within a DOS device driver because they use the same
 *  message port as the driver.  If you need to make such calls you must
 *  create a port and construct the DOS messages yourself.  I do not
 *  do this.  To get debugging info out another PROCESS is created to which
 *  debugging messages can be sent.
 *
 *  You want the priority of the debug process to be larger than the
 *  priority of your DOS handler.  This is so if your DOS handler crashes
 *  you have a better idea of where it died from the debugging messages
 *  (remember that the two processes are asyncronous from each other).
 */
 
extern void debugproc();
 
dbinit()
{
    TASK *task = FindTask(NULL);
 
    Dback = CreatePort(NULL,NULL);
    CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096);
    WaitPort(Dback);                                /* handshake startup    */
    GetMsg(Dback);                                  /* remove dummy msg     */
    dbprintf("Debugger running V1.00\n");
}
 
dbuninit()
{
    MSG killmsg;
 
    if (Dbport) {
        killmsg.mn_Length = 0;      /*  0 means die         */
        PutMsg(Dbport,&killmsg);
        WaitPort(Dback);            /*  He's dead jim!      */
        GetMsg(Dback);
        DeletePort(Dback);
 
        /*
         *  Since the debug process is running at a greater priority, I
         *  am pretty sure that it is guarenteed to be completely removed
         *  before this task gets control again.  Still, it doesn't hurt...
         */
 
        Delay(50);                  /*  ensure he's dead    */
    }
}
 
dbprintf(a,b,c,d,e,f,g,h,i,j)
{
    char buf[256];
    MSG *msg;
 
    if (Dbport && !DBDisable) {
        sprintf(buf,a,b,c,d,e,f,g,h,i,j);
        msg = AllocMem(sizeof(MSG)+strlen(buf)+1, MEMF_PUBLIC|MEMF_CLEAR);
        msg->mn_Length = strlen(buf)+1;     /*  Length NEVER 0  */
        strcpy(msg+1,buf);
        PutMsg(Dbport,msg);
    }
}
 
/*
 *  BTW, the DOS library used by debugmain() was actually openned by
 *  the device driver.  Note: DummyMsg cannot be on debugmain()'s stack
 *  since debugmain() goes away on the final handshake.
 */
 
debugmain()
{
    MSG *msg;
    short len;
    void *fh;
 
    Dbport = CreatePort(NULL,NULL);
    fh = Open("con:0/0/640/100/debugwindow", 1006);
    PutMsg(Dback, &DummyMsg);
    for (;;) {
        WaitPort(Dbport);
        msg = GetMsg(Dbport);
        len = msg->mn_Length;
        if (len == 0)
            break;
        --len;                      /*  Fix length up   */
        Write(fh, msg+1, len);
        FreeMem(msg,sizeof(MSG)+len+1);
    }
    Close(fh);
    DeletePort(Dbport);
    PutMsg(Dback,&DummyMsg);          /*  Kill handshake  */
}
 
/*
 *  The assembly tag for the DOS process:  CNOP causes alignment problems
 *  with the Aztec assembler for some reason.  I assume then, that the
 *  alignment is unknown.  Since the BCPL conversion basically zero's the
 *  lower two bits of the address the actual code may start anywhere
 *  within 8 bytes of address (remember the first longword is a segment
 *  pointer and skipped).  Sigh....  (see CreatProc() above).
 */
 
#asm
        public  _debugproc
        public  _debugmain
 
        cseg
_debugproc:
        nop
        nop
        nop
        nop
        nop
        movem.l D2-D7/A2-A6,-(sp)
        jsr     _debugmain
        movem.l (sp)+,D2-D7/A2-A6
        rts
#endasm
!Funky!Stuff!
fi  # end of overwriting check
exit 0
