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,©packet,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(©packet,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,©packet,sizeof(PACKET));
X copypacket.dp_Arg1 = mfh->devfh;
X dbprintf("accessing %s\n",name);
X if(!(sendpacket(©packet,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.