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.