
/*
 *  NFS-HANDLER.C	V1.11	27-June-89
 *
 *  DNet DOS level NFS handler.
 *
 *  By Matthew Dillon.
 *
 *  This handler converts DOS packets into a form suitable for transmission
 *  over a DNet link to a remote server which will execute the operations.
 */

#include "dos.h"
#include <exec/alerts.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.
 */

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			*/
/*
void	*DResBase;
*/

DOSLIB	*DOSBase;   /*	DOS library base for debug process	*/
MLIST	FHBase;     /*	Open Files				*/
MLIST	LCBase;     /*	Open Locks				*/

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

void	*CHan;	    /*	DNet channel				*/
HANDLE	RFRoot;

char	TmpBuf[256];

#ifdef DEBUG
short DBDisable = 0;	/*  Debug code				    */
#endif


HANDLE *AllocHandle();

/*
 *  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 __saveds noname ARGS((void));

void returnpacket ARGS((PACKET *));
int packetsqueued ARGS((void));
HANDLE *GetHandleForLock ARGS((LOCK *));
void *GetLockForHandle ARGS((HANDLE *));
HANDLE *AllocHandle ARGS((char *, RtOpen *));
void FreeHandle ARGS((HANDLE *));
void *dosalloc ARGS((ulong));
void dosfree ARGS((ulong *));
char *bstos ARGS((ubyte *));
char *skipdevice ARGS((char *));
int DoNetworkOp ARGS((char, void *, int, void *, int, void *, int));

void __saveds
noname()
{
    reg PACKET	*packet;
    reg short	error;
    MSG     *msg;
    ubyte   notdone;
    void    *tmp;

#ifndef LATTICE
    mygeta4();
#endif

    /*
     *	Initialize all global variables.  SysBase MUST be initialized before
     *	we can make Exec calls.
     */

    TotalBytes = 0;
    SysBase = *(void **)4;
    DOSBase = (void *)OpenLibrary("dos.library",0);
    /*
    DResBase= OpenLibrary("dres.library",0);
    */
    DosProc = (PROC *)FindTask(NULL);
    CHan = 0;
    {
	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 /*&& DResBase*/) {
	    DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
	    register DEVLIST *dl = dosalloc(sizeof(DEVLIST));

	    DosNode = BTOC(packet->dp_Arg3);
	    DosNode->dn_Task = &DosProc->pr_MsgPort;

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

	    DevList = dl;
	    dl->dl_Type = DLT_VOLUME;
	    dl->dl_Task = &DosProc->pr_MsgPort;
	    dl->dl_DiskType = ID_DOS_DISK;
	    dl->dl_Name = (BSTR)DosNode->dn_Name;
	    dl->dl_Next = di->di_DevInfo;
	    di->di_DevInfo = (long)CTOB(dl);

	    packet->dp_Res1 = DOS_TRUE;
	    packet->dp_Res2 = 0;
	} else {			    /*	couldn't open dos.library   */
	    packet->dp_Res1 = DOS_FALSE;
	    returnpacket(packet);
	    return;			    /*	exit process		    */
	}
	returnpacket(packet);
    }

    /*	Initialize  RAM disk	*/
#ifdef DEBUG
    dbinit();
#endif

    {
	NewList((LIST *)&FHBase);                   /*  more globals    */
	NewList((LIST *)&LCBase);

	BZero(&RFRoot, sizeof(RFRoot));
	DateStamp((long *)&RFRoot.Date);
	RFRoot.Type = FILE_DIR;
	RFRoot.Name = "Root";
    }

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

top:
    for (notdone = 1; notdone;) {
	WaitPort(&DosProc->pr_MsgPort);
	while (msg = GetMsg(&DosProc->pr_MsgPort)) {
	    packet = (PACKET *)msg->mn_Node.ln_Name;
	    packet->dp_Res1 = DOS_TRUE;
	    packet->dp_Res2 = 0;
	    error = 0;

	    if (!CHan) {            /*  attempt to open server channel      */
		CHan = DOpen(NULL, PORT_NFS, -80, -80);
		if (!CHan) {        /*  otherwise giveup!                   */
		    notdone = 0;
		    goto fail;
		}
	    }

#ifdef DEBUG
	    dbprintf("ACTION %ld\n", packet->dp_Type);
#endif
	    switch(packet->dp_Type) {
	    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    */
		{
		    OpOpen	opkt;
		    RtOpen	rpkt;
		    short	r;
		    char	*name = skipdevice(bstos((ubyte *)packet->dp_Arg3));
		    HANDLE	*handle;

		    opkt.DirHandle = GetHandleForLock((LOCK *)packet->dp_Arg2)->Handle;
		    opkt.Modes = packet->dp_Type;

		    r = DoNetworkOp('O',&opkt,sizeof(opkt),name,strlen(name)+1,&rpkt,sizeof(rpkt));
		    if (r) {
			error = ERROR_OBJECT_IN_USE;
		    } else if (rpkt.Handle == -1) {
			error = ERROR_OBJECT_NOT_FOUND;
		    } else if (rpkt.Type > 0) {     /* can't open dir */
			OpClose  opkt;

			opkt.Handle = rpkt.Handle;
			DoNetworkOp('C',&opkt,sizeof(opkt),NULL,0,NULL,0);
			error = ERROR_OBJECT_WRONG_TYPE;
		    } else {
			if (name[0] == 0)
			    name = GetHandleForLock((LOCK *)packet->dp_Arg2)->Name;
			handle = AllocHandle(name, &rpkt);
			((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)handle;
		    }
		}
		break;
	    case ACTION_READ:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
		{
		    HANDLE  *handle = (HANDLE *)packet->dp_Arg1;
		    OpRead	opkt;
		    RtRead	rpkt;
		    short	err;

		    opkt.Handle = handle->Handle;
		    opkt.Bytes	    = packet->dp_Arg3;

		    err = DoNetworkOp('R',&opkt,sizeof(opkt),NULL,0,&rpkt,sizeof(rpkt));
		    if (err)
			rpkt.Bytes = -1;
		    if (rpkt.Bytes > 0 && rpkt.Bytes <= opkt.Bytes)
			DRead(CHan, (void *)packet->dp_Arg2, rpkt.Bytes);
		    packet->dp_Res1 = rpkt.Bytes;
		}
		break;
	    case ACTION_WRITE:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
		{
		    HANDLE  *handle = (HANDLE *)packet->dp_Arg1;
		    OpWrite	opkt;
		    RtWrite	rpkt;
		    short	err;

		    opkt.Handle = handle->Handle;
		    opkt.Bytes	    = packet->dp_Arg3;

		    err = DoNetworkOp('W',&opkt,sizeof(opkt),(void *)packet->dp_Arg2,packet->dp_Arg3,&rpkt,sizeof(rpkt));

		    if (err)
			rpkt.Bytes = -1;
		    packet->dp_Res1 = rpkt.Bytes;
		}
		break;
	    case ACTION_CLOSE:	    /*	 FHArg1 		    Bool:TRUE  */
		{
		    HANDLE  *handle = (HANDLE *)packet->dp_Arg1;
		    OpClose	opkt;

		    if (packet->dp_Arg1 == NULL || handle->Magic != MAGIC) {
			Alert(AT_Recovery|0, (char *)0x12345555);
		    } else {
			opkt.Handle = handle->Handle;

			DoNetworkOp('C',&opkt,sizeof(opkt),NULL,0,NULL,0);
			FreeHandle(handle);
		    }
		}
		if (!GetHead(&FHBase))
		    notdone = 0;
		break;
	    case ACTION_SEEK:	    /*	 FHArg1,Position,Mode	    OldPosition*/
		{
		    HANDLE  *handle = (HANDLE *)packet->dp_Arg1;
		    OpSeek  opkt;
		    RtSeek  rpkt;
		    short   err;

		    opkt.Handle = handle->Handle;
		    opkt.Offset = packet->dp_Arg2;
		    opkt.How	= packet->dp_Arg3 + 1;

		    err = DoNetworkOp('S',&opkt,sizeof(opkt),NULL,0,&rpkt,sizeof(rpkt));
		    if (err)
			rpkt.NewOffset = -1;
		    if (rpkt.NewOffset < 0)
			error = ERROR_SEEK_ERROR;
		    else
			packet->dp_Res1 = rpkt.OldOffset;
		}
		break;
	    case ACTION_EXAMINE_NEXT: /*   Lock,Fib		      Bool	 */
		{
		    HANDLE *handle = GetHandleForLock((LOCK *)packet->dp_Arg1);
		    register FIB *fib = BTOC(packet->dp_Arg2);
		    OpNextDir opkt;
		    RtNextDir rpkt;
		    short err;

#ifdef DEBUG
		    dbprintf("FIB = %08lx\n", fib);
#endif
		    if (handle->Type < 0) {
			error = ERROR_OBJECT_WRONG_TYPE;
			break;
		    }

		    opkt.Handle = handle->Handle;
		    opkt.Index	= fib->fib_DiskKey;
		    err = DoNetworkOp('N',&opkt,sizeof(opkt),NULL,0,&rpkt,sizeof(rpkt));
		    if (err || rpkt.Handle == -1) {
			error = ERROR_NO_MORE_ENTRIES;
		    } else {	/* not a real file handle, just info & name */
			unsigned char fn;

			if (DRead(CHan, &fn, 1) != 1)
			    TmpBuf[fn = 0] = 0;
			if (DRead(CHan, TmpBuf, fn) != fn)
			    TmpBuf[fn = 0] = 0;
			++fib->fib_DiskKey;	/*  next key */
			fib->fib_DirEntryType = rpkt.Type;
			fib->fib_Protection = rpkt.Prot;
			fib->fib_EntryType = NULL;
			fib->fib_Size = rpkt.Size;
			fib->fib_NumBlocks = rpkt.Size >> 9;
			if (strlen(TmpBuf) >= sizeof(fib->fib_FileName) - 2) {
			    fib->fib_FileName[0] = 1;
			    fib->fib_FileName[1] = '?';
			    fib->fib_FileName[2] = 0;
			} else {
			    strcpy(fib->fib_FileName+1, TmpBuf);
			    fib->fib_FileName[0] = strlen(TmpBuf);
			}
			fib->fib_Comment[0] = 0;
			fib->fib_Comment[1] = 0;
			fib->fib_Date = rpkt.Date;
		    }
		}
		break;
	    case ACTION_EXAMINE_OBJECT: /*   Lock,Fib			Bool	   */
		{
		    HANDLE *handle = GetHandleForLock((LOCK *)packet->dp_Arg1);
		    register FIB *fib = BTOC(packet->dp_Arg2);

#ifdef DEBUG
		    dbprintf("FIB = %08lx\n", fib);
#endif
		    fib->fib_DiskKey = 0;
		    fib->fib_DirEntryType = handle->Type;
		    /*
		     *	fib->fib_FileName bcpl type string
		     */
		    fib->fib_Protection = handle->Prot;
		    fib->fib_EntryType = NULL;
		    fib->fib_Size = handle->Size;
		    fib->fib_NumBlocks = handle->Size >> 9;

		    if (strlen(handle->Name) >= sizeof(fib->fib_FileName) - 2) {
			fib->fib_FileName[0] = 1;
			fib->fib_FileName[1] = '?';
			fib->fib_FileName[2] = 0;
		    } else {
			strcpy(fib->fib_FileName+1, handle->Name);
			fib->fib_FileName[0] = strlen(handle->Name);
		    }
		    fib->fib_Comment[0] = 0;
		    fib->fib_Comment[1] = 0;
		    fib->fib_Date = handle->Date;
		}
		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).  Other programs probably break
		     *	as well.
		     */

		    (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 */
		{
		    HANDLE *handle = GetHandleForLock((LOCK *)packet->dp_Arg1);
		    OpParent	opkt;
		    RtParent	rpkt;
		    short	err;

		    opkt.Handle = handle->Handle;

		    err = DoNetworkOp('P',&opkt,sizeof(opkt),NULL,0,&rpkt,sizeof(rpkt));
		    if (err)
			rpkt.Handle = NULL;
		    if (rpkt.Handle == -1) {
			error = ERROR_OBJECT_NOT_FOUND;
		    } else {	/*  create a file handle and return a lock */
			HANDLE *newhandle;
			unsigned char fn;

			if (DRead(CHan, &fn, 1) != 1)
			    TmpBuf[fn = 0] = 0;
			if (DRead(CHan, TmpBuf, fn) != fn)
			    TmpBuf[fn = 0] = 0;
			newhandle = AllocHandle(TmpBuf, &rpkt);
			packet->dp_Res1 = (long)GetLockForHandle(newhandle);
		    }
		}
		break;
	    case ACTION_DELETE_OBJECT: /*Lock,Name		    Bool       */
		{
		    OpDelete	opkt;
		    RtDelete	rpkt;
		    short	err;
		    char	*name = skipdevice(bstos((ubyte *)packet->dp_Arg2));

		    opkt.DirHandle = GetHandleForLock((LOCK *)packet->dp_Arg1)->Handle;

		    err = DoNetworkOp('D',&opkt,sizeof(opkt),name,strlen(name)+1,&rpkt,sizeof(rpkt));
		    if (err || rpkt.Error)
			error = ERROR_OBJECT_NOT_FOUND;
		}
		break;
	    case ACTION_CREATE_DIR: /*	 Lock,Name		    Lock       */
		{
		    OpCreateDir opkt;
		    RtCreateDir rpkt;
		    short	err;
		    char	*name = skipdevice(bstos((ubyte *)packet->dp_Arg2));

		    opkt.DirHandle = GetHandleForLock((LOCK *)packet->dp_Arg1)->Handle;

		    err = DoNetworkOp('M',&opkt,sizeof(opkt),name,strlen(name)+1,&rpkt,sizeof(rpkt));
		    if (err)
			rpkt.Handle = NULL;
		    if (rpkt.Handle == -1) {
			error = ERROR_OBJECT_EXISTS;
		    } else {	/*  create a file handle and return a lock */
			HANDLE *newhandle = AllocHandle(name, &rpkt);

			packet->dp_Res1 = (long)GetLockForHandle(newhandle);
		    }
		}
		break;
	    case ACTION_LOCATE_OBJECT:	/*   Lock,Name,Mode		Lock	   */
		{
		    OpOpen	opkt;
		    RtOpen	rpkt;
		    short	err;
		    char	*name = skipdevice(bstos((ubyte *)packet->dp_Arg2));
		    HANDLE	*handle = GetHandleForLock((LOCK *)packet->dp_Arg1);

		    opkt.DirHandle = handle->Handle;
		    opkt.Modes = 1005;

		    err = DoNetworkOp('O',&opkt,sizeof(opkt),name,strlen(name)+1,&rpkt,sizeof(rpkt));
		    if (err) {
			error = ERROR_OBJECT_IN_USE;
		    } else if (rpkt.Handle == -1) {
			error = ERROR_OBJECT_NOT_FOUND;
		    } else {
			HANDLE *newhandle;
			if (name[0] == 0)
			    name = handle->Name;

			newhandle = AllocHandle(name, &rpkt);
			packet->dp_Res1 = (long)GetLockForHandle(newhandle);
		    }
		}
		break;
	    case ACTION_COPY_DIR:   /*	 Lock,			    Lock       */
		{
		    OpDup	opkt;
		    RtDup	rpkt;
		    short	err;
		    HANDLE	*oldhandle = GetHandleForLock((LOCK *)packet->dp_Arg1);

		    opkt.Handle = oldhandle->Handle;

		    err = DoNetworkOp('d',&opkt,sizeof(opkt),NULL,0,&rpkt,sizeof(rpkt));
		    if (err) {
			error = ERROR_OBJECT_IN_USE;
		    } else if (rpkt.Handle == -1) {
			error = ERROR_OBJECT_NOT_FOUND;
		    } else {
			HANDLE *handle = AllocHandle(oldhandle->Name, &rpkt);

			packet->dp_Res1 = (long)GetLockForHandle(handle);
		    }
		}
		break;
	    case ACTION_FREE_LOCK:  /*	 Lock,			    Bool       */
		{
		    HANDLE  *handle = GetHandleForLock((LOCK *)packet->dp_Arg1);
		    OpClose	opkt;

		    opkt.Handle = handle->Handle;

		    DoNetworkOp('C',&opkt,sizeof(opkt),NULL,0,NULL,0);
		    FreeHandle(handle);
		}
		break;
	    case ACTION_SET_PROTECT:/*	 -,Lock,Name,Mask	   Bool       */
		error = ERROR_ACTION_NOT_KNOWN;
		break;
	    case ACTION_SET_COMMENT:/*	 -,Lock,Name,Comment	   Bool       */
		error = ERROR_ACTION_NOT_KNOWN;
		break;
	    case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName    Bool       */
		{
		    OpRename	opkt;
		    RtRename	rpkt;
		    short	err;
		    char	*name1= skipdevice(bstos((ubyte *)packet->dp_Arg2));
		    char	*name2= skipdevice(bstos((ubyte *)packet->dp_Arg4));
		    short	len1 = strlen(name1);
		    short	len2 = strlen(name2);
		    char	*name = AllocMem(len1+len2+2, MEMF_PUBLIC);

		    opkt.DirHandle1 = GetHandleForLock((LOCK *)packet->dp_Arg1)->Handle;
		    opkt.DirHandle2 = GetHandleForLock((LOCK *)packet->dp_Arg3)->Handle;
		    strcpy(name, name1);
		    strcpy(name+len1+1,name2);

		    err = DoNetworkOp('r',&opkt,sizeof(opkt),name,len1+len2+2,&rpkt,sizeof(rpkt));
		    FreeMem(name, len1+len2+2);
		    if (err) {
			error = ERROR_OBJECT_IN_USE;
		    } else if (rpkt.Error) {
			error = ERROR_OBJECT_NOT_FOUND;
		    }
		}
		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:
fail:
		error = ERROR_ACTION_NOT_KNOWN;
		break;
	    }
	    if (packet) {
		if (error) {
		    packet->dp_Res1 = DOS_FALSE;
		    packet->dp_Res2 = error;
		}
		returnpacket(packet);
	    }
	}
    }
    Delay(50);      /*  I wanna even see the debug message! */
    Forbid();
    if (packetsqueued() || GetHead(&FHBase)) {
	Permit();
	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((ulong *)dl);
	} else {
	    ;
	}
    }

    /*
     *	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).
     */

    if (CHan)
	DClose(CHan);
    CHan = 0;
#ifdef DEBUG
    dbuninit();
#endif
    CloseLibrary((LIB *)DOSBase);
    /*
    CloseLibrary(DResBase);
    */
}


/*
 *  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)
PACKET *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?
 */

int
packetsqueued()
{
    return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
	    (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
}

/*
 *  Handle structure, locks, and manipulation.
 */

HANDLE *
GetHandleForLock(block)
LOCK *block;
{
    register LOCK *lock = BTOC(block);
    register HANDLE *handle;

#ifdef DEBUG
    dbprintf("GetHandleForLock: %08lx ", lock);
#endif
    if (lock) {
	handle = (HANDLE *)lock->fl_Key;
#ifdef DEBUG
	dbprintf("Handle=%08lx (h=$%08lx)\n", handle, handle->Handle);
#endif
	if (handle->Magic == MAGIC)
	    return(handle);
	Alert(AT_Recovery|1, (char *)handle);
    }
#ifdef DEBUG
    dbprintf("\n");
#endif
    /*Alert(AT_Recovery|2, (char *)-2);*/
    return(&RFRoot);
}

void *
GetLockForHandle(handle)
HANDLE *handle;
{
    register LOCK *lock = handle->Lock;
    register LOCKLINK *ln;

#ifdef DEBUG
    dbprintf("GetLockForHandle: %08lx ", handle);
#endif
    if (!lock) {
	lock = dosalloc(sizeof(LOCK));
#ifdef DEBUG
	dbprintf("(allocate) %08lx\n", lock);
#endif
	ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC|MEMF_CLEAR);
	AddHead((LIST *)&LCBase,(NODE *)ln);
	ln->Lock = lock;
	lock->fl_Link = (long)ln;
	lock->fl_Key = (long)handle;
	lock->fl_Access = ACCESS_READ;
	lock->fl_Task= &DosProc->pr_MsgPort;
	lock->fl_Volume = (BPTR)CTOB(DosNode);

	handle->Lock = lock;
    }
#ifdef DEBUG
    dbprintf("Lock=%08lx\n", lock);
#endif
    return((void *)CTOB(lock));
}

HANDLE *
AllocHandle(name, rpkt)
char *name;
RtOpen *rpkt;
{
    HANDLE *handle;
    register char *ptr;

    for (ptr = name + strlen(name) - 1; ptr >= name && *ptr != '/'; --ptr);
    ++ptr;

    handle = AllocMem(sizeof(HANDLE), MEMF_PUBLIC|MEMF_CLEAR);
    handle->Magic = MAGIC;
    handle->Handle= rpkt->Handle;
    handle->Type = rpkt->Type;
    handle->Prot = rpkt->Prot;
    handle->Size = rpkt->Size;
    handle->Name = AllocMem(strlen(ptr)+1, MEMF_PUBLIC);
    handle->Date = rpkt->Date;
    strcpy(handle->Name, ptr);
    AddHead((LIST *)&FHBase, (NODE *)handle);

#ifdef DEBUG
    dbprintf("AllocHandle: %08lx '%s' h=$%08lx\n", handle, ptr, handle->Handle);
#endif
    return(handle);
}

void
FreeHandle(handle)
HANDLE *handle;
{
#ifdef DEBUG
    dbprintf("FreeHandle: %08lx %08lx\n", handle, handle->Lock);
#endif
    if (handle->Lock) {
	register LOCK *lock = handle->Lock;
	Remove((NODE *)lock->fl_Link);
	FreeMem((void *)lock->fl_Link, sizeof(LOCKLINK));
	dosfree((ulong *)lock);
    }
    Remove((NODE *)handle);
    handle->Magic = 0;
    FreeMem(handle->Name, strlen(handle->Name)+1);
    FreeMem(handle, sizeof(HANDLE));
}

/*
 *  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.
 *
 *  NOTE!!!!! The workbench does not follow the rules and assumes it can
 *  copy lock structures.  This means that if you want to be workbench
 *  compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
 */

void *
dosalloc(bytes)
reg ulong bytes;
{
    reg ulong *ptr;

    bytes += 4;
    ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
    *ptr = bytes;
    return(ptr+1);
}

void
dosfree(ptr)
register ulong *ptr;
{
    --ptr;
    FreeMem(ptr, *ptr);
}

/*
 *  Convert a BSTR into a normal string.. copying the string into buf.
 *  I use normal strings for internal storage, and convert back and forth
 *  when required.
 */

char *
bstos(bstr)
ubyte *bstr;
{
    static char buf[512];
    static char *ptr = buf;

    if (ptr == buf)     /*  switch which buffer to use */
	ptr = buf + 256;
    else
	ptr = buf;

    bstr = BTOC(bstr);
    BMov(bstr+1,ptr,*bstr);
    ptr[*bstr] = 0;
    return(ptr);
}

char *
skipdevice(str)
register char *str;
{
    char *base = str;

    while (*str && *str != ':')
	++str;
    if (*str == 0)
	str = base;
    else
	++str;
    return(str);
}

/*
 *  data = DoNetworkOp(&cmd, &len, data)
 */

int
DoNetworkOp(cmd, s, _slen, d, dlen, r, rlen)
char cmd;
int _slen;
void *s;
void *d;
void *r;
{
    ubyte slen = _slen;
    if (CHan) {
	if (DWrite(CHan, &cmd, 1) < 0)
	    goto fail;
	if (DWrite(CHan, &slen, 1) < 0)
	    goto fail;
	if (DWrite(CHan, &dlen, 4) < 0)
	    goto fail;
	if (DWrite(CHan, s, slen) < 0)
	    goto fail;

	if (dlen) {
	    if (DWrite(CHan, d, dlen) < 0)
		goto fail;
	}
	if (r) {
	    if (DRead(CHan, r, rlen) != rlen)
		goto fail;
	}
	return(0);
    }
fail:
    if (CHan) {
	DClose(CHan);
	CHan = 0;
    }
    return(1);
}

#ifndef LATTICE

#asm

_mygeta4:
	far	data
	lea	__H1_org+32766,a4
	rts

	public	__H0_org

	dseg

	public	__H1_org

	cseg
#endasm

#endif


#ifdef DEBUG	    /*	note, doesn't work w/ Lattice   */

/*
 *			    DEBUG CODE
 */


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

/*
 *  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.10, 2 November 1987\n");
    dbprintf("Works with WORKBENCH!\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.
 */

void
debugmain()
{
    MSG *msg;
    short len;
    void *fh;

    mygeta4();
    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 around
 *  the label....  Sigh....  (see CreatProc() above).
 */

#asm
	public	_debugproc
	public	_debugmain

	cseg
	nop
	nop
	nop
_debugproc:
	nop
	nop
	movem.l D2-D7/A2-A6,-(sp)
	jsr	_debugmain
	movem.l (sp)+,D2-D7/A2-A6
	rts
#endasm


#endif


