Path: utzoo!utgpu!water!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!rutgers!iuvax!pur-ee!j.cc.purdue.edu!ain From: ain@j.cc.purdue.edu (Pat-bob White) Newsgroups: comp.sources.amiga Subject: PATH: (sources 1 of 1) Keywords: DOS, device, paths Message-ID: <7985@j.cc.purdue.edu> Date: 19 Oct 88 12:10:19 GMT Organization: PUCC Land, USA Lines: 1554 Approved: akl@j.cc.purdue.edu (Rob Tillotson) Submitted by: Rico Mariani Summary: Generalized file path searching implemented as a device Poster Boy: Rob Tillotson (akl@j.cc.purdue.edu) Archive Name: sources/amiga/volume5/pathdev.s.Z Tested NOTES: This is a mountable device driver that allows you to have search paths for any directory, and those paths may include drive names as well as volume names, thus fixing one of the most irritating limitations of the AmigaDOS PATH command. The source code originally came from a sample RAM disk driver by Matt Dillon, and the documentation for it is also included. I got a few mysterious gurus when using this device, but they occurred when I was trying to test its limits; it does what it is supposed to, but it seems to have a few minor difficulties dealing with boundary conditions. ======================================== # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # Makefile # bset.asm # device.c # dos.h # support.c # This archive created: Thu Oct 13 14:27:59 1988 # By: Rob Tillotson (Bored Students Anonymous) cat << \SHAR_EOF > Makefile CFLAGS= +BCDL +Iram:symbols.m OBJS= device.o bset.o support.o .c.o: shell -c grep -v DB <$*.c >ram:$*.c cc $(CFLAGS) -o $*.o ram:$*.c delete ram:$*.c all: ram:symbols.m $(OBJS) ln +Q $(OBJS) -lcl32 -O path-handler ram:symbols.m : copy dh0:include/symbols.m ram:symbols.m flush: cc +L +Iram:include/symbols.m flush.c -o ram:flush.o ln +Q ram:flush.o -lc32 SHAR_EOF cat << \SHAR_EOF > bset.asm ;BSET.ASM ;BZERO.ASM ; ; Uses longword operations if data is aligned on a longword boundry ; and the size is a mulitple of 4. Otherwise, uses byte operations. xdef _bset xdef _bzero _bzero clr.l D1 bra begin _bset move.b 15(A7),D1 ;12(A7)-> msb . . lsb (D1 = data) begin move.l 4(A7),A0 ;A0 = address move.l 8(A7),D0 ;D0 = byte count andi.b #3,11(A7) ;byte count on long word boundry? bne drop andi.b #3,7(A7) ;address on longword boundry? bne drop bra lwb loop move.b D1,(A0)+ ;BYTE SET LOOP drop dbf.w D0,loop ;remember, only effects lower word sub.l #$10000,D0 ;for buffers >65535 bpl loop ;branch to loop because D0.W now is FFFF rts lwb lsr.l #2,D0 ;byte count / 4 (longword chunks) tst.l D1 ;BZERO beq dropl move.b D1,14(A7) ;15(A7) already contains the byte move.w 14(A7),D1 ;D1 0..15 set swap D1 move.w 14(A7),D1 ;D1 16..31 set bra dropl loopl move.l D1,(A0)+ ;BYTE SET LOOP dropl dbf.w D0,loopl ;remember, only effects lower word sub.l #$10000,D0 ;for buffers >65535 bpl loopl ;branch to loop because D0.W now is FFFF rts SHAR_EOF cat << \SHAR_EOF > device.c /* * DOSDEVICE.C V1.10 2 November 1987 * * EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN. * * By Matthew Dillon. * * Debugging routines are disabled by simply attempting to open the * file "debugoff", turned on again with "debugon". No prefix may be * attached to these names (you must be CD'd to TEST:). * * See Documentation for a detailed discussion. * * BUGS: * Currently the only known bug is with the implementation of the * RAM disk itself. Specifically, if filehandle A is at the end of * the file, and somebody appends to the file with another filehandle, * B, filehandle A will get confused as to it's current position in * the file. * * I am probably not updating all the right timestamps. This is * easy to fix... All you have to do is fool with the floppy and * see which datestamps get updated for certain operations. */ #include "dos.h" /* * Since this code might be called several times in a row without being * unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!! This also goes * for any global/static assignments that might be changed by running the * code. */ PORT *DosPort; /* Our DOS port... this is slick... */ PROC *DosProc; /* Our Process */ DEVNODE *DosNode; /* Our DOS node.. created by DOS for us */ DEVLIST *DevList; /* Device List structure for our volume node */ void *SysBase; /* EXEC library base */ DOSLIB *DOSBase; /* DOS library base for debug process */ RAMFILE RFRoot; /* Directory/File structure (root node) */ LIST FHBase; /* Open Files */ LIST LCBase; /* Open Locks */ long TotalBytes; /* total bytes of data in filesystem */ /* DEBUGGING */ PORT *Dbport; /* owned by the debug process */ PORT *Dback; /* owned by the DOS device driver */ MSG DummyMsg; /* Dummy message that debug proc can use */ RAMFILE xpath; /* This is used in case of off device path */ char *buf1; /* This holds the translated path names */ char *volname; /* This is my volume name */ RAMFILE * checkoutpath(); RAMFILE * vsearchpath(); void *DeviceProc(); /* * Don't call the entry point main(). This way, if you make a mistake * with the compile options you'll get a link error. */ void noname() { register PACKET *packet; register short error; register ubyte *ptr; MSG *msg; ubyte notdone; ubyte buf[256]; void *tmp; /* * Initialize all global variables. SysBase MUST be initialized before * we can make Exec calls. AbsExecBase is a library symbol * referencing absolute memory location 4. The DOS library is openned * for the debug process only. */ Dbport = Dback = NULL; TotalBytes = 0; SysBase = AbsExecBase; DOSBase = OpenLibrary("dos.library",0); DosProc = FindTask(NULL); DosPort = (PORT *)AllocMem(sizeof(PORT), MEMF_CLEAR | MEMF_PUBLIC); DosPort->mp_Node.ln_Type = NT_MSGPORT; DosPort->mp_Node.ln_Name = "Dos Port"; DosPort->mp_Flags = PA_SIGNAL; DosPort->mp_SigBit = AllocSignal(-1); DosPort->mp_SigTask = FindTask(NULL); NewList(&DosPort->mp_MsgList); buf1 = AllocMem(512, MEMF_PUBLIC); { WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */ msg = GetMsg(&DosProc->pr_MsgPort); packet = (PACKET *)msg->mn_Node.ln_Name; /* * Loading DosNode->dn_Task causes DOS *NOT* to startup a new * instance of the device driver for every reference. E.G. if * you were writing a CON device you would want this field to * be NULL. */ if (DOSBase) { DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info); register DEVLIST *dl = dosalloc(sizeof(DEVLIST)); DosNode = BTOC(PArg3); /* * Create Volume node and add to the device list. This will * cause the WORKBENCH to recognize us as a disk. If we don't * create a Volume node, Wb will not recognize us. However, * we are a RAM: disk, Volume node or not. */ volname = AllocMem(32,MEMF_PUBLIC); volname[0] = 11; strcpy(volname+1,"Path Server"); DevList = dl; dl->dl_Type = DLT_VOLUME; dl->dl_Task = DosPort; dl->dl_DiskType = ID_DOS_DISK; dl->dl_Name = CTOB(volname); /* DosNode->dn_Name*/; dl->dl_Next = di->di_DevInfo; di->di_DevInfo = (long)CTOB(dl); /* * Set dn_Task field which tells DOS not to startup a new * process on every reference. */ DosNode->dn_Task = DosPort; PRes1 = DOS_TRUE; PRes2 = 0; } else { /* couldn't open dos.library */ PRes1 = DOS_FALSE; returnpacket(packet); return; /* exit process */ } returnpacket(packet); } /* * Initialize debugging code */ /*DB*/ dbinit(); /* Initialize RAM disk */ { ubyte *ptr = BTOC(DosNode->dn_Name); short len = *ptr; NewList(&FHBase); /* more globals */ NewList(&LCBase); bzero(&RFRoot,sizeof(RFRoot)); RFRoot.type = FILE_DIR; /* root directory */ DateStamp(&RFRoot.date); /* datestamp */ NewList(&RFRoot.list); /* sub dirs */ RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /* Root NAME */ bmov(ptr+1,RFRoot.name,len); RFRoot.name[len] = 0; /*DB*/ dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name); } /* * Here begins the endless loop, waiting for requests over our * message port and executing them. Since requests are sent over * our message port, this precludes being able to call DOS functions * ourselves (that is why the debugging routines are a separate process) */ WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */ msg = GetMsg(&DosProc->pr_MsgPort); notdone = 1; goto entry; top: for (notdone = 1; notdone;) { WaitPort(DosPort); while (msg = GetMsg(DosPort)) { entry: packet = (PACKET *)msg->mn_Node.ln_Name; PRes1 = DOS_TRUE; PRes2 = 0; error = 0; /*DB*/ dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ", /*DB*/ PType, PArg1, PArg2, PArg3, typetostr(PType) ); switch(PType) { case ACTION_DIE: /* attempt to die? */ notdone = 0; /* try to die */ break; case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */ case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */ case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3,buf); /*DB*/ dbprintf("'%s' ", buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { FH *p; if (p = Open(buf1,PType)) { bmov(BTOC(p),BTOC(PArg1),sizeof(FH)); FreeMem(BTOC(p),sizeof(FH)); } else error = IoErr(); goto openbreak; } if (ramfile->type == FILE_DIR) { error = ERROR_OBJECT_WRONG_TYPE; goto openbreak; } if (ramfile->locks < 0) { error = ERROR_OBJECT_IN_USE; goto openbreak; } if (PType == ACTION_OPENOLD) { ++ramfile->locks; } else { if (ramfile->locks > 0) { error = ERROR_OBJECT_IN_USE; } else { if (PType == ACTION_OPENNEW) { freedata(ramfile); ramfile->protection = 0; } --ramfile->locks; } } } else { if (!parentdir) { error = ERROR_INVALID_COMPONENT_NAME; goto openbreak; } if (PType == ACTION_OPENNEW) { ramfile = createramfile(parentdir,FILE_FILE,ptr); --ramfile->locks; } else { error = ERROR_OBJECT_NOT_FOUND; } } if (!error) { register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR); ((FH *)BTOC(PArg1))->fh_Arg1 = (long)mfh; mfh->file = ramfile; mfh->fentry = GetHead(&ramfile->list); AddHead(&FHBase,mfh); } } openbreak: if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen = mfh->fentry; register ubyte *ptr = (ubyte *)PArg2; register long left = PArg3; register long scr; while (left && fen) { scr = fen->bytes - mfh->offset; if (left < scr) { bmov(fen->buf + mfh->offset, ptr, left); mfh->offset += left; left = 0; } else { bmov(fen->buf + mfh->offset, ptr, scr); left -= scr; ptr += scr; mfh->base += fen->bytes; mfh->offset = 0; fen = NextNode(fen); } } mfh->fentry = fen; PRes1 = PArg3 - left; } break; case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen = (FENTRY *)mfh->fentry; ubyte *ptr = (ubyte *)PArg2; long left = PArg3; long scr; /* * Doesn't work right if multiple readers/appenders. */ while (left) { if (fen) { /*DB*/ dbprintf("FEN: %ld left: %ld\n",fen->bytes,left); scr = fen->bytes - mfh->offset; if (left < scr) { if (fen->bytes < mfh->offset + left) { /*DB*/ dbprintf("PANIC! AWR0\n"); } else bmov(ptr, fen->buf + mfh->offset, left); mfh->offset += left; left = 0; } else { if (fen->bytes < mfh->offset + scr) { /*DB*/ dbprintf("PANIC! AWR1\n"); } else bmov(ptr, fen->buf + mfh->offset, scr); ptr += scr; left -= scr; mfh->base += fen->bytes; mfh->offset = 0; fen = NextNode(fen); } } else { fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC); if (fen->buf = AllocMem(left, MEMF_PUBLIC)) { fen->bytes = left; mfh->file->bytes += left; mfh->base += left; mfh->offset = 0; TotalBytes += left; AddTail(&mfh->file->list, fen); /*DB*/ dbprintf("NEWFEN: (%ld)\n", fen->bytes); bmov(ptr, fen->buf, left); left = 0; } else { FreeMem(fen, sizeof(FENTRY)); /*DB*/ dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left); mfh->offset = 0; break; } fen = NULL; /* cause append */ } } PRes1 = PArg3 - left; mfh->fentry = fen; } break; case ACTION_CLOSE: /* FHArg1 Bool:TRUE */ { register MYFH *mfh = (MYFH *)PArg1; register RAMFILE *file = mfh->file; Remove(mfh); FreeMem(mfh,sizeof(*mfh)); if (--file->locks < 0) file->locks = 0; } if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition*/ { register MYFH *mfh = (MYFH *)PArg1; register FENTRY *fen; register long absseek; PRes1 = mfh->base + mfh->offset; absseek = PArg2; if (PArg3 == 0) absseek += mfh->base + mfh->offset; if (PArg3 == 1) absseek = mfh->file->bytes + absseek; if (absseek < 0 || absseek > mfh->file->bytes) { error = ERROR_SEEK_ERROR; break; } mfh->base = mfh->offset = 0; /* * Stupid way to do it but.... */ for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) { if (mfh->base + fen->bytes > absseek) { mfh->offset = absseek - mfh->base; break; } mfh->base += fen->bytes; } mfh->fentry = fen; } break; /* * This implementation sucks. The right way to do it is with * a hash table. The directory must be searched for the file * name, then the next entry retrieved. If the next entry is * NULL there are no more entries. If the filename could not * be found we return the first entry, if any. * * You can't simply keep a pointer around to the next node * because it can be moved or removed at any time. */ case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */ { register FIB *fib = BTOC(PArg2); register RAMFILE *dir = getlockfile(PArg1); register RAMFILE *file; if (dir->type == FILE_FILE) { error = ERROR_OBJECT_WRONG_TYPE; break; } file = GetHead(&dir->list); if (fib->fib_DiskKey) { register int len = *(ubyte *)fib->fib_FileName; for (; file; file = NextNode(file)) { if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len)) break; } if (file) file = NextNode(file); else file = GetHead(&dir->list); } fib->fib_DiskKey = 1; error = -1; if (!(tmp=file)) { error = ERROR_NO_MORE_ENTRIES; break; } } /* fall through */ case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */ { register FIB *fib; register RAMFILE *file; register RAMFILE *dummy; fib = BTOC(PArg2); if (error) { file = tmp; /* fall through from above */ } else { file = getlockfile(PArg1); fib->fib_DiskKey = 0; } error = 0; fib->fib_DirEntryType = file->type; strcpy(fib->fib_FileName+1, file->name); fib->fib_FileName[0] = strlen(file->name); fib->fib_Protection = file->protection; fib->fib_EntryType = NULL; fib->fib_Size = file->bytes; fib->fib_NumBlocks = file->bytes >> 9; fib->fib_Date = file->date; if (file->comment) { strcpy(fib->fib_Comment+1, file->comment); fib->fib_Comment[0] = strlen(file->comment); } else { fib->fib_Comment[0] = 0; } } break; case ACTION_INFO: /* Lock, InfoData Bool:TRUE */ tmp = BTOC(PArg2); error = -1; /* fall through */ case ACTION_DISK_INFO: /* InfoData Bool:TRUE */ { register INFODATA *id; /* * Note: id_NumBlocks is never 0, but only to get * around a bug I found in my shell (where I divide * by id_NumBlocks). Other programs probably break * as well. */ (error) ? (id = tmp) : (id = BTOC(PArg1)); error = 0; bzero(id, sizeof(*id)); id->id_DiskState = ID_VALIDATED; id->id_NumBlocks = (TotalBytes >> 9) + 1; id->id_NumBlocksUsed = (TotalBytes >> 9) + 1; id->id_BytesPerBlock = 512; id->id_DiskType = ID_DOS_DISK; id->id_VolumeNode = (long)CTOB(DosNode); id->id_InUse = (long)GetHead(&LCBase); } break; case ACTION_PARENT: /* Lock ParentLock */ { register RAMFILE *file = getlockfile(PArg1); /* if (file->type == FILE_FILE) { error = ERROR_OBJECT_NOT_FOUND; break; } */ if (file->locks < 0) { error = ERROR_OBJECT_IN_USE; break; } if (file->parent) PRes1 = (long)CTOB(ramlock(file->parent, ACCESS_READ)); else error = ERROR_OBJECT_NOT_FOUND; } break; case ACTION_DELETE_OBJECT: /*Lock,Name Bool */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; btos(PArg2, buf); if (ramfile = searchpath(&parentdir,buf,NULL)) { if (ramfile == &xpath) { if (!DeleteFile(buf1)) error = IoErr(); break; } if (ramfile->locks || ramfile == &RFRoot) { error = ERROR_OBJECT_IN_USE; break; } if (ramfile->type == FILE_DIR) { if (GetHead(&ramfile->list)) error = ERROR_DIRECTORY_NOT_EMPTY; } else { freedata(ramfile); } if (!error) { freeramfile(ramfile); DateStamp(&parentdir->date); } } else { if (!parentdir) error = ERROR_INVALID_COMPONENT_NAME; else error = ERROR_OBJECT_NOT_FOUND; } } if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_CREATE_DIR: /* Lock,Name Lock */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; char *ptr; btos(PArg2, buf); if (ramfile = vsearchpath(&parentdir,buf,&ptr)) { error = ERROR_OBJECT_EXISTS; break; } if (!parentdir) { error = ERROR_INVALID_COMPONENT_NAME; break; } ramfile = createramfile(parentdir, FILE_DIR, ptr); PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE)); } break; case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */ { RAMFILE *parentdir = getlockfile(PArg1); RAMFILE *ramfile; btos(PArg2, buf); /*DB*/ dbprintf("'%s' %ld ", buf, PArg3); if (ramfile = searchpath(&parentdir,buf,NULL)) { if (ramfile == &xpath) { PRes1 = Lock(buf1,PArg3); if (!PRes1) PRes2 = IoErr(); break; } if (ramfile->locks < 0 || (ramfile->locks && PArg3 == ACCESS_WRITE)) { error = ERROR_OBJECT_IN_USE; break; } PRes1 = (long)CTOB(ramlock(ramfile, PArg3)); } else { if (!parentdir) error = ERROR_INVALID_COMPONENT_NAME; else error = ERROR_OBJECT_NOT_FOUND; } } break; case ACTION_COPY_DIR: /* Lock, Lock */ { register RAMFILE *ramfile = getlockfile(PArg1); if (ramfile->locks < 0) error = ERROR_OBJECT_IN_USE; else PRes1 = (long)CTOB(ramlock(ramfile, ACCESS_READ)); } break; case ACTION_FREE_LOCK: /* Lock, Bool */ if (PArg1); ramunlock(BTOC(PArg1)); if (!GetHead(&FHBase) && !GetHead(&LCBase)) notdone = 0; break; case ACTION_SET_PROTECT:/* -,Lock,Name,Mask Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3, buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { if (!SetProtection(buf1,PArg4)) error = IoErr(); break; } ramfile->protection = PArg4; } else { if (parentdir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; case ACTION_SET_COMMENT:/* -,Lock,Name,Comment Bool */ { register RAMFILE *ramfile; RAMFILE *parentdir = getlockfile(PArg2); char *ptr; btos(PArg3, buf); if (ramfile = searchpath(&parentdir,buf,&ptr)) { if (ramfile == &xpath) { btos(PArg4,buf); if (!SetComment(buf1,buf)) error = IoErr(); break; } btos(PArg4, buf); if (ramfile->comment) FreeMem(ramfile->comment,strlen(ramfile->comment)+1); ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC); strcpy(ramfile->comment, buf); } else { if (parentdir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName Bool */ { register RAMFILE *file1; RAMFILE *sourcedir = getlockfile(PArg1); RAMFILE *destdir = getlockfile(PArg3); char *ptr; btos(PArg2,buf); /*DB*/ dbprintf("\nRENAME '%s' (%ld) ", buf, strlen(buf)); if (file1 = vsearchpath(&sourcedir,buf,NULL)) { btos(PArg4,buf); /*DB*/ dbprintf("TO '%s' (%ld)", buf, strlen(buf)); if (vsearchpath(&destdir,buf,&ptr)) { error = ERROR_OBJECT_EXISTS; } else { if (destdir) { if (file1 == destdir) { /* moving inside self */ error = ERROR_OBJECT_IN_USE; break; } /*DB*/ dbprintf("REN '%s' %ld", ptr, strlen(ptr)); DateStamp(&sourcedir->date); DateStamp(&destdir->date); Remove(file1); file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC); file1->parent = destdir; strcpy(file1->name, ptr); AddHead(&destdir->list, file1); } else { error = ERROR_INVALID_COMPONENT_NAME; } } } else { if (sourcedir) error = ERROR_OBJECT_NOT_FOUND; else error = ERROR_INVALID_COMPONENT_NAME; } } break; /* * A few other packet types which we do not support */ case ACTION_INHIBIT: /* Bool Bool */ /* Return success for the hell of it */ break; case ACTION_RENAME_DISK:/* BSTR:NewName Bool */ case ACTION_MORECACHE: /* #BufsToAdd Bool */ case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */ case ACTION_FLUSH: /* writeout bufs, disk motor off */ case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */ default: error = ERROR_ACTION_NOT_KNOWN; break; } if (packet) { if (error) { /*DB*/ dbprintf("ERR=%ld\n", error); PRes1 = DOS_FALSE; PRes2 = error; } else { /*DB*/ dbprintf("RES=%06lx\n", PRes1); } returnpacket(packet); } else { /*DB*/ dbprintf("NOREP\n"); } } } /*DB*/ dbprintf("Can we remove ourselves? "); /*DB*/ Delay(50); /* I wanna even see the debug message! */ Forbid(); if (packetsqueued() || GetHead(&FHBase) || GetHead(&LCBase) || GetHead(&RFRoot.list)) { Permit(); /*DB*/ dbprintf(" .. not yet!\n"); goto top; /* sorry... can't exit */ } /* * Causes a new process to be created on next reference */ DosNode->dn_Task = FALSE; /* * Remove Volume entry. Since DOS uses singly linked lists, we * must (ugg) search it manually to find the link before our * Volume entry. */ { DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info); register DEVLIST *dl; register void *dlp; dlp = &di->di_DevInfo; for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next)) dlp = &dl->dl_Next; if (dl == DevList) { *(BPTR *)dlp = dl->dl_Next; dosfree(dl); } else { /*DB*/ dbprintf("****PANIC: Unable to find volume node\n"); } } /* * Remove debug process, closedown, fall of the end of the world * (which is how you kill yourself if a PROCESS. A TASK would have * had to RemTask(NULL) itself). */ /*DB*/ dbuninit(); CloseLibrary(DOSBase); } /*DB*/ static FH *debugfh; /*DB*/ /*DB*/ dbinit() /*DB*/ { /*DB*/ debugfh = Open("con:0/0/640/150/debugwindow", 1006); /*DB*/ /*DB*/ } /*DB*/ /*DB*/ dbuninit() /*DB*/ { /*DB*/ Close(debugfh); /*DB*/ } /*DB*/ /*DB*/ dbprintf(a,b,c,d,e,f,g,h,i,j) /*DB*/ { /*DB*/ static char buf[256]; /*DB*/ /*DB*/ sprintf(buf,a,b,c,d,e,f,g,h,i,j); /*DB*/ Write(debugfh,buf,strlen(buf)); /*DB*/ /*DB*/ } SHAR_EOF cat << \SHAR_EOF > dos.h /* * DOS.H */ #ifdef NOTDEF #include "exec/types.h" #include "exec/memory.h" #include "libraries/dos.h" #include "libraries/dosextens.h" #include "libraries/filehandler.h" #endif NOTDEF /* * ACTIONS which do not exist in dosextens.h but which indeed exist on * the Amiga. */ #define ACTION_OPENRW 1004 #define ACTION_OPENOLD 1005 #define ACTION_OPENNEW 1006 #define ACTION_CLOSE 1007 #define ACTION_SEEK 1008 #define ACTION_RAWMODE 994 #define ACTION_MORECACHE 18 #define ACTION_FLUSH 27 #define CTOB(x) (void *)(((long)(x))>>2) /* BCPL conversion */ #define BTOC(x) (void *)(((long)(x))<<2) #define bmov(ss,dd,nn) CopyMem(ss,dd,nn) /* my habit */ #define DOS_FALSE 0 #define DOS_TRUE -1 #define RAMFILE struct _RAMFILE /* less restrictive typedefs */ #define FENTRY struct _FENTRY #define LOCKLINK struct _LL #define MYFH struct _MYFH typedef unsigned char ubyte; /* unsigned quantities */ typedef unsigned short uword; typedef unsigned long ulong; typedef struct Interrupt INTERRUPT; typedef struct Task TASK; typedef struct FileLock LOCK; /* See LOCKLINK */ typedef struct FileInfoBlock FIB; typedef struct DosPacket PACKET; typedef struct Process PROC; typedef struct DeviceNode DEVNODE; typedef struct DeviceList DEVLIST; typedef struct DosInfo DOSINFO; typedef struct RootNode ROOTNODE; typedef struct FileHandle FH; typedef struct MsgPort PORT; typedef struct Message MSG; typedef struct MinList LIST; typedef struct MinNode NODE; typedef struct DateStamp STAMP; typedef struct InfoData INFODATA; typedef struct DosLibrary DOSLIB; #define FILE_DIR 1 #define FILE_FILE -1 RAMFILE { NODE node; RAMFILE *parent; char *name; char *comment; short flags; short type; /* -1 = file, 1 = dir, 0 = dummy entry */ short locks; /* <0:exclusive 0:none >0:shared */ ulong protection; ulong bytes; LIST list; /* list of FENTRY's or RAMFILE's */ STAMP date; }; FENTRY { NODE node; ubyte *buf; ulong bytes; }; /* * We use this structure to link locks together in a list for internal * usage. I could have use the link field in the lock structure as a * real linked list, but didn't want to have to sequentially search the * list to remove a node. * * NOTE: You CANNOT simply extend the FileLock (LOCK) structure. Some * programs assume it is sizeof(LOCK) big and break. I found this out the * hard way. */ LOCKLINK { NODE node; LOCK *lock; }; MYFH { NODE node; RAMFILE *file; /* file header */ FENTRY *fentry; long base; /* base of FENTRY */ long offset; /* offset into FENTRY */ }; /* * (void *) in Aztec C means 'pointer to anything'. I use it * extensively. */ extern void *AbsExecBase; extern void *AllocMem(), *RemHead(), *CreatePort(), *GetMsg(); extern void *FindTask(), *Open(), *OpenLibrary(); extern void *dosalloc(), *NextNode(), *GetHead(); extern void freedata(), freeramfile(), ramunlock(), btos(), returnpacket(); extern LOCK *ramlock(); extern RAMFILE *searchpath(), *createramfile(), *getlockfile(); extern char *getpathelement(); extern char *typetostr(); #define PType (packet->dp_Type) #define PArg1 (packet->dp_Arg1) #define PArg2 (packet->dp_Arg2) #define PArg3 (packet->dp_Arg3) #define PArg4 (packet->dp_Arg4) #define PRes1 (packet->dp_Res1) #define PRes2 (packet->dp_Res2) SHAR_EOF cat << \SHAR_EOF > support.c #include "dos.h" extern PORT *DosPort; /* Our DOS port... this is slick... */ extern PROC *DosProc; /* Our Process */ extern DEVNODE *DosNode; /* Our DOS node.. created by DOS for us */ extern DEVLIST *DevList; /* Device List structure for our volume node */ extern void *SysBase; /* EXEC library base */ extern DOSLIB *DOSBase; /* DOS library base for debug process */ extern RAMFILE RFRoot; /* Directory/File structure (root node) */ extern LIST FHBase; /* Open Files */ extern LIST LCBase; /* Open Locks */ extern long TotalBytes; /* total bytes of data in filesystem */ extern RAMFILE xpath; /* This is used in case of off device path */ extern char *buf1; /* This holds the translated path names */ RAMFILE * checkoutpath(); RAMFILE * vsearchpath(); void *DeviceProc(); /* * PACKET ROUTINES. Dos Packets are in a rather strange format as you * can see by this and how the PACKET structure is extracted in the * GetMsg() of the main routine. */ void returnpacket(packet) register struct DosPacket *packet; { register struct Message *mess; register struct MsgPort *replyport; replyport = packet->dp_Port; mess = packet->dp_Link; packet->dp_Port = DosPort; mess->mn_Node.ln_Name = (char *)packet; mess->mn_Node.ln_Succ = NULL; mess->mn_Node.ln_Pred = NULL; PutMsg(replyport, mess); } /* * Are there any packets queued to our device? */ packetsqueued() { return ((void *)DosPort->mp_MsgList.lh_Head != (void *)DosPort->mp_MsgList.lh_Tail); } /* * DOS MEMORY ROUTINES * * DOS makes certain assumptions about LOCKS. A lock must minimally be * a FileLock structure, with additional private information after the * FileLock structure. The longword before the beginning of the structure * must contain the length of structure + 4. * * NOTE!!!!! The workbench does not follow the rules and assumes it can * copy lock structures. This means that if you want to be workbench * compatible, your lock structures must be EXACTLY sizeof(struct FileLock). */ void * dosalloc(bytes) register ulong bytes; { register ulong *ptr; bytes += 4; ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR); *ptr = bytes; return(ptr+1); } dosfree(ptr) register ulong *ptr; { --ptr; FreeMem(ptr, *ptr); } /* * Convert a BSTR into a normal string.. copying the string into buf. * I use normal strings for internal storage, and convert back and forth * when required. */ void btos(bstr,buf) ubyte *bstr; ubyte *buf; { bstr = BTOC(bstr); bmov(bstr+1,buf,*bstr); buf[*bstr] = 0; } /* * Some EXEC list handling routines not found in the EXEC library. */ void * NextNode(node) NODE *node; { node = node->mln_Succ; if (node->mln_Succ == NULL) return(NULL); return(node); } void * GetHead(list) LIST *list; { if ((void *)list->mlh_Head != (void *)&list->mlh_Tail) return(list->mlh_Head); return(NULL); } /* * Compare two names which are at least n characters long each, * ignoring case. */ nccmp(p1,p2,n) register ubyte *p1, *p2; register short n; { while (--n >= 0) { if ((p1[n]|0x20) != (p2[n]|0x20)) return(0); } return(1); } /* * Create a file or directory and link it into it's parent directory. */ RAMFILE * createramfile(parentdir, type, name) RAMFILE *parentdir; char *name; { register RAMFILE *ramfile; ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC); AddTail(&parentdir->list, ramfile); ramfile->parent = parentdir; ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC); strcpy(ramfile->name, name); ramfile->type = type; ramfile->protection = 0; NewList(&ramfile->list); DateStamp(&ramfile->date); DateStamp(&ramfile->parent->date); return(ramfile); } /* * Free all data associated with a file */ void freedata(ramfile) RAMFILE *ramfile; { FENTRY *fen; TotalBytes -= ramfile->bytes; while (fen = RemHead(&ramfile->list)) { /*DB*/ dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes); FreeMem(fen->buf, fen->bytes); FreeMem(fen, sizeof(*fen)); } ramfile->bytes = 0; DateStamp(&ramfile->date); DateStamp(&ramfile->parent->date); } /* * Unlink and remove a file. Any data associated with the file or * directory has already been freed up. */ void freeramfile(ramfile) RAMFILE *ramfile; { Remove(ramfile); /* unlink from parent directory */ if (ramfile->name) FreeMem(ramfile->name,strlen(ramfile->name)+1); if (ramfile->comment) FreeMem(ramfile->comment,strlen(ramfile->comment)+1); FreeMem(ramfile,sizeof(*ramfile)); } /* * The lock function. The file has already been checked to see if it * is lockable given the mode. */ LOCK * ramlock(ramfile, mode) RAMFILE *ramfile; { LOCK *lock = dosalloc(sizeof(LOCK)); LOCKLINK *ln; if (mode != ACCESS_WRITE) mode = ACCESS_READ; ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC); AddHead(&LCBase,ln); ln->lock = lock; lock->fl_Link= (long)ln; lock->fl_Key = (long)ramfile; lock->fl_Access = mode; lock->fl_Task = DosPort; lock->fl_Volume = (BPTR)CTOB(DosNode); if (mode == ACCESS_READ) ++ramfile->locks; else ramfile->locks = -1; return(lock); } void ramunlock(lock) LOCK *lock; { RAMFILE *file = (RAMFILE *)lock->fl_Key; Remove(lock->fl_Link); /* unlink from list */ FreeMem(lock->fl_Link, sizeof(LOCKLINK)); /* free link node */ if (lock->fl_Access == ACCESS_READ) /* undo lock effect */ --file->locks; else file->locks = 0; dosfree(lock); /* free lock */ } /* * GETLOCKFILE(bptrlock) * * Return the RAMFILE entry (file or directory) associated with the * given lock, which is passed as a BPTR. * * According to the DOS spec, the only way a NULL lock will ever be * passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure. * In anycase, If a NULL lock is passed to me I simply assume it means * the root directory of the RAM disk. */ RAMFILE * getlockfile(lock) void *lock; /* actually BPTR to LOCK */ { register LOCK *rl = BTOC(lock); if (rl) return((RAMFILE *)rl->fl_Key); return(&RFRoot); } /* * Search the specified path beginning at the specified directory. * The directory pointer is updated to the directory containing the * actual file. Return the file node or NULL if not found. If the * path is illegal (an intermediate directory was not found), set *ppar * to NULL and return NULL. * * *ppar may also be set to NULL if the search path IS the root. * * If pptr not NULL, Set *pptr to the final component in the path. */ char *rindex(); RAMFILE * searchpath(ppar,buf,pptr) RAMFILE **ppar; char *buf; char **pptr; { RAMFILE *file = *ppar; RAMFILE *srch; short len; char *ptr; ptr = rindex(buf,':'); if (ptr) buf = ptr+1; *ppar = NULL; for (;*buf && file;) { ptr = getpathelement(&buf,&len); if (*ptr == '/') { /* go back a directory */ if (!file->parent) { /* no parent directory */ return(NULL); } file = file->parent; continue; } if (file->type == FILE_FILE) return(checkoutpath(file,ptr)); for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) { if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) { file = srch; /* element found */ break; } } if (srch == NULL) { if (*buf == 0) /* Element not found. If it was the final */ *ppar = file; /* element the parent directory is valid */ if (pptr) *pptr = ptr; return(NULL); } } if (pptr) *pptr = ptr; *ppar = file->parent; return(file); } RAMFILE * vsearchpath(ppar,buf,pptr) RAMFILE **ppar; char *buf; char **pptr; { RAMFILE *file = *ppar; RAMFILE *srch; short len; char *ptr; ptr = rindex(buf,':'); /* this fixes up the colon treatment */ if (ptr) buf = ptr+1; *ppar = NULL; for (;*buf && file;) { ptr = getpathelement(&buf,&len); if (*ptr == '/') { /* go back a directory */ if (!file->parent) { /* no parent directory */ return(NULL); } file = file->parent; continue; } if (file->type == FILE_FILE) return(NULL); for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) { if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) { file = srch; /* element found */ break; } } if (srch == NULL) { if (*buf == 0) /* Element not found. If it was the final */ *ppar = file; /* element the parent directory is valid */ if (pptr) *pptr = ptr; return(NULL); } } if (pptr) *pptr = ptr; *ppar = file->parent; return(file); } typedef struct { FENTRY *fentry; long offset; } SFH; /* simple file handle */ RAMFILE * checkoutpath(file,path) RAMFILE *file; char *path; { SFH fh; int lck; fh.fentry = GetHead(&file->list); fh.offset = 0; while (getnexttry(&fh,buf1)) { strcat(buf1,path); if (DeviceProc(buf1) == DosPort) { /* refers to us.. ignore */ continue; } if (!index(buf1,':')) { /* not an absolute path */ continue; } if (lck = Lock(buf1,ACCESS_READ)) { UnLock(lck); return(&xpath); } } return(0); } getnexttry(fh,buf) register SFH *fh; register char *buf; { register int i; register FENTRY *fe; fe = fh->fentry; i = fh->offset; for (fe = fh->fentry; fe ; fe = NextNode(fe)) { for (; ibytes; i++) { *buf = fe->buf[i]; if (*buf == '\n') { *buf = 0; fh->fentry = fe; fh->offset = ++i; return(1); } buf++; } i=0; } return(0); } /* * Return the next path element in the string. The routine effectively * removes any trailing '/'s, but treats ':' as part of the next component * (i.e. ':' is checked and skipped in SEARCHPATH()). */ char * getpathelement(pstr,plen) char **pstr; short *plen; { char *base; register char *ptr = *pstr; register short len = 0; if (*(base = ptr)) { if (*ptr == '/') { ++ptr; ++len; } else { while (*ptr && *ptr != '/') { ++ptr; ++len; } if (*ptr == '/') ++ptr; } } *pstr = ptr; *plen = len; return(base); } char * typetostr(ty) { switch(ty) { case ACTION_DIE: return("DIE"); case ACTION_OPENRW: return("OPEN-RW"); case ACTION_OPENOLD: return("OPEN-OLD"); case ACTION_OPENNEW: return("OPEN-NEW"); case ACTION_READ: return("READ"); case ACTION_WRITE: return("WRITE"); case ACTION_CLOSE: return("CLOSE"); case ACTION_SEEK: return("SEEK"); case ACTION_EXAMINE_NEXT: return("EXAMINE NEXT"); case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ"); case ACTION_INFO: return("INFO"); case ACTION_DISK_INFO: return("DISK INFO"); case ACTION_PARENT: return("PARENTDIR"); case ACTION_DELETE_OBJECT: return("DELETE"); case ACTION_CREATE_DIR: return("CREATEDIR"); case ACTION_LOCATE_OBJECT: return("LOCK"); case ACTION_COPY_DIR: return("DUPLOCK"); case ACTION_FREE_LOCK: return("FREELOCK"); case ACTION_SET_PROTECT: return("SETPROTECT"); case ACTION_SET_COMMENT: return("SETCOMMENT"); case ACTION_RENAME_OBJECT: return("RENAME"); case ACTION_INHIBIT: return("INHIBIT"); case ACTION_RENAME_DISK: return("RENAME DISK"); case ACTION_MORECACHE: return("MORE CACHE"); case ACTION_WAIT_CHAR: return("WAIT FOR CHAR"); case ACTION_FLUSH: return("FLUSH"); case ACTION_RAWMODE: return("RAWMODE"); default: return("---------UNKNOWN-------"); } } SHAR_EOF # End of shell archive exit 0