[comp.sources.amiga] v91i103: virt alpha - virtual device driver, Part04/04

amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (05/08/91)

Submitted-by: grahamw@cpsc.ucalgary.ca (William Graham)
Posting-number: Volume 91, Issue 103
Archive-name: devices/virt-alpha/part04

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 4)."
# Contents:  virtual.c
# Wrapped by tadguy@ab20 on Tue May  7 20:50:17 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'virtual.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'virtual.c'\"
else
echo shar: Extracting \"'virtual.c'\" \(58572 characters\)
sed "s/^X//" >'virtual.c' <<'END_OF_FILE'
X/*
X	about this project:
X
X	I started doing this thing in M.L. ( FindHandler is a remnant) but
X	then I realized I wanted to explore Handlers, not the Age of the
X	Universe.
X
X	But! luckily I encountered this fine piece of work by a
X	certain guy named *** Matt Dillion *** ; perhaps you've heard of him,
X	here's a guy who's making the best of the terrible misfortune to
X	have been born with eight arms; and the reflexes of a fly ;-)
X
X	yours truly anselm
X */
X
X#define NOBECOLORS
X#define BEVIRTUAL
X
X
X#include "dos.h"
X
X
X#define MakeID(a,b,c,d)  ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )
X#define VIRT	MakeID('V','I','R','T')
X
Xint myreadsome(ubyte *,FENTRY *,int);
Xint andycode(char *,MYFH *,PACKET *);
X
X/*
X	THE FOLLOWING IS A REVIEW OF THE APPROACH I TOOK TO SOLVING THE
X	PROBLEM OF THIS HANDLER NOT COMPILING PROPERLY UNDER LATTICE 5.10
X
X	It should serve as general help on writing MANX compatible code,
X	code using the AMIGADOS CreateProc, and nailing bugs in LATTICE 5.10.
X
X	(this review was released as an informative guide to some of the
X	 pitfalls of C programming, and as a bit of humour about just how
X	 yuchy things can get sometimes :)  Anselm Hook - Jan 21 1991. )
X
X	(this exploration was evolved over a 3 day period at about 8 hours
X	 a day (my computer is very slow) (now i know why people use machine
X	 code; comparatively in 3 days worth of time we mostly wrote DATASTORM
X	 (admittedly at 24 hours a day but....) )
X
X	( I bothered with this analysis because of the inexplicable erratic
X	  nature of the bugs, and the dependancy i was experiencing on
X	  an enviroment i didn't fully understand...now that I've solved the
X	  problem this analysis will service me in the future, and the general
X	  knowledge i've acquired should help me design around potential
X	  catastrophes like this; all of this applies particularily when i
X	  try to do some really heavy coding in C++)).
X
X
X
Xcompiling pitfalls:
X
X
XGENERAL:
X
Xlinker
X	VERBOSE NOALVS  both have no effect
X
X	SC SD
X
X	***	if both are set then _sprintf attempts to use (a4) base
X		relative addressing.  However base relative addressing
X		is not enabled, so the system crashes.
X
X		Presumably if the -y option is set in the program then
X		base relative addressing will magically become functional;
X		except that I'm not certain if the system has been given
X		space for _sprintf to use...which may mean that rather than
X		crashing right away it just trashes local memory for a while.
X
X		(we'll have to try -y, and SC, SD seperately and combined)
X
X	SC
X		WHEN REMOVED: (and -v -b0 -r are on)
X		- modifies the addresses accessed by (a4), however a4 is
X		  still not initialized.  Note that all of my code seems to
X		  be OK, its just the linked in system support code that is
X		  attempting to use the uninitialized a4.
X
X		- some symbols become indirect (less visible too)
X
X	SD
X		WHEN REMOVED:  (and -v -b0 -r are on)
X		- does fix problem of code trying to use (a4)
X		- code seems to call the wrong addresses or something weird
X		- freezes up and dies when trying to "findport"
X
X
X	NODEBUG  actually helps supply debug symbols, I don't know why...
X
X	ADDSYM	must have NODEBUG present, and if NODEBUG is set then
X		this must be present or the file won't be executable
X		(for some reason).
X
X	in general doesn't bitch as profluently if lcm.lib is left out.
X
Xcompiler
X
X	sometimes ABSEXECBASE is 0  - I don't know why, may be BLINKS fault
X
X	-r  = pc relative addressing (but not base relative???)
X	      seems to work when this is on.
X
X	-y  = reload a4 always, seems to make package fail, i'm not
X	      sure why, but i did notice that it did not set A4 as
X	      it promised, furthermore that A4 was (or became required).
X		(actually this is due to SC SD i think)
X
X	-b0 = does something, I'm not exactly sure what
X	      so far i haven't got it to work without -b0
X	      Ok -> -b is ON by default, thats why -b has no effect, and
X	      -b0 turns it off (base register relative mode addressing)
X
X	      -b0 definately makes it not require a4,
X	       ABSEXECBASE is wrong still however (a6).
X		(actually still requires A4 depending on SC SD setting)
X
X	-v = disable stack checking, does help (although I don't know why).
X
X	      interestingly enough the linker will fail unless this option
X	      is set.  The linker appears to be looking for some variables
X	      to give it position specific guidance if -v is on.
X
X	      IF ONLY -V is set then the program will fail when it
X	      trys to access relative to (a4).
X
X	-d = not important.  but to access debugging info in general:
X
X	     turn ADDSYM on in linker then:
X
X	     debug on...this works ok, to trap the handler run 2 copies of
X	     DB, do an 'al' on "cd device", then set a second 'al' on
X	     second DB once first one has trapped the 'cd', then quit
X	     the first one - the cd will run, and then try start up the
X	     device process, at which point the second db catches it, and
X	     if you are in the right directory it will load up the properly
X	     named symbol table.
X
X
X
X
XDIFFERENT ATTEMPTS:
X
Xone:
X	using -v -b0 -r	and SC SD ADDSYM
X
X	with my code:
X		FINDPORT = hang point		- doesn't reach debug window
X
X	without my code:
X		SPRINTF = hang point (missing (a4)) - gets as far as debug window
X
Xtwo:
X	using -v -b -r -y and ADDSYM SC SD  (may change code size, erratic result?)
X
X	with my code:
X		(it should crash at FINDPORT still)
X		(no purpose in trying if below crashes)
X
X	without my code:
X		(it should work ok)
X		actually crashes still, still missing (a4) at _sprintf
X
Xthree:
X	-v -r -y -b0 and ADDSYM SC SD ADDSYM
X
X	without my code:
X		still misses (a4) at sprintf
X
Xfour:
X	-v -b0, SC SD (without my code)
X
X	- works, doesn't try to access a4
X	- obviously the -r is forcing a base relative addressing on.
X	- and -y just doesn't ever work.
X	- notably some tasks in system at same time crash on exiting
X	- notably (a4) seems to point to something although its not used
X
Xfive:
X	-v -b0, SC SD ADDSYM (without my code)
X
X	- exactly same response as (four)
X	- (including types of crashes etc.  DEBUG doesn't seem to have bad fx)
X
Xsix:
X	-b0 SC SD ADDSYM (without my code)
X
X	- complains about missing link time stuff _StackPtr etc...
X
Xseven:
X	-v -b0, SC SD ADDSYM WITH-MY-CODE
X
X	crashes in this way:
X
X	_typetostr+12c
X		jsr _typetostr+18a
X			(sysbase is corrupt incidentally)
X			_pf+46
X			(crashes here, A4 is not initialized, yet required!)
X			(works fine from here on - if i force A4 to be valid)
X			_strupr+2
X			_dbinit
X	_typetostr+130
X
Xeight:
X	-v -b0 NO-MY-CODE, NO SC SC, JUST ADDSYMS
X
X	freezes up (at findport i believe) - forever.
X
Xnine:
X	-v -b0	....no blink options, none of my code
X
X	freezes up exactly same... (thus ADDSYM definately has no effect)
X
X	BLINK states that SC SD makes any object module that was compiled
X	with -b use it.  Even if I disable -b, blink is still going to
X	throw in some system modules using it if i set SC SD.  This is
X	why code can be referencing (a4) when its not enabled by compiler.
X
X	Interestingly enough I noticed that _pf+46 was not supposed to
X	fall through to _strupr+2, when recompiled without SC SD, this
X	may be another bug. (actually i believe this is because i was
X	using an old symbol table, as i just found out)
X
X	Altogether there are then these bugs I have seen:
X		1) use of (a4) illegally
X		2) bad init on SysBase  (probably not)
X		3) bad code (probably not actually)
X		4) freeze up waiting for port sig (bad sysbase?)
X
X	I have figured out:
X		1) kindof my fault, anyway I can avoid it
X		2) no idea? perhaps debugger is showing wrong address? (no see 3)
X		3) no idea? perhaps compiler bug? (is bad symbol table)
X		4) perhaps bad sysbase?
X
X	ok so bugs are:
X		1) (a4) use...fixed
X		4) freeze waiting for port -> createproc failed?
X
X	Notably the reason it doesn't run with my code, and crashes externals
X	is due to (a4) causing erratic crash behavior.  That is definately
X	fixed now.  It would be nice to have a proper A4 however, and possibly
X	i can try use the -y to force it on, or check some code from the
X	lattice support startup source to see how they do it properly.
X
X	unknown:
X		major is why should it freeze waiting for port sig if
X		the silly SC SD is not set? its gotta be a sysbase problem
X		cause sysbase was corrupt before, but maybe not needed.
X
X*** NEWS BULLETIN ***
X
X	the reason for bug #4 is very very bad....
X
X	Matt patched in a hunk of machine code such that no matter what
X	happened the BCPL<<C segment would hit approximately the right
X	area and get funneled off to where it belonged.
X
X	When I *don't* use a SMALLCODE SMALLDATA model, the operation
X	gets indexed through a JUMP TABLE!!!! and of course %78 of the
X	time it fucks up; the BCPL<<C being potentially off 2 in either
X	direction...at run time...
X
X	so - i know you're all waiting in breathless anticipation -
X
X	how do i solve it!!!?
X
X	maybe ARP has a createproc of its own... [ nope ]
X
X	hmmm...
X
X	[ TECHNICALLY THIS IS A BUG IN LATTICE 5.10 for permitting jump
X	  tables on non-longword aligned addresses - because surely DOS
X	  must load longword aligned? (now that i think about it) ]
X
X	i guess maybe what lattice does is just keep functions longword
X	aligned? (lets check) [ NOPE ]
X
X	[ this is probably why historically I've never been able to
X	  get CreateProc to work, actually when I first wanted to try
X	  a "neato" function like that I presumably didn't even cast
X	  the function pointer as a BCPL... ]
X
X	an easy way to check if the bug has been resolved is to
X	just look at _debugproc
X
X	- we basically must remove this call from the jump table...
X
X	- _builtin_ is a way  [ but its a mess! ]
X
X	- r may solve this...but lets really deal with it... [ NAH JUST DO IT ]
X
X	[ -r does not remove the jump table...therefore it doesn't help ]
X
X	- lets try base relative? (just once?)
X	  ok config is now:
X		-v -y -r,  ADDSYM, MY-CODE-ON	(and -b on by default)
X
X	- now SysBase is wrong *AND* debugproc still jumps via library ref!!!
X
X	  (why the fuck is SysBase wrong???)
X
X	  SysBase is wrong because - get this - its accessed off of (A4) now,
X	  and *(4(a4)) is something other than *(4(0))
X
X	  ABSEXECBASE became a *variable* with an ABSOLUTE delta offset of
X	  4 into the program data block; versus being ABSOLUTE in the vernacular
X	  logically correct sense...it has choosen to a be groovier, cooler
X	  kind of ABSEXECBASE than all those other squares...
X
X	  This is partly Matts fault because of his damned "void *"'s but
X	  lattice clearly made a compile mistake.
X
X	OK, lets just fix SysBase, and turn on SC SD to hopefully kill the
X	jump table...
X
X	*Y*E*S*  FUCK ME BLUE it worked....
X
X	Ok so the jump table is axed, and the only system reference that
X	I encountered is fixed...hopefully there aren't any other defines
X	like that...[ there appear not to be any ].
X
X	Ok now the bugs were:
X
X		1) ABSEXECBASE was referenced as an external void *; which
X		   to lattice could mean fucking anything...so naturally it
X		   became consumed into the compiler as a "variable" versus
X		   whatever it really was.
X
X		   Despite understanding it, i would still say this is a bug
X		   in lattice 5.10 (mostly).
X
X		2) The Jump Tables.  Should be longword aligned.  I would
X		   say that this is a bug; because its something unexpected
X		   and non-obvious to the casual user.  Especially in an
X		   Amiga Enviroment where BCPL DOS requires longword
X		   alignment.  So this is also a bug in Lattice 5.10.
X
X
X	*** the end of this 3 day debug session ***
X
X
XNow we have a bug where:
X
X	my readsome bad values, as compared to submitted values
X
X	possible problems:
X
X		1) void * (again) treated as not long sometimes?
X		   but then it should freak everywhere
X		2) structure passed on stack, not just address
X
XBUG:  DOSDevice isn't pointed right from M.L...
X
X	doesn't handle pc relative?
X
X
X *  DOSDEVICE.C 	V1.10	2 November 1987
X *
X *  EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C   PUBLIC DOMAIN.
X *
X *  By Matthew Dillon.
X *
X *  Debugging routines are disabled by simply attempting to open the
X *  file "debugoff", turned on again with "debugon".  No prefix may be
X *  attached to these names (you must be CD'd to TEST:).
X *
X *  See Documentation for a detailed discussion.
X *
X *  BUGS:
X *	Currently the only known bug is with the implementation of the
X *	RAM disk itself.  Specifically, if filehandle A is at the end of
X *	the file, and somebody appends to the file with another filehandle,
X *	B, filehandle A will get confused as to it's current position in
X *	the file.
X *
X *	I am probably not updating all the right timestamps.  This is
X *	easy to fix... All you have to do is fool with the floppy and
X *	see which datestamps get updated for certain operations.
X */
X
X
X/*
X *  Since this code might be called several times in a row without being
X *  unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
X *  for any global/static assignments that might be changed by running the
X *  code.
X */
X
X#ifdef BEVIRTUAL
Xint Virtual = 1;			/* default on!!! */
X#endif
X
XPROC	*DosProc;   /*	Our Process				    */
XDEVNODE	*DosNode;   /*	Our DOS node.. created by DOS for us	    */
XDEVLIST	*DevList;   /*	Device List structure for our volume node   */
X
Xvoid	*SysBase;   /*	EXEC library base			*/
XDOSLIB	*DOSBase;   /*	DOS library base for debug process	*/
XRAMFILE	RFRoot;     /*	Directory/File structure    (root node) */
XLIST	FHBase;     /*	Open Files				*/
XLIST	LCBase;     /*	Open Locks				*/
X
Xlong	TotalBytes; /*	total bytes of data in filesystem	*/
X
X
X		    /*	DEBUGGING			*/
XPORT *Dbport = 0;    /*	owned by the debug process	*/
XPORT *Dback = 0;    /*	owned by the DOS device driver	*/
Xshort DBDisable;
XMSG DummyMsg;	    /*	Dummy message that debug proc can use	*/
X
X/*
X *  Don't call the entry point main().  This way, if you make a mistake
X *  with the compile options you'll get a link error.
X */
X
Xvoid
Xnoname()
X{
X    register PACKET *packet;
X    register short   error;
X    MSG     *msg;
X    ubyte   notdone;
X    ubyte   buf[256];
X    void    *tmp;
X
X	ULONG *wowsers = 0x4;
X
X#ifdef BECOLORS
X	UWORD *colorptr = 0xdff180;
X	UWORD i;
X
X	for(i=0;i<300;i++) {
X		*colorptr = i;
X		};
X#endif
X
X    /*
X     *	Initialize all global variables.  SysBase MUST be initialized before
X     *	we can make Exec calls.  AbsExecBase is a library symbol
X     *	referencing absolute memory location 4.  The DOS library is opened
X     *	for the debug process only.
X     */
X
X    DBDisable = 1;				/*  Init. globals	*/
X    Dbport = Dback = NULL;
X    TotalBytes = 0;
X    SysBase = *(wowsers);  /* AbsExecBase; not anymore AH 91 */
X    DOSBase = OpenLibrary("dos.library",0);
X    DosProc = FindTask(NULL);
X    {
X	WaitPort(&DosProc->pr_MsgPort); 	/*  Get Startup Packet	*/
X	msg = GetMsg(&DosProc->pr_MsgPort);
X	packet = (PACKET *)msg->mn_Node.ln_Name;
X
X	/*
X	 *  Loading DosNode->dn_Task causes DOS *NOT* to startup a new
X	 *  instance of the device driver for every reference.	E.G. if
X	 *  you were writing a CON device you would want this field to
X	 *  be NULL.
X	 */
X
X	if (DOSBase) {
X	    DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
X	    register DEVLIST *dl = dosalloc(sizeof(DEVLIST));
X
X	    DosNode = BTOC(packet->dp_Arg3);
X
X	    /*
X	     *	Create Volume node and add to the device list.	This will
X	     *	cause the WORKBENCH to recognize us as a disk.	If we don't
X	     *	create a Volume node, Wb will not recognize us.  However,
X	     *	we are a RAM: disk, Volume node or not.
X	     */
X
X	    DevList = dl;
X	    dl->dl_Type = DLT_VOLUME;
X	    dl->dl_Task = &DosProc->pr_MsgPort;
X	    dl->dl_DiskType = ID_DOS_DISK;
X	    dl->dl_Name = (void *)DosNode->dn_Name;
X	    dl->dl_Next = di->di_DevInfo;
X	    di->di_DevInfo = (long)CTOB(dl);
X
X	    /*
X	     *	Set dn_Task field which tells DOS not to startup a new
X	     *	process on every reference.
X	     */
X
X	    DosNode->dn_Task = &DosProc->pr_MsgPort;
X	    packet->dp_Res1 = DOS_TRUE;
X	    packet->dp_Res2 = 0;
X	} else {			    /*	couldn't open dos.library   */
X	    packet->dp_Res1 = DOS_FALSE;
X	    returnpacket(packet);
X	    return;			    /*	exit process		    */
X	}
X	returnpacket(packet);
X    }
X
X    /*
X     *	Initialize debugging code
X     */
X
X    if(!DBDisable)dbinit();
X
X    /*	Initialize  RAM disk	*/
X
X    {
X	ubyte *ptr = BTOC(DosNode->dn_Name);
X	short len = *ptr;
X
X	NewList(&FHBase);			    /*	more globals	*/
X	NewList(&LCBase);
X	bzero(&RFRoot,sizeof(RFRoot));
X	RFRoot.type = FILE_DIR; 		    /*	root directory	*/
X	DateStamp(&RFRoot.date);		    /*	datestamp	*/
X	NewList(&RFRoot.list);			    /*	sub dirs	*/
X	RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /*	Root NAME	*/
X	bmov(ptr+1,RFRoot.name,len);
X	RFRoot.name[len] = 0;
X	dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
X    }
X
X    /*
X     *	Here begins the endless loop, waiting for requests over our
X     *	message port and executing them.  Since requests are sent over
X     *	our message port, this precludes being able to call DOS functions
X     *	ourselves (that is why the debugging routines are a separate process)
X     */
X
Xtop:
X    for (notdone = 1; notdone;) {
X	WaitPort(&DosProc->pr_MsgPort);
X
X#ifdef BECOLORS
X	for(i=0;i<300;i++) {
X		*colorptr = i;
X		};
X#endif
X
X	while (msg = GetMsg(&DosProc->pr_MsgPort)) {
X
X	    register ubyte *ptr;
X
X#ifdef BECOLORS
X	for(i=0;i<300;i++) {
X		*colorptr = i;
X		};
X#endif
X
X
X	    packet = (PACKET *)msg->mn_Node.ln_Name;
X	    packet->dp_Res1 = DOS_TRUE;
X	    packet->dp_Res2 = 0;
X	    error = 0;
X	    dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
X		packet->dp_Type,
X		packet->dp_Arg1, packet->dp_Arg2,
X		packet->dp_Arg3,
X		typetostr(packet->dp_Type)
X	    );
X
X
X/*#ifdef BEDEVICE*/
X
X	    switch(packet->dp_Type) {
X	    case ACTION_DIE:	    /*	attempt to die? 		    */
X		notdone = 0;	    /*	try to die			    */
X		break;
X	    case ACTION_OPENRW:     /*	FileHandle,Lock,Name	    Bool    */
X	    case ACTION_OPENOLD:    /*	FileHandle,Lock,Name	    Bool    */
X	    case ACTION_OPENNEW:    /*	FileHandle,Lock,Name	    Bool    */
X		{
X		    register RAMFILE *ramfile;
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg2); /* passive AH */
X		    char    *ptr;
X
X#ifdef BEVIRTUAL
X			ulong *virtual_device = 0;
X			ulong *virtual_fh = 0;
X			int	VirtualOpen = 0;
X#endif
X		    btos(packet->dp_Arg3,buf);
X		    dbprintf("'%s' ", buf);
X
X		    if (strcmp(buf,"debugoff") == 0)
X			{ DBDisable = 1; dbuninit(); }
X
X		    if (strcmp(buf,"debugon") == 0)
X			{ DBDisable = 0; dbinit(); }
X
X#ifdef BEVIRTUAL
X		    if (strcmp(buf,"virtualon") == 0)	/* debugging */
X			{
X			dbprintf("Virtual is on\n");
X			Virtual = 1;
X			}
X		    if (strcmp(buf,"virtualoff") == 0)
X			{
X			dbprintf("Virtual is off\n");
X			Virtual = 0;
X			}
X#endif
X
X/* is found? */
X		    if (ramfile = searchpath(&parentdir,buf,&ptr))
X		    {
X			if (ramfile->type == FILE_DIR)
X			   { error=ERROR_OBJECT_WRONG_TYPE; goto openbreak;}
X			if (ramfile->locks < 0)
X			   { error = ERROR_OBJECT_IN_USE; goto openbreak;}
X
X
X			if (packet->dp_Type == ACTION_OPENOLD)
X			{
X			    ++ramfile->locks;  /* fall thru to last bundle */
X#ifdef BEVIRTUAL
X			/*
X				this routine may abort rest of read attempt
X				note that i've checked above code to make
X				sure that nothing was initialized...so its
X				ok to just "step out" as it were AH Jan2291
X			*/
X
X			if (Virtual)
X			{
X			UBYTE	mtemp[VIBLEN+512];
X			DEVNODE	*MyDevNode;
X			/*VIB	*vib;*/
X			int	len;
X			len = myreadsome(mtemp,GetHead(&(ramfile->list)),VIBLEN+512);
X
X			if(!len)
X			{
X			dbprintf("not virtual file, treating as normal\n");
X			goto BENORMAL;
X			}
X
X			dbprintf("virtual: name to find is %s\n",mtemp+VIBLEN);
X
X			if(!(MyDevNode = FindHandler(mtemp+VIBLEN,DOSBase,mtemp+VIBLEN-20)))  /* and key */
X				{
X
X
X				/* ask user to insert disk please */
X				/* if user cancels then say: */
X				/* wait for reply, or search again myself */
X				/* if user does insert I would like to lock it */
X
X
X				dbprintf("device %s not found\n",mtemp+VIBLEN);
X				error = ERROR_OBJECT_NOT_FOUND;
X				--ramfile->locks;
X				goto openbreak;
X				}
X				else
X				{
X				PACKET	copypacket;
X
X/* gee i really hope that "buffer" is longword aligned!!!! */
X
X				UBYTE	buffer[512];
X
X				dbprintf("device %s was found %lx\n",mtemp+VIBLEN,MyDevNode->dn_Task);
X				virtual_device = MyDevNode->dn_Task;
X
X/*
Xarg1 = filehandle (to fill)
Xarg2 = lock (i'll zero this cause there is no parent - i pass entire path!)
Xarg3 = name (to find) [as a BSTR!]
X */
X				bmov(packet,&copypacket,sizeof(PACKET));
X				/* copypacket.dp_Arg1 = packet->dp_Arg1...*/
X
X				copypacket.dp_Arg2 = 0;	/* no parent lock! */
X
X				buffer[0] = strlen(mtemp+VIBLEN);
X				strncpy(&(buffer[1]),mtemp+VIBLEN,512-2);
X				copypacket.dp_Arg3 = CTOB(&(buffer[0]));
X
X				if(!(sendpacket(&copypacket,virtual_device)))
X					{
X					error = ERROR_OBJECT_NOT_FOUND;
X					--ramfile->locks;
X					goto openbreak;
X					}
X
X	dbprintf("001: packet sent!\n");
X
X				if(!copypacket.dp_Res1)
X					{
X					error = copypacket.dp_Res2;
X					--ramfile->locks;
X					goto openbreak;
X					}
X	dbprintf("002: no error\n");
X
X				/* get and stash the real file handle */
X				virtual_fh = ((FH *)BTOC(packet->dp_Arg1))->fh_Arg1;
X
X	dbprintf("file handle is %ld\n",virtual_fh);
X
X/*
Xi call other device, it takes over fh->arg1
Xi copy fh->arg1 away (may check if 0??? (no should leave same)
Xi call vir, it takes over fh->arg1 (again)
X */
X				VirtualOpen = 1;
X
X
X /*
Xopen
X	- copy the packet (or make a packet)
X	- set FileHandle to packet->arg1 (FileHandle)  (in copy also)
X	- replace name field of packet (packet->arg3)  (in copy only)
X	- set lock to 0				       (in copy only)
X	- submit packet to device
X	- wait for reply
X	- if failed then error = its error, goto openbreak;
X	- save for a moment DeviceID & FileHandle, set flag *special*
X	- at bottom of open: if *special* set then copy DeviceID/FileHandle in
X	  (just override parts of MYFH because it shouldn't be used outside
X	   of READ/WRITE [ SEEK/CLOSE ] and if a file handle is in virtual
X	   mode it stays that way, regardless of if the handler is in
X	   virtual mode or not.
X
X	*** general; we should think about the visibility of the virtual
X	    flag, and if it conflicts with re-entrant nature of these
X	    routines -> i think it does?)
X	    in which case we should have special packets instead
X */
X
X				}
X			} /* endif virtual */
XBENORMAL:
X#endif
X			goto success;
X			}
X			/* end of open_old */
X
X
X
X			else  /* packet request is NOT OPEN OLD */
X			{
X/* shared read only? */
X			    if (ramfile->locks > 0)
X				{
X				error = ERROR_OBJECT_IN_USE;
X				}
X				else
X				{
X				/* new open overtop old file */
X				if (packet->dp_Type == ACTION_OPENNEW) {
X					freedata(ramfile);
X					ramfile->protection = 0;
X					}
X				--ramfile->locks;
X			    	}
X			}
X		    }
X		    else  /* file is not found */
X		    {
X			if (!parentdir) {
X			    error = ERROR_INVALID_COMPONENT_NAME;
X			    goto openbreak;
X				}
X			if (packet->dp_Type == ACTION_OPENNEW) {
X			    ramfile = createramfile(parentdir, FILE_FILE, ptr);
X			    --ramfile->locks;
X				} else
X			  	{  error = ERROR_OBJECT_NOT_FOUND; }
X		    }
X
X			/* open new and open old success hit here */
X
Xsuccess:
X
X/*
X	OPEN_NEW was passed a "FileHandle"; in the file handle there is
X	a slot for matt to stick in his "myfh"; which he then does; when we
X	want to patch the requests on we will pass the supplied file handle
X	onto the next device, and then swap its sub_arg with ours, such that
X	we can handle any further requests....swapping as needed.
X */
X		    if (!error)
X		    {
X			register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR);
X			((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)mfh;
X			mfh->file = ramfile;
X			mfh->fentry = GetHead(&ramfile->list);
X
X#ifdef BEVIRTUAL
X			if(!VirtualOpen)
X			{
X				mfh->device = 0;	/* Virtual AHJan2391 */
X				mfh->devfh = 0;
X			}
X			else
X			{
X				mfh->device = virtual_device;
X				mfh->devfh  = virtual_fh;
X			}
X#endif
X			AddHead(&FHBase,mfh);
X		    }
X		}
Xopenbreak:
X		if (!GetHead(&FHBase) && !GetHead(&LCBase))
X		    notdone = 0;
X		break;
X	    case ACTION_READ:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
X		{
X		    register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
X		    register FENTRY *fen = mfh->fentry;
X		    register ubyte  *ptr = (ubyte *)packet->dp_Arg2;
X		    register long   left = packet->dp_Arg3;
X		    register long   scr;
X
X		if(andycode("read",mfh,packet))break;
X
X		    while (left && fen) {
X			scr = fen->bytes - mfh->offset;
X			if (left < scr) {
X			    bmov(fen->buf + mfh->offset, ptr, left);
X			    mfh->offset += left;
X			    left = 0;
X			} else {
X			    bmov(fen->buf + mfh->offset, ptr, scr);
X			    left -= scr;
X			    ptr += scr;
X			    mfh->base += fen->bytes;
X			    mfh->offset = 0;
X			    fen = NextNode(fen);
X			}
X		    }
X		    mfh->fentry = fen;
X		    packet->dp_Res1 = packet->dp_Arg3 - left;
X		}
X		break;
X	    case ACTION_WRITE:	    /*	 FHArg1,CPTRBuffer,Length   ActLength  */
X		{
X		    register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
X		    register FENTRY *fen = (FENTRY *)mfh->fentry;
X		    ubyte  *ptr = (ubyte *)packet->dp_Arg2;
X		    long   left = packet->dp_Arg3;
X		    long   scr;
X
X		if(andycode("write",mfh,packet)) break;
X
X		    /*
X		     *	Doesn't work right if multiple readers/appenders.
X		     */
X
X		    while (left) {
X			if (fen) {
X			    dbprintf("FEN: %ld  left: %ld\n", fen->bytes, left);
X			    scr = fen->bytes - mfh->offset;
X			    if (left < scr) {
X				if (fen->bytes < mfh->offset + left)
X				    dbprintf("PANIC! AWR0\n");
X				else
X				    bmov(ptr, fen->buf + mfh->offset, left);
X				mfh->offset += left;
X				left = 0;
X			    } else {
X				if (fen->bytes < mfh->offset + scr)
X				    dbprintf("PANIC! AWR1\n");
X				else
X				    bmov(ptr, fen->buf + mfh->offset, scr);
X				ptr += scr;
X				left -= scr;
X				mfh->base += fen->bytes;
X				mfh->offset = 0;
X				fen = NextNode(fen);
X			    }
X			} else {
X			    fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
X			    if (fen->buf = AllocMem(left, MEMF_PUBLIC)) {
X				fen->bytes = left;
X				mfh->file->bytes += left;
X				mfh->base  += left;
X				mfh->offset = 0;
X				TotalBytes += left;
X				AddTail(&mfh->file->list, fen);
X				dbprintf("NEWFEN: (%ld)\n", fen->bytes);
X				bmov(ptr, fen->buf, left);
X				left = 0;
X			    } else {
X				FreeMem(fen, sizeof(FENTRY));
X				dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left);
X				mfh->offset = 0;
X				break;
X			    }
X			    fen = NULL;     /*	cause append	*/
X			}
X		    }
X		    packet->dp_Res1 = packet->dp_Arg3 - left;
X		    mfh->fentry = fen;
X		}
X		break;
X	    case ACTION_CLOSE:	    /*	 FHArg1 		    Bool:TRUE  */
X		{
X		    register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
X		    register RAMFILE *file = mfh->file;
X
X		andycode("close",mfh,packet);
X
X		    Remove(mfh);
X		    FreeMem(mfh,sizeof(*mfh));
X		    if (--file->locks < 0)
X			file->locks = 0;
X		}
X		if (!GetHead(&FHBase) && !GetHead(&LCBase))
X		    notdone = 0;
X		break;
X	    case ACTION_SEEK:	    /*	 FHArg1,Position,Mode	    OldPosition*/
X		{
X		    register MYFH *mfh = (MYFH *)packet->dp_Arg1;
X		    register FENTRY *fen;
X		    register long absseek;
X
X		if(andycode("seek",mfh,packet))break;
X
X		    packet->dp_Res1 = mfh->base + mfh->offset;
X		    absseek = packet->dp_Arg2;
X		    if (packet->dp_Arg3 == 0)
X			absseek += mfh->base + mfh->offset;
X		    if (packet->dp_Arg3 == 1)
X			absseek = mfh->file->bytes + absseek;
X		    if (absseek < 0 || absseek > mfh->file->bytes) {
X			error = ERROR_SEEK_ERROR;
X			break;
X		    }
X		    mfh->base = mfh->offset = 0;
X
X		    /*
X		     *	Stupid way to do it but....
X		     */
X
X		    for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) {
X			if (mfh->base + fen->bytes > absseek) {
X			    mfh->offset = absseek - mfh->base;
X			    break;
X			}
X			mfh->base += fen->bytes;
X		    }
X		    mfh->fentry = fen;
X		}
X		break;
X	    /*
X	     *	This implementation sucks.  The right way to do it is with
X	     *	a hash table.  The directory must be searched for the file
X	     *	name, then the next entry retrieved.  If the next entry is
X	     *	NULL there are no more entries.  If the filename could not
X	     *	be found we return the first entry, if any.
X	     *
X	     *	You can't simply keep a pointer around to the next node
X	     *	because it can be moved or removed at any time.
X	     */
X
X	    case ACTION_EXAMINE_NEXT: /*   Lock,Fib		      Bool	 */
X		{
X		    register FIB *fib = BTOC(packet->dp_Arg2);
X		    register RAMFILE *dir = getlockfile(packet->dp_Arg1);
X		    register RAMFILE *file;
X			long	*blah;
X
X		    if (dir->type == FILE_FILE) {
X			error = ERROR_OBJECT_WRONG_TYPE;
X			break;
X		    }
X		    file = GetHead(&dir->list);
X
X		    blah = &(fib->fib_Comment[68]);
X			fib->fib_Comment[67] = 0;
X
X/*		if(fib->fib_DiskKey) { */
X		    if (*blah) {
X			register int len = *(ubyte *)fib->fib_FileName;
X			for (; file; file = NextNode(file)) {
X			    if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len))
X				break;
X			}
X			if (file)
X			    file = NextNode(file);
X			else
X			    file = GetHead(&dir->list);
X		    }
X			*blah = 1; 
X/*		    fib->fib_DiskKey = 1; */
X		    error = -1;
X		    if (!(tmp=file)) {
X			error = ERROR_NO_MORE_ENTRIES;
X			break;
X		    }
X		}
X		/*  fall through    */
X	    case ACTION_EXAMINE_OBJECT: /*   Lock,Fib			Bool	   */
X		{
X		    register FIB *fib;
X		    register RAMFILE *file;
X		    register RAMFILE *dummy;
X
X		    fib = BTOC(packet->dp_Arg2);
X		    if (error) {
X			file = tmp;	/*  fall through from above */
X		    } else {
X			file = getlockfile(packet->dp_Arg1);
X 
X			fib->fib_Comment[67] = 0;
X			fib->fib_Comment[68] = 0;
X			fib->fib_Comment[69] = 0;
X			fib->fib_Comment[70] = 0;
X			fib->fib_Comment[71] = 0;
X
X/*			fib->fib_DiskKey = 0; */
X		    }
X		    error = 0;
X		    fib->fib_DirEntryType = file->type;
X		    strcpy(fib->fib_FileName+1, file->name);
X		    fib->fib_FileName[0] = strlen(file->name);
X		    fib->fib_Protection = file->protection;
X		    fib->fib_EntryType = file->type;
X		{
X			long	*blah;
X			blah = (long *)(&(fib->fib_DiskKey));
X			*blah = (long *)file;
X				/* every file must have a unique KEY!!! */
X				/* AmigaDOS and I both use this scheme to
X				   make sure 2 files are not same instance */
X		}
X		    fib->fib_Size = file->bytes;
X		    fib->fib_NumBlocks = (fib->fib_Size / 512);
X		    fib->fib_Date = file->date;
X		    if (file->comment) {
X/*!!AH!*/		strncpy(fib->fib_Comment+1, file->comment,65);
X			fib->fib_Comment[0] = strlen(file->comment);
X		    } else {
X			fib->fib_Comment[0] = 0;
X		    }
X
X#ifdef BEVIRTUAL
X		if (Virtual && file->type < 0) {
X			VIB vib;
X			int x;  long *myptr;
X			x = myreadsome(&vib,GetHead(&(file->list)),VIBLEN);
X
X			dbprintf("coolo %ld %ld\n",x,VIBLEN);
X
X			if(!x)	{
X				dbprintf("examine: file is not virtual");
X				goto JUSTNORMAL;
X				}
X
X			fib->fib_Size = vib.Size;
X			fib->fib_NumBlocks = (fib->fib_Size / 512);
X
X	/* information that must be returned to the coalesce utility,
X	   external to the handler...just a checksum... */
X
X			fib->fib_Comment[67] = 0;
X			myptr = &(fib->fib_Comment[72]);
X			*(myptr) = VIRT;
X			*(myptr+1) = vib.CheckSum;
X
X
X			}
XJUSTNORMAL:
X#endif
X		}
X		break;
X	    case ACTION_INFO:	    /*	Lock, InfoData	  Bool:TRUE    */
X		tmp = BTOC(packet->dp_Arg2);
X		error = -1;
X		/*  fall through    */
X	    case ACTION_DISK_INFO:  /*	InfoData	  Bool:TRUE    */
X		{
X		    register INFODATA *id;
X
X		    /*
X		     *	Note:	id_NumBlocks is never 0, but only to get
X		     *	around a bug I found in my shell (where I divide
X		     *	by id_NumBlocks).  Other programs probably break
X		     *	as well.
X		     */
X
X		    (error) ? (id = tmp) : (id = BTOC(packet->dp_Arg1));
X		    error = 0;
X		    bzero(id, sizeof(*id));
X		    id->id_DiskState = ID_VALIDATED;
X		    id->id_NumBlocks	 = (TotalBytes >> 9) + 1;
X		    id->id_NumBlocksUsed = (TotalBytes >> 9) + 1;
X		    id->id_BytesPerBlock = 512;
X		    id->id_DiskType = ID_DOS_DISK;
X		    id->id_VolumeNode = (long)CTOB(DosNode);
X		    id->id_InUse = (long)GetHead(&LCBase);
X		}
X		break;
X	    case ACTION_PARENT:     /*	 Lock			    ParentLock */
X		{
X		    register RAMFILE *file = getlockfile(packet->dp_Arg1);
X		    if (file->type == FILE_FILE) {
X			error = ERROR_OBJECT_NOT_FOUND;
X			break;
X		    }
X		    if (file->locks < 0) {
X			error = ERROR_OBJECT_IN_USE;
X			break;
X		    }
X		    if (file->parent)
X			packet->dp_Res1 = (long)CTOB(ramlock(file->parent, ACCESS_READ));
X		    else
X			error = ERROR_OBJECT_NOT_FOUND;
X		}
X		break;
X	    case ACTION_DELETE_OBJECT: /*Lock,Name		    Bool       */
X		{
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
X		    RAMFILE *ramfile;
X
X		    btos(packet->dp_Arg2, buf);
X		    if (ramfile = searchpath(&parentdir,buf,NULL)) {
X			if (ramfile->locks || ramfile == &RFRoot) {
X			    error = ERROR_OBJECT_IN_USE;
X			    break;
X			}
X			if (ramfile->type == FILE_DIR) {
X			    if (GetHead(&ramfile->list))
X				error = ERROR_DIRECTORY_NOT_EMPTY;
X			} else {
X			    freedata(ramfile);
X			}
X			if (!error) {
X			    freeramfile(ramfile);
X			    DateStamp(&parentdir->date);
X			}
X		    } else {
X			if (!parentdir)
X			    error = ERROR_INVALID_COMPONENT_NAME;
X			else
X			    error = ERROR_OBJECT_NOT_FOUND;
X		    }
X		}
X		if (!GetHead(&FHBase) && !GetHead(&LCBase))
X		    notdone = 0;
X		break;
X	    case ACTION_CREATE_DIR: /*	 Lock,Name		    Lock       */
X		{
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
X		    RAMFILE *ramfile;
X		    char *ptr;
X
X		    btos(packet->dp_Arg2, buf);
X		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
X			error = ERROR_OBJECT_EXISTS;
X			break;
X		    }
X		    if (!parentdir) {
X			error = ERROR_INVALID_COMPONENT_NAME;
X			break;
X		    }
X		    ramfile = createramfile(parentdir, FILE_DIR, ptr);
X		    packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE));
X		}
X		break;
X	    case ACTION_LOCATE_OBJECT:	/*   Lock,Name,Mode		Lock	   */
X		{
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
X		    RAMFILE *ramfile;
X
X		    btos(packet->dp_Arg2, buf);
X		    dbprintf("'%s' %ld ", buf, packet->dp_Arg3);
X		    if (ramfile = searchpath(&parentdir,buf,NULL)) {
X			if (ramfile->locks < 0 || (ramfile->locks && packet->dp_Arg3 == ACCESS_WRITE)) {
X			    error = ERROR_OBJECT_IN_USE;
X			    break;
X			}
X			packet->dp_Res1 = (long)CTOB(ramlock(ramfile, packet->dp_Arg3));
X		    } else {
X			if (!parentdir)
X			    error = ERROR_INVALID_COMPONENT_NAME;
X			else
X			    error = ERROR_OBJECT_NOT_FOUND;
X		    }
X		}
X		break;
X	    case ACTION_COPY_DIR:   /*	 Lock,			    Lock       */
X		{
X		    register RAMFILE *ramfile = getlockfile(packet->dp_Arg1);
X		    if (ramfile->locks < 0)
X			error = ERROR_OBJECT_IN_USE;
X		    else
X			packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_READ));
X		}
X		break;
X	    case ACTION_FREE_LOCK:  /*	 Lock,			    Bool       */
X		if (packet->dp_Arg1);
X		    ramunlock(BTOC(packet->dp_Arg1));
X		if (!GetHead(&FHBase) && !GetHead(&LCBase))
X		    notdone = 0;
X		break;
X	    case ACTION_SET_PROTECT:/*	 -,Lock,Name,Mask	   Bool       */
X		{
X		    register RAMFILE *ramfile;
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
X		    char *ptr;
X
X		    btos(packet->dp_Arg3, buf);
X		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
X			ramfile->protection = packet->dp_Arg4;
X		    } else {
X			if (parentdir)
X			    error = ERROR_OBJECT_NOT_FOUND;
X			else
X			    error = ERROR_INVALID_COMPONENT_NAME;
X		    }
X		}
X		break;
X	    case ACTION_SET_COMMENT:/*	 -,Lock,Name,Comment	   Bool       */
X		{
X		    register RAMFILE *ramfile;
X		    RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
X		    char *ptr;
X
X		    btos(packet->dp_Arg3, buf);
X		    if (ramfile = searchpath(&parentdir,buf,&ptr)) {
X			btos(packet->dp_Arg4, buf);
X			if (ramfile->comment)
X			    FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
X			ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC);
X			strcpy(ramfile->comment, buf);
X		    } else {
X			if (parentdir)
X			    error = ERROR_OBJECT_NOT_FOUND;
X			else
X			    error = ERROR_INVALID_COMPONENT_NAME;
X		    }
X		}
X		break;
X	    case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName    Bool       */
X		{
X		    register RAMFILE *file1;
X		    RAMFILE *sourcedir = getlockfile(packet->dp_Arg1);
X		    RAMFILE *destdir   = getlockfile(packet->dp_Arg3);
X		    char *ptr;
X
X		    btos(packet->dp_Arg2,buf);
X		    dbprintf("\nRENAME '%s' (%ld)  ", buf, strlen(buf));
X		    if (file1 = searchpath(&sourcedir,buf,NULL)) {
X			btos(packet->dp_Arg4,buf);
X			dbprintf("TO '%s' (%ld)", buf, strlen(buf));
X			if (searchpath(&destdir,buf,&ptr)) {
X			    error = ERROR_OBJECT_EXISTS;
X			} else {
X			    if (destdir) {
X				if (file1 == destdir) { /* moving inside self */
X				    error = ERROR_OBJECT_IN_USE;
X				    break;
X				}
X				dbprintf("REN '%s' %ld", ptr, strlen(ptr));
X				DateStamp(&sourcedir->date);
X				DateStamp(&destdir->date);
X				/*FreeMem(file1->name, strlen(file1->name)+1);*/
X				Remove(file1);
X				file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC);
X				file1->parent = destdir;
X				strcpy(file1->name, ptr);
X				AddHead(&destdir->list, file1);
X			    } else {
X				error = ERROR_INVALID_COMPONENT_NAME;
X			    }
X			}
X		    } else {
X			if (sourcedir)
X			    error = ERROR_OBJECT_NOT_FOUND;
X			else
X			    error = ERROR_INVALID_COMPONENT_NAME;
X		    }
X		}
X		break;
X	    /*
X	     *	A few other packet types which we do not support
X	     */
X	    case ACTION_INHIBIT:    /*	 Bool			    Bool       */
X		/*  Return success for the hell of it	*/
X		break;
X	    case ACTION_RENAME_DISK:/*	 BSTR:NewName		    Bool       */
X	    case ACTION_MORECACHE:  /*	 #BufsToAdd		    Bool       */
X	    case ACTION_WAIT_CHAR:  /*	 Timeout, ticks 	    Bool       */
X	    case ACTION_FLUSH:	    /*	 writeout bufs, disk motor off	       */
X	    case ACTION_RAWMODE:    /*	 Bool(-1:RAW 0:CON)	    OldState   */
X	    default:
X		error = ERROR_ACTION_NOT_KNOWN;
X		break;
X	    }
X/*#endif*/
X
X	    if (packet) {
X		if (error) {
X		    dbprintf("ERR=%ld\n", error);
X		    packet->dp_Res1 = DOS_FALSE;
X		    packet->dp_Res2 = error;
X		} else {
X		    dbprintf("RES=%06lx\n", packet->dp_Res1);
X		}
X
X/* #ifdef BEDEVICE */
X		returnpacket(packet);
X/*#endif */
X	    }
Xforgotten:
X	}
X    }
X    dbprintf("Can we remove ourselves? ");
X    Delay(50);	    /*	I wanna even see the debug message! */
X    Forbid();
X    if (packetsqueued(DosProc) || GetHead(&FHBase) || GetHead(&LCBase)
X      || GetHead(&RFRoot.list)) {
X	Permit();
X	dbprintf(" ..  not yet!\n");
X	goto top;		/*  sorry... can't exit     */
X    }
X
X    /*
X     *	Causes a new process to be created on next reference
X     */
X
X    DosNode->dn_Task = FALSE;
X
X    /*
X     *	Remove Volume entry.  Since DOS uses singly linked lists, we
X     *	must (ugg) search it manually to find the link before our
X     *	Volume entry.
X     */
X
X    {
X	DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
X	register DEVLIST *dl;
X	register void *dlp;
X
X	dlp = &di->di_DevInfo;
X	for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next))
X	    dlp = &dl->dl_Next;
X	if (dl == DevList) {
X	    *(BPTR *)dlp = dl->dl_Next;
X	    dosfree(dl);
X	} else {
X	    dbprintf("****PANIC: Unable to find volume node\n");
X	}
X    }
X
X    /*
X     *	Remove debug process, closedown, fall of the end of the world
X     *	(which is how you kill yourself if a PROCESS.  A TASK would have
X     *	had to RemTask(NULL) itself).
X     */
X
X    dbuninit();
X    CloseLibrary(DOSBase);
X}
X
X
X
X/*
X	the way this works is as follows:
X
X	[ page 273 of Amiga Dos Tech Ref Man (lower of the 2 paragraphs) ]
X
X	there is the idea of a user "FileHandle" - different from "MYFH"
X	when you call _LVORead it takes the user FileHandle and extracts
X	from it FileHandle->Arg1.  Where Arg1 is "MYFH" - in this case
X	as Matt prosaically refers to it "My File Handle".  This is what
X	we receive here.  MYFH was created by our own OPEN command, and
X	thus is our own completely private entity; and all we ever see
X	during the read - actually all we ever see ever.
X
X	presumably the _LVOOpen command stashes MYFH into the user FileHandle
X	after calling us (on success).
X
X	anyway this is non obvious and the subtle difference between
X	user-FileHandle and Handler-FileHandle is not delinated whatsoever.
X
X
Xread/write
X	- (must ignore Virtual on/off flag)
X	- if *special* set only:
X	- extract device id (MYFH->dev), real file handle (MYFH->special)
X	- put real file-handle (MYFH->special) into packet->arg 1
X	- patch packet directly to real device (as per device id)
X	- later may copy packet, but then i have to send it and wait for rep
X
X */
X
X
Xandycode(name,mfh,packet)
Xchar *name;
XMYFH *mfh;
XPACKET  *packet;
X{
X
X#ifdef BEVIRTUAL
X		    if(mfh->device)
X		    {
X			PACKET	copypacket;
X			bmov(packet,&copypacket,sizeof(PACKET));
X			copypacket.dp_Arg1 = mfh->devfh;
X dbprintf("accessing %s\n",name);
X			if(!(sendpacket(&copypacket,mfh->device)))
X				{
X				dbprintf("Holy Shit Batman!\n");
X				}
X		 	packet->dp_Res1 = copypacket.dp_Res1;
X dbprintf("accessing done %s\n",name);
X			return(1);
X		    }
X			else return(0);
X#else
X	return(0);
X#endif
X
X}
X
X
X
X/*
X *  PACKET ROUTINES.	Dos Packets are in a rather strange format as you
X *  can see by this and how the PACKET structure is extracted in the
X *  GetMsg() of the main routine.
X */
X
Xvoid
Xreturnpacket(packet)
Xregister struct DosPacket *packet;
X{
X    register struct Message *mess;
X    register struct MsgPort *replyport;
X
X    replyport		     = packet->dp_Port;
X    mess		     = packet->dp_Link;
X    packet->dp_Port	     = &DosProc->pr_MsgPort;
X    mess->mn_Node.ln_Name    = (char *)packet;
X    mess->mn_Node.ln_Succ    = NULL;
X    mess->mn_Node.ln_Pred    = NULL;
X    PutMsg(replyport, mess);
X}
X
X/*
X *  Are there any packets queued to our device?
X */
X
Xpacketsqueued()
X{
X    return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
X	    (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
X}
X
X/*
X *  DOS MEMORY ROUTINES
X *
X *  DOS makes certain assumptions about LOCKS.	A lock must minimally be
X *  a FileLock structure, with additional private information after the
X *  FileLock structure.  The longword before the beginning of the structure
X *  must contain the length of structure + 4.
X *
X *  NOTE!!!!! The workbench does not follow the rules and assumes it can
X *  copy lock structures.  This means that if you want to be workbench
X *  compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
X */
X
Xvoid *
Xdosalloc(bytes)
Xregister ulong bytes;
X{
X    register ulong *ptr;
X
X    bytes += 4;
X    ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
X    *ptr = bytes;
X    return(ptr+1);
X}
X
Xdosfree(ptr)
Xregister ulong *ptr;
X{
X    --ptr;
X    FreeMem(ptr, *ptr);
X}
X
X/*
X *  Convert a BSTR into a normal string.. copying the string into buf.
X *  I use normal strings for internal storage, and convert back and forth
X *  when required.
X */
X
Xvoid
Xbtos(bstr,buf)
Xubyte *bstr;
Xubyte *buf;
X{
X    bstr = BTOC(bstr);
X    bmov(bstr+1,buf,*bstr);
X    buf[*bstr] = 0;
X}
X
X/*
X *  Some EXEC list handling routines not found in the EXEC library.
X */
X
Xvoid *
XNextNode(node)
XNODE *node;
X{
X    node = node->mln_Succ;
X    if (node->mln_Succ == NULL)
X	return(NULL);
X    return(node);
X}
X
Xvoid *
XGetHead(list)
XLIST *list;
X{
X    if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
X	return(list->mlh_Head);
X    return(NULL);
X}
X
X/*
X *  Compare two names which are at least n characters long each,
X *  ignoring case.
X */
X
Xnccmp(p1,p2,n)
Xregister ubyte *p1, *p2;
Xregister short n;
X{
X    while (--n >= 0) {
X	if ((p1[n]|0x20) != (p2[n]|0x20))
X	    return(0);
X    }
X    return(1);
X}
X
X/*
X *  Create a file or directory and link it into it's parent directory.
X */
X
XRAMFILE *
Xcreateramfile(parentdir, type, name)
XRAMFILE *parentdir;
Xchar *name;
X{
X    register RAMFILE *ramfile;
X
X    ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
X    AddTail(&parentdir->list, ramfile);
X    ramfile->parent = parentdir;
X    ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
X    strcpy(ramfile->name, name);
X    ramfile->type = type;
X    ramfile->protection = 0;
X    NewList(&ramfile->list);
X    DateStamp(&ramfile->date);
X    DateStamp(&ramfile->parent->date);
X    return(ramfile);
X}
X
X/*
X *  Free all data associated with a file
X */
X
Xvoid
Xfreedata(ramfile)
XRAMFILE *ramfile;
X{
X    FENTRY *fen;
X
X    TotalBytes -= ramfile->bytes;
X    while (fen = RemHead(&ramfile->list)) {
X	dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes);
X	FreeMem(fen->buf, fen->bytes);
X	FreeMem(fen, sizeof(*fen));
X    }
X    ramfile->bytes = 0;
X    DateStamp(&ramfile->date);
X    DateStamp(&ramfile->parent->date);
X}
X
X/*
X *  Unlink and remove a file.  Any data associated with the file or
X *  directory has already been freed up.
X */
X
Xvoid
Xfreeramfile(ramfile)
XRAMFILE *ramfile;
X{
X    Remove(ramfile);		/*  unlink from parent directory    */
X    if (ramfile->name)
X	FreeMem(ramfile->name,strlen(ramfile->name)+1);
X    if (ramfile->comment)
X	FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
X    FreeMem(ramfile,sizeof(*ramfile));
X}
X
X/*
X *  The lock function.	The file has already been checked to see if it
X *  is lockable given the mode.
X */
X
XLOCK *
Xramlock(ramfile, mode)
XRAMFILE *ramfile;
X{
X    LOCK *lock = dosalloc(sizeof(LOCK));
X    LOCKLINK *ln;
X
X    if (mode != ACCESS_WRITE)
X	mode = ACCESS_READ;
X    ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC);
X    AddHead(&LCBase,ln);
X    ln->lock = lock;
X    lock->fl_Link= (long)ln;
X    lock->fl_Key = (long)ramfile;
X    lock->fl_Access = mode;
X    lock->fl_Task = &DosProc->pr_MsgPort;
X    lock->fl_Volume = (BPTR)CTOB(DosNode);
X    if (mode == ACCESS_READ)
X	++ramfile->locks;
X    else
X	ramfile->locks = -1;
X    return(lock);
X}
X
Xvoid
Xramunlock(lock)
XLOCK *lock;
X{
X    RAMFILE *file = (RAMFILE *)lock->fl_Key;
X
X    Remove(lock->fl_Link);			/* unlink from list */
X    FreeMem(lock->fl_Link, sizeof(LOCKLINK));	/* free link node   */
X    if (lock->fl_Access == ACCESS_READ) 	/* undo lock effect */
X	--file->locks;
X    else
X	file->locks = 0;
X    dosfree(lock);				/* free lock	    */
X}
X
X/*
X *  GETLOCKFILE(bptrlock)
X *
X *  Return the RAMFILE entry (file or directory) associated with the
X *  given lock, which is passed as a BPTR.
X *
X *  According to the DOS spec, the only way a NULL lock will ever be
X *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
X *  In anycase, If a NULL lock is passed to me I simply assume it means
X *  the root directory of the RAM disk.
X */
X
XRAMFILE *
Xgetlockfile(lock)
Xvoid *lock;		/*  actually BPTR to LOCK */
X{
X    register LOCK *rl = BTOC(lock);
X
X    if (rl)
X	return((RAMFILE *)rl->fl_Key);
X    return(&RFRoot);
X}
X
X/*
X *  Search the specified path beginning at the specified directory.
X *  The directory pointer is updated to the directory containing the
X *  actual file.  Return the file node or NULL if not found.  If the
X *  path is illegal (an intermediate directory was not found), set *ppar
X *  to NULL and return NULL.
X *
X *  *ppar may also be set to NULL if the search path IS the root.
X *
X *  If pptr not NULL, Set *pptr to the final component in the path.
X */
X
XRAMFILE *
Xsearchpath(ppar,buf,pptr)
XRAMFILE **ppar;
Xchar *buf;
Xchar **pptr;
X{
X    RAMFILE *file = *ppar;
X    RAMFILE *srch;
X    short len;
X    char *ptr;
X
X    *ppar = NULL;
X    for (;*buf && file;) {
X	ptr = getpathelement(&buf,&len);
X	if (buf[0] == ':') {    /*  go to root          */
X	    ++buf;
X	    file = &RFRoot;
X	    continue;
X	}
X	if (*ptr == '/') {          /*  go back a directory */
X	    if (!file->parent) {    /*	no parent directory */
X		return(NULL);
X	    }
X	    file = file->parent;
X	    continue;
X	}
X	if (file->type == FILE_FILE)
X	    return(NULL);
X	for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
X	    if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
X		file = srch;	    /*	element found	    */
X		break;
X	    }
X	}
X	if (srch == NULL) {
X	    if (*buf == 0)	/*  Element not found.	If it was the final */
X		*ppar = file;	/*  element the parent directory is valid   */
X	    if (pptr)
X		*pptr = ptr;
X	    return(NULL);
X	}
X    }
X    if (pptr)
X	*pptr = ptr;
X    *ppar = file->parent;
X    return(file);
X}
X
X/*
X *  Return the next path element in the string.  The routine effectively
X *  removes any trailing '/'s, but treats ':' as part of the next component
X *  (i.e. ':' is checked and skipped in SEARCHPATH()).
X */
X
Xchar *
Xgetpathelement(pstr,plen)
Xchar **pstr;
Xshort *plen;
X{
X    char *base;
X    register char *ptr = *pstr;
X    register short len = 0;
X
X    if (*(base = ptr)) {
X	if (*ptr == '/') {
X	    ++ptr;
X	    ++len;
X	} else {
X	    while (*ptr && *ptr != '/' && *ptr != ':') {
X		++ptr;
X		++len;
X	    }
X	    if (*ptr == '/')
X		++ptr;
X	}
X    }
X    *pstr = ptr;
X    *plen = len;
X    return(base);
X}
X
X
Xchar *
Xtypetostr(ty)
X{
X    switch(ty) {
X    case ACTION_DIE:		return("DIE");
X    case ACTION_OPENRW: 	return("OPEN-RW");
X    case ACTION_OPENOLD:	return("OPEN-OLD");
X    case ACTION_OPENNEW:	return("OPEN-NEW");
X    case ACTION_READ:		return("READ");
X    case ACTION_WRITE:		return("WRITE");
X    case ACTION_CLOSE:		return("CLOSE");
X    case ACTION_SEEK:		return("SEEK");
X    case ACTION_EXAMINE_NEXT:	return("EXAMINE NEXT");
X    case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ");
X    case ACTION_INFO:		return("INFO");
X    case ACTION_DISK_INFO:	return("DISK INFO");
X    case ACTION_PARENT: 	return("PARENTDIR");
X    case ACTION_DELETE_OBJECT:	return("DELETE");
X    case ACTION_CREATE_DIR:	return("CREATEDIR");
X    case ACTION_LOCATE_OBJECT:	return("LOCK");
X    case ACTION_COPY_DIR:	return("DUPLOCK");
X    case ACTION_FREE_LOCK:	return("FREELOCK");
X    case ACTION_SET_PROTECT:	return("SETPROTECT");
X    case ACTION_SET_COMMENT:	return("SETCOMMENT");
X    case ACTION_RENAME_OBJECT:	return("RENAME");
X    case ACTION_INHIBIT:	return("INHIBIT");
X    case ACTION_RENAME_DISK:	return("RENAME DISK");
X    case ACTION_MORECACHE:	return("MORE CACHE");
X    case ACTION_WAIT_CHAR:	return("WAIT FOR CHAR");
X    case ACTION_FLUSH:		return("FLUSH");
X    case ACTION_RAWMODE:	return("RAWMODE");
X    default:			return("---------UNKNOWN-------");
X    }
X}
X
X
X/*
X *  DEBUGGING CODE.	You cannot make DOS library calls that access other
X *  devices from within a DOS device driver because they use the same
X *  message port as the driver.  If you need to make such calls you must
X *  create a port and construct the DOS messages yourself.  I do not
X *  do this.  To get debugging info out another PROCESS is created to which
X *  debugging messages can be sent.
X *
X *  You want the priority of the debug process to be larger than the
X *  priority of your DOS handler.  This is so if your DOS handler crashes
X *  you have a better idea of where it died from the debugging messages
X *  (remember that the two processes are asyncronous from each other).
X */
X
Xextern void debugproc();
X
Xdbinit()
X{
X	TASK *task = FindTask(NULL);
X
X    if (!Dbport) {
X
X	Dback = CreatePort(NULL,NULL);
X	if((CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096)))
X	{
X	WaitPort(Dback);		    /* handshake startup    */
X	GetMsg(Dback);			    /* remove dummy msg     */
X	dbprintf("Debugger running V1.10, 2 November 1987\n");
X	dbprintf("Works with WORKBENCH!\n");
X	}
X	else
X	DeletePort(Dback);
X
X	}
X}
X
Xdbuninit()
X{
X    MSG killmsg;
X
X    if (Dbport) {
X	killmsg.mn_Length = 0;	    /*	0 means die	    */
X	PutMsg(Dbport,&killmsg);
X	WaitPort(Dback);	    /*	He's dead jim!      */
X	GetMsg(Dback);
X	DeletePort(Dback);
X	Dbport = 0;
X
X	/*
X	 *  Since the debug process is running at a greater priority, I
X	 *  am pretty sure that it is completely removed
X	 *  before this task gets control again.  Still, it doesn't hurt...
X	 */
X
X	Delay(50);		    /*	ensure he's dead    */
X    }
X}
X
Xdbprintf(a,b,c,d,e,f,g,h,i,j)
X{
X    char buf[256];
X    MSG *msg;
X
X    if (Dbport && !DBDisable) {
X	sprintf(buf,a,b,c,d,e,f,g,h,i,j);
X	msg = AllocMem(sizeof(MSG)+strlen(buf)+1, MEMF_PUBLIC|MEMF_CLEAR);
X	msg->mn_Length = strlen(buf)+1;     /*	Length NEVER 0	*/
X	strcpy(msg+1,buf);
X	PutMsg(Dbport,msg);
X    }
X}
X
X/*
X *  BTW, the DOS library used by debugmain() was actually openned by
X *  the device driver.	Note: DummyMsg cannot be on debugmain()'s stack
X *  since debugmain() goes away on the final handshake.
X */
X
Xdebugmain()
X{
X    MSG *msg;
X    short len;
X    void *fh;
X
X    Dbport = CreatePort(NULL,NULL);
X    fh = Open("con:0/0/640/100/debugwindow", 1006);
X    PutMsg(Dback, &DummyMsg);
X    for (;;) {
X	WaitPort(Dbport);
X	msg = GetMsg(Dbport);
X	len = msg->mn_Length;
X	if (len == 0)
X	    break;
X	--len;			      /*  Fix length up   */
X	Write(fh, msg+1, len);
X	FreeMem(msg,sizeof(MSG)+len+1);
X    }
X    Close(fh);
X    DeletePort(Dbport);
X    PutMsg(Dback,&DummyMsg);	      /*  Kill handshake  */
X}
X
X
X
X#ifdef BEVIRTUAL
X
X#ifdef TESTING
X
X/*
X *  Patch Data I/O functions directly to owner device
X *
X */
X
X
Xvoid
Xpatchpacket2()
X{
X
X	for(;;)
X	{
X		for(x=0;x<9999;x++)
X		{
X			if(!getnextdev("name")) break;
X			if(dev->VolDays != mydev->VolDays)
X				continue;
X			else
X			{
X			/* We found the device, now patch thru to it */
X			/* AmigaDOS-TR p271: "AmigaDOS maintains all other fields" */
X			/* so I guess, being AmigaDOS, I have to be nice */
X/*
X			- copy the packet
X			- replace name with original full path name
X			- issue the packet
X			- wait for reply
X			- return reply to host
X */
X			}
X		}
X		/* if(!(Request("Insert Disk X")))break; else continue; */
X	}
X}
X
X
X	/* to send a packet:
X
X   struct MsgPort        *replyport;
X   replyport = (struct MsgPort *) CreatePort(NULL,0);
X   if(!replyport) return(0L);
X   PutMsg(pid,(struct Message *)packet);
X   WaitPort(replyport);
X   GetMsg(replyport); 
X   DeletePort(replyport); 
X
X	note that deviceproc does quite a handy job of requesting a name
X	perhaps it calls some semi-generic routine?
X
X	*/
X
X				{
X				register struct Message *mess;
X				register struct MsgPort *replyport;
X				mess = packet->dp_Link;
X				mess->mn_Node.ln_Name = (char *)packet;
X				mess->mn_Node.ln_Succ = NULL;
X				mess->mn_Node.ln_Pred = NULL;
X				PutMsg(MyDevNode->dn_Task,mess);
X				/* now forget about the packet completely! */
X			goto forgotten;
X			}
X
X
X
X#ifdef BETESTING
X/*********************************************************/
X	{
X	register struct Message *mmouess;
X	register struct MsgPort *replyport;
X	DEVNODE	*MyDevNode;
X	if(!(MyDevNode = FindHandler("scratch:",DOSBase)))  /* and key */
X		{ dbprintf("Device not found!\n"); }
X		else
X		{
X		mess = packet->dp_Link;
X		mess->mn_Node.ln_Name = (char *)packet;
X		mess->mn_Node.ln_Succ = NULL;
X		mess->mn_Node.ln_Pred = NULL;
X		PutMsg(MyDevNode->dn_Task,mess);
X		/* now forget about the packet completely! */
X		/*goto forgotten;*/
X		}
X	}
X/*********************************************************/
X#endif
X
X/*
X *	rather than replying to a packet; pass it off to its real
X *	destination, in such a way that the client feels like he is
X *	talking directly with the third party - this permits me to
X *	drop out of the process without having to handle to crosstalk.  AH
X
X	(this function is commented out for the moment in favour of the
X	 absolutely most robust solution [ see sendpacket below ]...later on
X	if this is stable i may turn this back on...its moderately faster
X	and mongo coolo.
X
X */
X
Xpatchpacket(packet,mdest)
Xregister struct DosPacket *packet;
Xregister struct MsgPort *mdest;
X{
X	register struct Message *mess;
X
X	mess			= packet->dp_Link;	/* address of packet */
X	/* packet->dp_Port */				/* originator same!!*/
X	mess->mn_Node.ln_Name	= (char *)packet;	/* my message */
X	mess->mn_Node.ln_Succ	= NULL;
X	mess->mn_Node.ln_Pred	= NULL;
X	PutMsg(mdest,mess);				/* don't wait!!! */
X}
X
X
X#endif TESTING
X
X
X
X/*
X *	send a packet to a third party, but act as a mediator
X *	intercepting the reply and returning with it to higher
X *	level routines which will act based on response.  AH
X *
X */
X
Xsendpacket(packet,dest)
Xregister struct DosPacket *packet;
Xregister struct MsgPort *dest;
X{
X	register struct Message *mess;
X	struct MsgPort *replyport;
X
X	replyport = (struct MsgPort *) CreatePort(NULL,0);
X	if(!replyport) return(0L);
X
X	mess			= packet->dp_Link;
X	packet->dp_Port		= replyport; /* comes back to me */
X	mess->mn_Node.ln_Name	= (char *)packet;
X	mess->mn_Node.ln_Succ	= NULL;
X	mess->mn_Node.ln_Pred	= NULL;
X	PutMsg(dest,mess);
X	WaitPort(replyport);
X	GetMsg(replyport);
X	DeletePort(replyport);
X	return(1);
X}
X
X
X
X
X/*
X *	read from my own ram disk.  AH
X */
X
Xint
Xmyreadsome(mptr,mfen,mleft) /* return total read */
Xubyte *mptr;	/* ptr = destination */
XFENTRY *mfen;	/* node list of bits of RAM */
Xint mleft;	/* desired amount */
X{
X	register long scr;  long *start = mptr;
X
X	int	mtotal;	/* always returns 0? bug */
X
X	mtotal = 0;
X
X	dbprintf("readsome left, fen->bytes %ld %ld\n",mleft,mfen->bytes);
X
X	while (mleft && mfen) {
X		scr = mfen->bytes;	/* bytes here */
X		if (mleft < scr) {	/* desired < available */
X			bmov(mfen->buf, mptr, mleft);
X			mtotal += mleft;
X			mleft = 0;	/* done! */
X			}
X			else {		/* move what I can, goto next node */
X			bmov(mfen->buf, mptr, scr);
X			mleft -= scr;	/* decrease desired */
X			mtotal += scr;
X			mptr += scr;	/* increase write dest */
X			mfen = NextNode(mfen);
X			}
X		}
X
X/* once i've got the file just check here to make sure its a virtual instance
X   normally I only read around a hundred bytes...so even if this isn't a virtual
X   file there isn't a tremendous wastage of overhead... */
X
X	if ( *start !=VIRT) return(0);
X
X	dbprintf("total is %ld\n",mtotal);
X
X	return(mtotal);
X}
X
X#endif
X
X
X
X#ifdef COMMENT
X
X/* Returned by Examine() and ExNext(), must be on a 4 byte boundary */
Xstruct FileInfoBlock {
X   LONG	  fib_DiskKey;
X   LONG	  fib_DirEntryType;  /* Type of Directory. If < 0, then a plain file.
X			      * If > 0 a directory */
X   char	  fib_FileName[108]; /* Null terminated. Max 30 chars used for now */
X   LONG	  fib_Protection;    /* bit mask of protection, rwxd are 3-0.	   */
X   LONG	  fib_EntryType;
X   LONG	  fib_Size;	     /* Number of bytes in file */
X   LONG	  fib_NumBlocks;     /* Number of blocks in file */
X   struct DateStamp fib_Date;/* Date file last changed */
X   char	  fib_Comment[80];  /* Null terminated comment associated with file */
X   char	  fib_Reserved[36];
X}; /* FileInfoBlock */
X#endif
X
X
X/*
X
X*	need to check for the key also
X-	need to put up a "please insert disk x":
X		this requestor should have a variable length timeout
X		this requestor should accept an alternate file path
X		this requestor should have button for "just use df0:"
X		cancel and accept will also be there
X		as well i must keep perusing the device list...
X*	need to imbed the checksum in returned fib (somewhere safe)
X*	if file is not virtual do i call myreadsome anyway??? (YES)
X-	any weaknesses in matts driver in general?
X-	a compile may fail if buffer is not longword aligned.
X	may want to have some kind of runtime, or half-compile time check!
X-	device driver should checksum itself, and checksum its files, being
X	a ram device and prone to external trashing
X-	matt screws around with the diskkey...he shouldn't do that...
X-	replace viblen with sizeofvib
X-	there *must* be a "bestupid" mode which checks with much less rigour
X-	when i find a devnode i should lock it (but how do i do that???)
X*	blocks used is buggy
X*	what if the vir: passes of a packet and the silly dest isn't there?
X	does vir: close its own copy????
X */
X
END_OF_FILE
if test 58572 -ne `wc -c <'virtual.c'`; then
    echo shar: \"'virtual.c'\" unpacked with wrong size!
fi
# end of 'virtual.c'
fi
echo shar: End of archive 4 \(of 4\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.misc.