qix@ihlpa.UUCP (Ed Puckett) (07/08/87)
Here is part 1 of 2 of the source to Ed Puckett's pipe handler. This is the latest version including the bug fix he posted to the net. Binaries available in comp.binaries.amiga. # 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 # Xshar: Extended Shell Archiver. # This is part 1 out of 2. # This archive created: Wed Jul 8 01:44:10 1987 # By: Craig Norborg (Purdue University Computing Center) # Run the following text with /bin/sh to create: # Makefile # README # cc # loader.asm # pipe-handler.c # pipebuf.c # pipecreate.h # pipedebug.c # pipedebug.h # pipedir.c # pipesched.h # prelude_Mount cat << \SHAR_EOF > Makefile # remove the -DDEBUG from CFLAGS if not debugging CFLAGS = # -DDEBUG # set the DEBUG variables to null strings if not debugging DEBUG_H = # pipedebug.h DEBUG_O = # pipedebug.o PIPEHDR = pipe-handler.h pipelists.h pipename.h pipebuf.h \ pipecreate.h pipesched.h pipedir.h OBJ = pipe-handler.o pipelists.o pipename.o pipebuf.o \ pipecreate.o pipesched.o pipedir.o mount : loader_mount handler : loader_pipe-handler obj : $(OBJ) $(DEBUG_O) prelude_mount : prelude_pipe-handler Copy prelude_pipe-handler L:pipe-handler Copy prelude_Mount DEVS:Mountlist Mount P: loader_mount : loader loader_pipe-handler Copy pipe-handler-loader L: Copy loader_pipe-handler L:pipe-handler Copy loader_Mount DEVS:Mountlist Mount P: prelude_pipe-handler : prelude.o $(OBJ) $(DEBUG_O) BLink FROM prelude.o $(OBJ) $(DEBUG_O) \ TO prelude_pipe-handler \ LIBRARY CLIB:lc.lib CLIB:amiga.lib loader_pipe-handler : $(OBJ) $(DEBUG_O) BLink FROM $(OBJ) $(DEBUG_O) \ TO loader_pipe-handler \ LIBRARY CLIB:lc.lib CLIB:amiga.lib prelude : prelude.o prelude.o : prelude.asm Assem prelude -o prelude.o loader : loader.o BLink FROM loader.o TO pipe-handler-loader loader.o : loader.asm Assem loader.asm -i :include -o loader.o -c w100000 pipedebug.o : pipedebug.h pipedebug.c EXECUTE cc pipedebug $(CFLAGS) pipe-handler.o : $(PIPEHDR) pipe-handler.c $(DEBUG_H) EXECUTE cc pipe-handler $(CFLAGS) pipelists.o : pipelists.h pipelists.c EXECUTE cc pipelists $(CFLAGS) pipename.o : pipe-handler.h pipelists.h pipename.h pipebuf.h pipename.o : pipecreate.h pipesched.h pipename.o : pipename.c EXECUTE cc pipename $(CFLAGS) pipebuf.o : pipe-handler.h pipelists.h pipename.h pipebuf.h pipebuf.o : pipecreate.h pipesched.h pipebuf.o : pipebuf.c EXECUTE cc pipebuf $(CFLAGS) pipecreate.o : $(PIPEHDR) pipecreate.c $(DEBUG_H) EXECUTE cc pipecreate $(CFLAGS) pipesched.o : $(PIPEHDR) pipesched.c $(DEBUG_H) EXECUTE cc pipesched $(CFLAGS) pipedir.o : $(PIPEHDR) pipedir.c EXECUTE cc pipedir $(CFLAGS) SHAR_EOF cat << \SHAR_EOF > README README file for pipe-handler (version 1.2 13-Jun-87) ==================================================== This program and source are freely distributable, provided the file headers remain intact (i.e., my name is on them!!!). Ed Puckett accepts no responsibility for others' use of this program. ...but you shouldn't have any problems! WHAT IS THIS? ------------- Pipe-handler is an AmigaDOS device. It supports OPEN, CLOSE, READ and WRITE (of course), and also LOCK, EXAMINE, EXNEXT. Therefore, you can CD to the handler, use Dir and List on it, and ASSIGN to it as well as to individual pipes in it. Here is a complete list of the packet types it supports: MODE_READWRITE ACTION_FINDINPUT (syn: MODE_READONLY, MODE_OLDFILE) ACTION_FINDOUTPUT (syn: MODE_NEWFILE) ACTION_END ACTION_READ ACTION_WRITE ACTION_LOCATE_OBJECT ACTION_COPY_DIR ACTION_FREE_LOCK ACTION_EXAMINE_OBJECT ACTION_EXAMINE_NEXT ACTION_PARENT You cannot Seek() pipes nor can you create pipe subdirectories. INSTALLATION ------------ 1. Perform the following: <uudecode phl.uue to produce pipe-handler-l> <uudecode ph.uue to produce pipe-handler> Copy pipe-handler-l L:pipe-handler-loader Copy pipe-handler L:pipe-handler 2. Add to S:Startup-Sequence (do not include !'s - they denote start of line): !Mount P: !Dir >NIL: P: 3. Add to DEVS:Mountlist (do not include !'s - they denote start of line): !P: Handler = L:pipe-handler-loader ! Stacksize = 3000 ! Priority = 5 !# 4. Reboot NOTES ON INSTALLATION --------------------- * The handler, once installed, requires approximately 18k of memory. This memory cannot be reclaimed (unless you reboot and do not load the handler). Each pipe requires additional memory while it exists (its buffer size + about 100 bytes or so). * You can skip the reboot, and just perform the "Mount" and "Dir" from the startup-sequence manually. * After the "Dir", the pipe-handler is loaded into the system, and the files "L:pipe-handler-loader" and "L:pipe-handler" will not be accessed until the next reboot. This means you may remove them from L: if you want (until next reboot). I do this because I copy L: into Ram:. * TO CHANGE THE HANDLER NAME: change "P:" to whatever you want (e.g., "PIPE:") in the following 2 files: DEVS:Mountlist (1 occurrence) S:Startup-Sequence (2 occurrences) * Feel free to shorten or otherwise change the names "pipe-handler-loader" and "pipe-handler". Just be sure to reflect those changes in "S:Startup-Sequence" and "DEVS:Mountlist". TAPS ---- The handler also supports "taps". These are essentially tees off of the pipe, and can specify any destination to which a copy of the pipe's stream is to be sent. One interesting application of this is tapping to a CON: window; you can then see what is going through the pipe, and you can also stop pipe throughput by typing a character into this window. Taps can also be other pipes. For an interesting demo of taps, EXECUTE the file "tap_demo". All pipe I/O is asynchronous, so you will not be able to lock up the handler by stopping one of its pipes. A single reader / single writer discipline in enforced. Pipe buffers are dynamically allocated. A pipe is removed from memory when all openers have closed it and it is empty. The size of a pipe buffer may be specified as part of its name. Otherwise, pipe buffers have a default size of 4096 bytes. Pipes behave in most respects like ordinary files. Some differences follow: Pipes block for writing (i.e., the write request is suspended) when the pipe's buffer is full, and block for reading when the pipe's buffer is empty. Thus, pipes are sort of like bounded ram: files. EOF is returned for reading when the pipe's buffer is empty and no process has the pipe open for writing. NAME SYNTAX ----------- In addition to the pipe's identifier, its name can specify its size and a tap. The syntax is name[/size][/[tapname]] where the parts enclosed in "[]" are optional. The size must begin with a digit in the range 0-9. If first digit is not "0", the size is interpreted as decimal. If the size begins with "0x", it is interpreted as hexadecimal. Otherwise, if the size begins with "0" but not "0x", it is interpreted as octal. If an empty tapname is specified, the default tap CON:10/15/300/70/name is used (where "name" is the pipe's name given). EXAMPLES OF PIPE NAMES ---------------------- The following assume that the pipe-handler is mounted as device P: P:x Opens a pipe named "x" with buffer size 4096 P:x/100 Opens a pipe named "x" with buffer size 100 P:hold/ Opens a pipe named "hold" with buffer size 4096, and also opens a window which displays the data which passes through the pipe. P:xyzzy/plugh Opens a pipe named "xyzzy" with buffer size 4096, and also directs the data passing through into the file "plugh". (Note that taps may be specified without specifying a size.) P:thru/0x40/ram:thru-log Opens a pipe named "thru" with buffer size 64 (decimal), and also directs the data passing through the pipe into the file "ram:thru-log". WHAT IS THIS SILLY "LOADER" FILE? --------------------------------- According to _The_AmigaDOS_Manual_ (Bantam Books, Feb 1986), page 291: If you write your device handler in C, you cannot use the automatic load and process creation provided by the kernel. In this case you must load the code yourself . . . . Well, I know others have gotten around this, and I did, too. The "prelude" version of the handler (see the Makefile) does it all with one file. However, I noticed that doing it this way, the handler would take about 3 seconds to "Mount" (after first access to it). This made me very nervous - visions of wild linking through memory, etc. The loader version mounts almost immediately. Anyway, due to my (possibly unfounded) paranoia, I instead use the BCPL-like assembly module "pipe-handler-loader" which LoadSeg()'s pipe-handler. There are undoubtedly better ways of handling this, but this works and, for me, it is not too annoying to put up with the extra file. COMPILATION ----------- The supplied C source files were compiled with Lattice v3.03. The assembly programs were assembled using the Commodore Assembler. See the Makefile for information on creating a debugging version (this puts up a window and tells you what packets are received by the handler - fun!). Basically, you just compile with a -DDEBUG option and link in the debugging modules. The Makefile will make either a "prelude" version (no "loader file) or a "loader" version (the default). I use an EXECUTE file "cc" to driver the compiler. It is supplied. INQUIRIES / COMMENTS / SUGGESTIONS ---------------------------------- Ed Puckett US Mail: MIT Branch PO - PO Box 61 Cambridge, MA 02139 E Mail: ...!ihnp4!mit-eddie!mit-oz!qix SHAR_EOF cat << \SHAR_EOF > cc .KEY xxx_file,opt1,opt2,opt3 ; Compile a C program Version 4.00 ; Works with Lattice version 3.03 and above IF NOT EXISTS <xxx_file>.c ECHO "File <xxx_file>.c does not exist." SKIP END ENDIF ECHO "-- compiling...<xxx_file>.c" IF NOT EXISTS "Ram:cctempdir" MAKEDIR Ram:cctempdir ENDIF :c/LC1 <opt1> <opt2> <opt3> -oRam:cctempdir/cctemp.q -i:include/ -i:include/lattice/ <xxx_file> IF NOT EXISTS "Ram:cctempdir/cctemp.q" ECHO "Compile failed." QUIT 20 ENDIF :c/LC2 -v -o<xxx_file>.o Ram:cctempdir/cctemp DELETE Ram:cctempdir all ECHO "-- done compiling '<xxx_file>'. --" LAB END SHAR_EOF cat << \SHAR_EOF > loader.asm ; loader.asm INCLUDE "exec/types.i" INCLUDE "exec/exec.i" INCLUDE "libraries/dosextens.i" INCLUDE "libraries/amiga._LVO.i" STRUCTURE STACKDATA,0 APTR Packet LONG ReturnVal APTR DOSBase BPTR Segment BYTE STACKDATA_SIZE LINKEXE: MACRO MOVEA.L _AbsExecBase,A6 JSR _LVO\1(A6) ENDM LINKDOS: MACRO MOVEA.L DOSBase(SP),A6 JSR _LVO\1(A6) ENDM _AbsExecBase EQU 4 ;---------------------------------------------------------------------------- StartModule: DC.L (EndModule-StartModule)/4 ; for BCPL linking EntryPoint: SUBA.L #STACKDATA_SIZE,SP LSL.L #2,D1 ; convert to byte pointer MOVE.L D1,Packet(SP) CLR.L ReturnVal(SP) ; no error - for now OpenDOS: LEA DOSName(PC),A1 CLR.L D0 LINKEXE OpenLibrary MOVE.L D0,DOSBase(SP) BNE LoadCode MOVE.L #ERROR_INVALID_RESIDENT_LIBRARY,ReturnVal(SP) BRA Return LoadCode: LEA HandlerName(PC),A1 MOVE.L A1,D1 LINKDOS LoadSeg MOVE.L D0,Segment(SP) BNE CallHandler MOVE.L #ERROR_OBJECT_NOT_FOUND,ReturnVal(SP) BRA CloseDOS CallHandler: LEA SPsave(PC),A1 MOVE.L SP,(A1) ; save current SP MOVE.L Segment(SP),D0 ; BPTR to segment LSL.L #2,D0 MOVEA.L D0,A0 ; byte pointer to segment MOVE.L Packet(SP),D0 ; packet address MOVE.L D0,-(SP) ; push (not sure if safe above) ; --- Now, call the loaded handler code. ; --- It is sent the byte address of the startup packet passed to this code. JSR 4(A0) ; call first code in segment MOVEA.L SPsave(PC),SP ; restore SP UnloadCode: MOVE.L Segment(SP),D1 LINKDOS UnLoadSeg CloseDOS: MOVE.L DOSBase(SP),A1 LINKEXE CloseLibrary Return: MOVE.L ReturnVal(SP),D0 ; retrieve return value ADDA.L #STACKDATA_SIZE,SP RTS SPsave: DC.L 0 DOSName: DOSNAME HandlerName: DC.B 'L:' ProcessName: DC.B 'pipe-handler',0 ; trailing definitions for BCPL linking CNOP 0,4 ; align to lonword boundary DC.L 0 ; End Marker DC.L 1 ; Global 1 DC.L EntryPoint-StartModule ; Offset DC.L 1 ; Highest Global Used EndModule: END SHAR_EOF cat << \SHAR_EOF > pipe-handler.c /**************************************************************************** ** File: pipe-handler.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added shared locks for individual pipes. ** PIPEDATA structure modified to include ** a FileLock structure. ** 07-Feb-87 Added #if's forautomatic pipe naming "feature" ** for pipes specified with empty names. ** 12-Feb-87 Added ParentDir packet handling. ** 12-Feb-87 Fixed bug in OpenPipe() and PipeLock(): ** they previously ignored the lock passed in ** packet. Bug uncovered when pipes became ** lockable, and thus assignable. ** 27-Mar-87 Added the case for PipeDupLock(). This was ** missing in the original version! ** 28-Mar-87 Added code to handler() to remove ':' from ** end of handler name. This caused problems ** with Examine(); it expects no ending ':'. */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #if PIPEDIR # include "pipedir.h" #endif PIPEDIR #ifdef DEBUG # include "pipedebug.h" #endif DEBUG /*--------------------------------------------------------------------------- ** pipe-handler.c ** -------------- ** This is the main module for the handler. Handlers are started with ** register D1 containing a BPTR to a startup packet, which in turn contains ** (BCPL) pointers to the name and DeviceNode. Since the entry, handler(), ** expects a byte address of the startup packet, an assembly language startup ** must be used to convert the BCPL pointer, and pass it on the stack. ** ** Problems arise if a handler tries to do I/O via the DOS functions Open(), ** Close(), Read() and Write(). DOS sends request packets to the handler ** via its DOS port (the one whose address forms the process ID). This is ** also the port used by the I/O functions. Therefore, if a request comes, ** and then an Open() call is performed, DOS will send a request packet for ** the open and erroneously pick up the request packet meant for the handler ** as its reply. A crash ensues. ** ** This is the reason for the I/O functions in pipedebug.c. They implement ** the regualar I/O calls, but use a different ReplyPort. With no debugging, ** these functions are unneeded, since all of the handler's normal I/O is ** performed asynchronously, using PutMsg(). ** ** An alternate solution is to patch the handler's Task field with a new port ** instead of the handler's DOS port. This works, except that DOS always ** sends the initial request packets to the DOS port (when the handler is ** first started). This is probably because DeviceProc(), upon seeing that ** the handler has not yet been loaded, returns the result from its call to ** CreateProc() for the handler process. Only on subsequent calls to ** DeviceProc() will the patched field be returned. The upshot of this is ** that an alternate port can be used for handler requests, but there are ** always an unspecified number that may come over the DOS port regardless. ** Note that since not all handlers patch their Task field (because they want ** to be restarted each time), DOS is doing the "right" thing, or at least ** the best it can. ** ** Visible Functions ** ----------------- ** void handler (StartPkt) ** PIPEDATA *FindPipe (name) ** ** Macros (in pipe-handler.h) ** -------------------------- ** BPTRtoCptr (Bp) ** CptrtoBPTR (Cp) ** ReplyPkt (pkt) ** ** Local Functions ** --------------- ** struct DosPacket *GetPkt (port) */ /*--------------------------------------------------------------------------- ** HandlerName : passed as a BSTR in startup packet Arg1, our device name. ** Everything from the ':' and beyond is removed. ** Used by PipeExamine() for the handler's "directory" name. ** ** DevNode : passed as a BPTR in startup packet Arg3. This is a pointer ** to our DeviceNode entry in the system device list (DevInfo). ** ** Pipeort : our DOS MsgPort, as well as our process ID. See above for ** notes about why we can't let DOS use this. ** ** pipelist : the list of currently existing pipes. PIPEDATA nodes are ** linked into this list. ** ** tapwaitlist : the list of requests waiting on tap opens/closes/writes. ** WAITINGDATA nodes are linked into this list. See pipesched.c ** and pipecreate.c. ** ** TapReplyPort : this is the MsgPort to which tap I/O replys are returned. ** ** SysBase, ** DOSBase : Standard system library pointers. Since we don't have the ** usual startup code, we must initialize these ourselves. ** ** PipeDate : If compiled with PIPEDIR true, the handler responds to some ** directory-like actions. This is the date for the entire ** handler, i.e., the directory date. The flag UPDATE_PIPEDATE ** controls whether this date is updated with each pipe access ** (true) or not (false). See SetPipeDate() and PipeExamine(). */ char HandlerName[30]; struct DeviceNode *DevNode = NULL; struct MsgPort *PipePort = NULL; PIPELISTHEADER pipelist; PIPELISTHEADER tapwaitlist; struct MsgPort *TapReplyPort = NULL; struct Library *SysBase = NULL; struct Library *DOSBase = NULL; #if PIPEDIR struct DateStamp PipeDate; #endif PIPEDIR /*--------------------------------------------------------------------------- ** Performs initialization, replies to startup packet, and dispatches ** incoming request packets to the apropriate functions. The TapReplyPort is ** also monitored for returning requests which were sent out by the handler. ** These returned requests are routed to HandleTapReply(). ** Our DeviceNode Task field is patched with our process ID so that this ** process is used for subsequent handler requests. The function exits only ** if there is some initialization error. */ void handler (StartPkt) struct DosPacket *StartPkt; { char *cp; struct Task *Task; ULONG PipeMask, TapReplyMask, WakeupMask, SigMask; struct DosPacket *pkt, *GetPkt(); void OpenPipe(), ClosePipe(); SysBase= AbsExecBase; if ((DOSBase= OpenLibrary (DOSNAME, 0)) == NULL) goto QUIT; BSTRtoCstr (BPTRtoCptr (StartPkt->dp_Arg1), HandlerName, sizeof (HandlerName)); for (cp= HandlerName; *cp != '\0'; ++cp) if (*cp == ':') /* remainder of handler's first refernece follows */ { *cp= '\0'; break; } Task= FindTask (0); PipePort= (struct MsgPort *) ((ULONG) Task + sizeof (struct Task)); ((struct Process *) Task)->pr_CurrentDir= 0; /* initial file system root */ if ((TapReplyPort= CreatePort (NULL, PipePort->mp_Node.ln_Pri)) == NULL) goto QUIT; #ifdef DEBUG if (! InitDebugIO (PipePort->mp_Node.ln_Pri)) goto QUIT; #endif DEBUG PipeMask= (1L << PipePort->mp_SigBit); TapReplyMask= (1L << TapReplyPort->mp_SigBit); WakeupMask= (PipeMask | TapReplyMask); DevNode= (struct DeviceNode *) BPTRtoCptr (StartPkt->dp_Arg3); DevNode->dn_Task= PipePort; InitList (&pipelist); InitList (&tapwaitlist); #if PIPEDIR (void) DateStamp (&PipeDate); #endif PIPEDIR ReplyPkt (StartPkt); LOOP: SigMask= Wait (WakeupMask); if (SigMask & TapReplyMask) while ((pkt= GetPkt (TapReplyPort)) != NULL) HandleTapReply (pkt); if (SigMask & PipeMask) while ((pkt= GetPkt (PipePort)) != NULL) switch (pkt->dp_Type) { case MODE_READWRITE: #ifdef DEBUG OS ("Open READWRITE packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case MODE_READONLY: /* syn: MODE_OLDFILE, ACTION_FINDINPUT */ #ifdef DEBUG OS ("Open READONLY packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case MODE_NEWFILE: /* syn: ACTION_FINDOUTPUT */ #ifdef DEBUG OS ("Open NEWFILE packet received\n"); #endif DEBUG OpenPipe (pkt, 0); break; case ACTION_END: #ifdef DEBUG OS ("Close packet received\n"); #endif DEBUG ClosePipe (pkt); break; case ACTION_READ: #ifdef DEBUG OS ("<<< Read packet received\n"); #endif DEBUG StartPipeIO (pkt, PIPEREAD); break; case ACTION_WRITE: #ifdef DEBUG OS (">>> Write packet received\n"); #endif DEBUG StartPipeIO (pkt, PIPEWRITE); break; #if PIPEDIR case ACTION_LOCATE_OBJECT: # ifdef DEBUG OS ( "Lock packet received\n"); # endif DEBUG PipeLock (pkt); break; case ACTION_COPY_DIR: # ifdef DEBUG OS ( "DupLock packet received\n"); # endif DEBUG PipeDupLock (pkt); break; case ACTION_FREE_LOCK: # ifdef DEBUG OS ( "UnLock packet received\n"); # endif DEBUG PipeUnLock (pkt); break; case ACTION_EXAMINE_OBJECT: # ifdef DEBUG OS ( "Examine packet received\n"); # endif DEBUG PipeExamine (pkt); break; case ACTION_EXAMINE_NEXT: # ifdef DEBUG OS ( "ExNext packet received\n"); # endif DEBUG PipeExNext (pkt); break; case ACTION_PARENT: # ifdef DEBUG OS ( "ParentDir packet received\n"); # endif DEBUG PipeParentDir (pkt); break; #endif PIPEDIR default: #ifdef DEBUG OS ("BAD packet received, type = "); OL (pkt->dp_Type); NL; #endif DEBUG pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_ACTION_NOT_KNOWN; ReplyPkt (pkt); } goto LOOP; QUIT: DevNode->dn_Task= NULL; /* bad if someone in process of accessing us . . . */ if (TapReplyPort != NULL) FreeMem (TapReplyPort, sizeof (struct MsgPort)); /* signal bit won't matter */ #ifdef DEBUG CleanupDebugIO (); #endif DEBUG if (DOSBase != NULL) CloseLibrary (DOSBase); } /*--------------------------------------------------------------------------- ** Returns the DosPacket associated with the next message on "port", or NULL ** if the port is empty. The message is removed from the port. ** A related macro, ReplyPkt() is provided in pipe-handler.h. */ static struct DosPacket *GetPkt (port) register struct MsgPort *port; { register struct Message *msg; return ((msg= GetMsg (port)) == NULL) ? NULL : (struct DosPacket *) msg->mn_Node.ln_Name; } /*--------------------------------------------------------------------------- ** Searches "pipelist" for a pipe whose name is "name". If found, a pointer ** to the pipe returns. Otherwise, NULL returns. */ PIPEDATA *FindPipe (name) char *name; { PIPEDATA *p; char *cp, *strdiff(); for (p= (PIPEDATA *) FirstItem (&pipelist); p != NULL; p= (PIPEDATA *) NextItem (p)) { cp= strdiff (name, p->name); if ((*cp == '\0') && (p->name[(LONG) cp - (LONG) name] == '\0')) return p; /* same name */ } return NULL; /* no match found */ } SHAR_EOF cat << \SHAR_EOF > pipebuf.c /**************************************************************************** ** File: pipebuf.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" /*--------------------------------------------------------------------------- ** pipebuf.c ** --------- ** This module contains functions that manage the circular buffer for pipes. ** ** Visible Functions ** ----------------- ** PIPEBUF *AllocPipebuf (len) ** ULONG MoveFromPipebuf (pb, dest, amt) ** ULONG MoveToPipebuf (pb, src, amt) ** ** Macros (in pipebuf.h) ** --------------------- ** PipebufEmpty (pb) ** PipebufFull (pb) ** FreePipebuf (pb) ** ** Local Functions ** --------------- ** - none - */ /*--------------------------------------------------------------------------- ** AllocPipebuf() returns a pointer to a new PIPEBUF structure if there is ** enough free memory to allocate one with the requested ("len") storage. ** The structure is iinitialized as empty. Notice that the buffer storage ** area is the tail part of the structure. */ PIPEBUF *AllocPipebuf (len) ULONG len; { PIPEBUF *pb = NULL; if ( (len > 0) && (len <= MAX_PIPELEN) && ((pb= (PIPEBUF *) AllocMem (sizeof (PIPEBUF) - 1 + len, ALLOCMEM_FLAGS)) != NULL) ) { pb->head= pb->tail= 0; pb->full= FALSE; pb->len= len; } return pb; } /*--------------------------------------------------------------------------- ** Move bytes from the PIPEBUF to the memory pointed to by "dest". At most ** "amt" bytes are moved. The actual number moved is returned. */ ULONG MoveFromPipebuf (pb, dest, amt) PIPEBUF *pb; register BYTE *dest; ULONG amt; { register BYTE *src; register LONG ct; ULONG amtleft; if ((amt <= 0) || PipebufEmpty (pb)) return 0L; amtleft= amt; src= pb->buf + pb->tail; if (pb->tail >= pb->head) /* then have to wrap around */ { if ((ct= (pb->len - pb->tail)) > amtleft) ct= amtleft; /* more than needed in end of pipebuf */ CopyMem (src, dest, ct); pb->tail= (pb->tail + ct) % pb->len; amtleft -= ct; src= pb->buf + pb->tail; dest += ct; } if ( (amtleft > 0) && (ct= (pb->head - pb->tail)) ) { if (ct > amtleft) ct= amtleft; /* more than needed */ CopyMem (src, dest, ct); pb->tail += ct; /* no need to mod */ amtleft -= ct; } pb->full= FALSE; /* has to be: nonzero amt */ return (amt - amtleft); } /*--------------------------------------------------------------------------- ** Move bytes to the PIPEBUF from the memory pointed to by "src". At most ** "amt" bytes are moved. The actual number moved is returned. */ ULONG MoveToPipebuf (pb, src, amt) PIPEBUF *pb; register BYTE *src; ULONG amt; { register BYTE *dest; register LONG ct; ULONG amtleft; if ((amt <= 0) || PipebufFull (pb)) return 0L; amtleft= amt; dest= pb->buf + pb->head; if (pb->head >= pb->tail) /* then have to wrap around */ { if ((ct= (pb->len - pb->head)) > amtleft) ct= amtleft; /* more than will fit in end of pipebuf */ CopyMem (src, dest, ct); pb->head= (pb->head + ct) % pb->len; amtleft -= ct; src += ct; dest= pb->buf + pb->head; } if ( (amtleft > 0) && (ct= (pb->tail - pb->head)) ) { if (ct > amtleft) ct= amtleft; /* more than will fit */ CopyMem (src, dest, ct); pb->head += ct; /* no need to mod */ amtleft -= ct; } pb->full= (pb->head == pb->tail); return (amt - amtleft); } SHAR_EOF cat << \SHAR_EOF > pipecreate.h /**************************************************************************** ** File: pipecreate.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added lock initialization to OpenPipe() ** for locks on individual pipes. ** 12-Feb-87 Fixed bug in OpenPipe(): previously ignored ** lock passed in packet. Bug uncovered when ** pipes became lockable, and thus assignable. */ #define OPENTAP_STRSIZE 108 extern void OpenPipe ( /* pkt, tapfh */ ); extern void ClosePipe ( /* pkt */ ); extern void DiscardPipe ( /* pipe */ ); SHAR_EOF cat << \SHAR_EOF > pipedebug.c /**************************************************************************** ** File: pipedebug.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <exec/exec.h> #include "pipedebug.h" /*--------------------------------------------------------------------------- ** pipedebug.c ** ----------- ** This module contains debugging functions. In need only be included if the ** other modules are compiled with DEBUG defined. ** ** Visible Functions ** ----------------- ** int InitDebugIO (NodePri) ** void CleanupDebugIO () ** BPTR DebugOpen (name, mode) ** void DebugClose (fh) ** int DebugWrite (fh, buf, len) ** void OutStr (str, fh) ** void OutLONG (n, fh) ** ** Macros (in pipedebug.h) ** ----------------------- ** OS (s) ** NL ** OL (n) ** ** Local Functions ** --------------- ** void DebugIO (Handler, Type, Arg1, Arg2, Arg3) */ #define BPTRtoCptr(Bp) ((char *) ((ULONG) (Bp) << 2)) #define CptrtoBPTR(Cp) ((BPTR) ((ULONG) (Cp) >> 2)) #define MEMFLAGS (MEMF_PUBLIC | MEMF_CLEAR) static struct MsgPort *DebugDOSPort = NULL; static struct Message *DebugMsg = NULL; static struct DosPacket *DebugPkt = NULL; BPTR DebugFH = 0; /*--------------------------------------------------------------------------- ** InitDebugIO() allocates things for the debugging functions, and opens a ** window for output. It MUST be called before any of the I/O operations ** are used. CleanupDebugIO() frees the resources allocated here, and closes ** the window. ** The routines DebugOpen(), DebugClose(), and DebugWrite() mimic their ** corresponding DOS functions, except they use a private reply port, not ** the process' DOS port. DOS does bad things if a handler request comes in ** while it is waiting or a reply to one of its requests made on your behalf. ** The return value is nonzero iff no error occurred. */ int InitDebugIO (NodePri) BYTE NodePri; { struct MsgPort *CreatePort(); BYTE *AllocMem(); DebugDOSPort= NULL; DebugMsg= NULL; DebugPkt= NULL; DebugFH= 0; if ( ((DebugDOSPort= CreatePort (NULL, NodePri)) == NULL) || ((DebugMsg= (struct Message *) AllocMem (sizeof (struct Message), MEMFLAGS)) == NULL) || ((DebugPkt= (struct DosPacket *) AllocMem (sizeof (struct DosPacket), MEMFLAGS)) == NULL) || ((DebugFH= DebugOpen (DEBUG_CON_NAME, MODE_NEWFILE)) == 0) ) { CleanupDebugIO (); return FALSE; } return TRUE; } /*--------------------------------------------------------------------------- ** Cleanup things allocated by InitDebugIO, and close the window. */ void CleanupDebugIO () { void FreeMem(); if (DebugFH != 0) DebugClose (DebugFH); if (DebugPkt != NULL) FreeMem (DebugPkt, sizeof (struct DosPacket)); if (DebugMsg != NULL) FreeMem (DebugMsg, sizeof (struct Message)); if (DebugDOSPort != NULL) { FreeSignal (DebugDOSPort->mp_SigBit); FreeMem (DebugDOSPort, sizeof (struct MsgPort)); } } /*--------------------------------------------------------------------------- ** DebugOpen() performs just like the DOS function Open(). ** InitDebugIO() MUST have been called and returned successful before calling ** this function. */ BPTR DebugOpen (name, mode) char *name; int mode; { char Bnamebuf[DEBUGOPEN_MAXNAMELEN + 3 + 2], *Bname; UBYTE namelen; struct MsgPort *HandlerPID, *DeviceProc(); int IoErr(); struct FileLock *Lock; struct FileHandle *handle; BYTE *AllocMem(); void DebugIO(), FreeMem(); Bname= (char *) (((ULONG) Bnamebuf + 3) & (~0 << 2)); /* longword align */ for (namelen= 0; (Bname[namelen + 1]= name[namelen]); ++namelen) if (namelen > DEBUGOPEN_MAXNAMELEN) return 0; Bname[0]= (char) namelen; /* make a BSTR */ HandlerPID= DeviceProc (name); if (HandlerPID == NULL) return 0; Lock= (struct FileLock *) IoErr (); if ((handle= (struct FileHandle *) AllocMem (sizeof (struct FileHandle), MEMFLAGS)) == NULL) return 0; handle->fh_Pos= -1; handle->fh_End= -1; handle->fh_Type= HandlerPID; DebugIO (HandlerPID, mode, CptrtoBPTR (handle), CptrtoBPTR (Lock), CptrtoBPTR (Bname)); if (DebugPkt->dp_Res1 == 0) { FreeMem (handle, sizeof (struct FileHandle)); return 0; } return CptrtoBPTR (handle); } /*--------------------------------------------------------------------------- ** DebugClose() performs just like the DOS function Close(). ** InitDebugIO() MUST have been called and returned successful before calling ** this function. */ void DebugClose (fh) BPTR fh; { struct FileHandle *handle; void DebugIO(), FreeMem(); handle= (struct FileHandle *) BPTRtoCptr (fh); DebugIO (handle->fh_Type, 1007, handle->fh_Arg1, 0, 0); FreeMem (handle, sizeof (struct FileHandle)); } /*--------------------------------------------------------------------------- ** DebugWrite() performs just like the DOS function Write(). ** InitDebugIO() MUST have been called and returned successful before calling ** this function. */ int DebugWrite (fh, buf, len) BPTR fh; BYTE *buf; ULONG len; { struct FileHandle *handle; void DebugIO(); handle= (struct FileHandle *) BPTRtoCptr (fh); DebugIO (handle->fh_Type, ACTION_WRITE, handle->fh_Arg1, buf, len); return DebugPkt->dp_Res1; } /*--------------------------------------------------------------------------- ** DebugIO() sets up the DosPacket with the specified information, initiates ** the request, and waits for the reply. */ static void DebugIO (Handler, Type, Arg1, Arg2, Arg3) struct MsgPort *Handler; LONG Type; LONG Arg1; LONG Arg2; LONG Arg3; { void PutMsg(); struct MsgPort *WaitPort(), *Getmsg(); DebugMsg->mn_ReplyPort= DebugDOSPort; DebugMsg->mn_Node.ln_Type= NT_MESSAGE; DebugMsg->mn_Node.ln_Name= (char *) DebugPkt; DebugPkt->dp_Link= DebugMsg; DebugPkt->dp_Port= DebugDOSPort; DebugPkt->dp_Type= Type; DebugPkt->dp_Arg1= Arg1; DebugPkt->dp_Arg2= Arg2; DebugPkt->dp_Arg3= Arg3; PutMsg (Handler, DebugMsg); (void) WaitPort (DebugDOSPort); (void) GetMsg (DebugDOSPort); /* assume it is DebugMsg */ } /*--------------------------------------------------------------------------- ** OutStr() outputs the null-terminated string "str" to the filehandle "fh". */ void OutStr (str, fh) char *str; BPTR fh; { int strlen(); DebugWrite (fh, str, strlen (str)); } /*--------------------------------------------------------------------------- ** OutLONG() outputs the decimal representaion on "n" to the filehandle "fh". ** The conversion function stcu_d() is used -- this may not be available ** on all systems. In that case, such a function will need to be written. */ void OutLONG (n, fh) ULONG n; BPTR fh; { char buf[80]; int stcu_d(); /* Lattice C Library conversion function */ (void) stcu_d (buf, n, 80); OutStr (buf, fh); } SHAR_EOF cat << \SHAR_EOF > pipedebug.h /**************************************************************************** ** File: pipedebug.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ #define DEBUGOPEN_MAXNAMELEN 108 #define DEBUG_CON_NAME "CON:10/30/300/100/pipe-handler DEBUG" #define OS(s) OutStr ((s), DebugFH) #define NL OutStr ("\n", DebugFH) #define OL(n) OutLONG ((n), DebugFH) extern BPTR DebugFH; extern int InitDebugIO ( /* NodePri */ ); extern void CleanupDebugIO ( ); extern BPTR DebugOpen ( /* name, mode */ ); extern void DebugClose ( /* fh */ ); extern int DebugWrite ( /* fh, buf, len */ ); extern void OutStr ( /* str, fh */ ); extern void OutLONG ( /* n, fh */ ); SHAR_EOF cat << \SHAR_EOF > pipedir.c /**************************************************************************** ** File: pipedir.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added modifications for allowing shared locks ** on individual pipes. ** 12-Feb-87 Added PipeParentDir. ** 12-Feb-87 Fixed bug in PipeLock(): previously ignored ** lock passed in packet. Bug uncovered when ** pipes became lockable, and thus assignable. ** 27-Mar-87 Added PipeDupLock(). This was missing ** in the original version! */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #include "pipedir.h" /*--------------------------------------------------------------------------- ** pipedir.c ** --------- ** This module handles the directory-related requests to the handler. ** The functions contained here are not needed if the compile-time flag ** PIPEDIR is false. ** ** Visible Functions ** ----------------- ** void SetPipeDate (pipe) ** void PipeLock (pkt) ** void PipeDupLock (pkt) ** void PipeUnLock (pkt) ** void PipeExamine (pkt) ** void PipeExNext (pkt) ** void InitLock (lock, key) ** ** Macros (in pipedir.h) ** --------------------- ** - none - ** ** Local Functions ** --------------- ** void InitPipedirLock () ** void FillFIB (fib, DiskKey, FileName, Protection, Type, Size, NumBlocks, Datep) */ /*--------------------------------------------------------------------------- ** "PipedirLock" is the lock returned by PipeLock() to clients requesting a ** shared lock on the handler. "LockBytes" is used for the storage of the ** lock. InitLock() sets "PipedirLock" to point to the first longword within ** "LockBytes" to ensure longword alignment for BCPL's sake. */ static BYTE LockBytes[sizeof (struct FileLock) + 3]; static struct FileLock *PipedirLock = NULL; /*--------------------------------------------------------------------------- ** SetPipeDate() modifies the date field for the pipe sent. If the compile- ** time flag UPDATE_PIPEDATE is true (see pipe-handler.h), the handler's date ** is modified as well. */ void SetPipeDate (pipe) PIPEDATA *pipe; { (void) DateStamp (&pipe->accessdate); #if UPDATE_PIPEDATE (void) DateStamp (&PipeDate); #endif UPDATE_PIPEDATE } /*--------------------------------------------------------------------------- ** PipeLock() responds to Lock requests. Only multiple access locks are ** granted. The same lock is returned to all clients for a given entity. ** Note: the code which checks if the lock sent in the packet relies on ** the fact that the pipe-handler does not allow subdirectories. If a lock ** on a pipe is passed in, then that pipe is opened. Otherwise, the name is ** parsed without reference to the lock. */ void PipeLock (pkt) struct DosPacket *pkt; { char *name, *tapname; ULONG size; struct FileLock *lock; PIPEDATA *pipe; void InitPipedirLock(); InitPipedirLock (); pkt->dp_Res1= 0; /* error, for now */ pkt->dp_Res2= 0; /* clear for case of no error */ lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1); if (pkt->dp_Arg3 != SHARED_LOCK) { pkt->dp_Res2= ERROR_OBJECT_WRONG_TYPE; goto PLOCKEXIT; } if (! ParsePipeName (BPTRtoCptr (pkt->dp_Arg2), &name, &size, &tapname)) { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto PLOCKEXIT; } if ( (lock == NULL) || ((pipe= (PIPEDATA *) lock->fl_Key) == NULL) ) { if (name[0] == '\0') pkt->dp_Res1= CptrtoBPTR (PipedirLock); else { if ((pipe= FindPipe (name)) == NULL) { pkt->dp_Res2= ERROR_OBJECT_NOT_FOUND; goto PLOCKEXIT; } pkt->dp_Res1= CptrtoBPTR (pipe->lock); ++pipe->lockct; } } else /* lock sent in packet was on the pipe */ { if (name[0] != '\0') { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto PLOCKEXIT; } pkt->dp_Res1= CptrtoBPTR (pipe->lock); ++pipe->lockct; } PLOCKEXIT: ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** PipeDupLock() responds to DupLock requests. It is assumed that the lock ** sent is valid. The same lock is returned; the only action taken is to ** increment the lock count if the lock is on an individual pipe. If the ** zero lock is sent, the zero lock is (properly) returned, even though this ** handler should never receive that request. Notice that this routine never ** returns an error. */ void PipeDupLock (pkt) struct DosPacket *pkt; { struct FileLock *lock; PIPEDATA *pipe; pkt->dp_Res1= pkt->dp_Arg1; /* reuse same structure */ pkt->dp_Res2= 0; if ((lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1)) != NULL) { if ((pipe= (PIPEDATA *) lock->fl_Key) != NULL) ++pipe->lockct; /* lock is on an individual pipe */ } ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** PipeUnLock() responds to UnLock requests. */ void PipeUnLock (pkt) struct DosPacket *pkt; { struct FileLock *lock; PIPEDATA *pipe; if ((lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1)) == NULL) { pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_LOCK; } else { if ((pipe= (PIPEDATA *) lock->fl_Key) != NULL) { --pipe->lockct; CheckWaiting (pipe); /* will discard if totally unused */ } pkt->dp_Res1= 1; /* no error */ pkt->dp_Res2= 0; } ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** PipeExamine() responds to Examine requests. For locks on the handler, the ** address first item of the pipelist is stored in the DiskKey field for ** PipeExNext()'s reference. */ void PipeExamine (pkt) struct DosPacket *pkt; { struct FileInfoBlock *fib; struct FileLock *lock; PIPEDATA *pipe; void FillFIB(); pkt->dp_Res1= 1; /* no error, for now */ pkt->dp_Res2= 0; fib= (struct FileInfoBlock *) BPTRtoCptr (pkt->dp_Arg2); if ((lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1)) == NULL) { pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_OBJECT_NOT_FOUND; } else { if ((pipe= (PIPEDATA *) lock->fl_Key) == NULL) /* then this is a lock on the handler */ { FillFIB ( fib, FirstItem (&pipelist), HandlerName, (FIBF_EXECUTE | FIBF_DELETE), 1, 0, 0, &PipeDate ); } else { FillFIB ( fib, NULL, pipe->name, (FIBF_EXECUTE | FIBF_DELETE), -1, pipe->buf->len, 1, &pipe->accessdate ); } } ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** PipeExNext() responds to ExNext requests. The DiskKey field of the ** FileInfoBlock is assumed to be a pointer to the next pipe in the pipelist ** which is to be listed in the directory. We then scan pipelist for this ** pointer, and upon finding it, store its information in the FileInfoBlock ** and store the address of the next pipe in pipelist in the DiskKey field. ** If the pipe is not found in the list, or if DiskKey is NULL, then ** ERROR_NO_MORE_ENTRIES is returned. ** By rescanning the list each time, deletion of a pipe cannot hurt us ** by causing a dangling pointer in DiskKey -- we just end the directory ** listing there. This can cause incomplete directory information for the ** cleint, however, if the last listed pipe is deleted before the client's ** next ExNext() call. */ void PipeExNext (pkt) struct DosPacket *pkt; { struct FileLock *lock; struct FileInfoBlock *fib; PIPEDATA *listitem, *pipe; void FillFIB(); pkt->dp_Res1= 0; /* error, for now */ if ((lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1)) == NULL) { pkt->dp_Res2= ERROR_INVALID_LOCK; goto EXNEXTREPLY; } if (lock->fl_Key != NULL) /* then an individual pipe */ { pkt->dp_Res2= ERROR_OBJECT_WRONG_TYPE; goto EXNEXTREPLY; } pkt->dp_Res2= ERROR_NO_MORE_ENTRIES; /* until found otherwise */ fib= (struct FileInfoBlock *) BPTRtoCptr (pkt->dp_Arg2); if ((listitem= (PIPEDATA *) fib->fib_DiskKey) == NULL) goto EXNEXTREPLY; for (pipe= (PIPEDATA *) FirstItem (&pipelist); pipe != NULL; pipe= (PIPEDATA *) NextItem (pipe)) if (listitem == pipe) break; if (listitem == pipe) /* then found next entry */ { FillFIB ( fib, NextItem (listitem), listitem->name, (FIBF_EXECUTE | FIBF_DELETE), -1, listitem->buf->len, 1, &listitem->accessdate ); pkt->dp_Res1= 1; pkt->dp_Res2= 0; } EXNEXTREPLY: ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** PipeParentDir() responds to ParentDir requests. */ void PipeParentDir (pkt) struct DosPacket *pkt; { struct FileLock *lock; void InitPipedirLock(); InitPipedirLock (); pkt->dp_Res2= 0; if ((lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg1)) == NULL) { pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_LOCK; } else { if (lock->fl_Key == NULL) /* then lock is on handler */ pkt->dp_Res1= 0; /* root of current filing system */ else pkt->dp_Res1= CptrtoBPTR (PipedirLock); } ReplyPkt (pkt); } /*--------------------------------------------------------------------------- */ static void InitPipedirLock () { if (PipedirLock == NULL) { PipedirLock= (struct FileLock *) (((ULONG) LockBytes + 3) & ((~0)<<2)); InitLock (PipedirLock, NULL); } } /*--------------------------------------------------------------------------- ** InitLock() initializes locks returned to clients by PipeLock(). For locks ** on individual pipes, the "fl_Key" field points to the associated pipe's ** PIPEDATA structure. For the handler, the "fl_Key" field is NULL. */ void InitLock (lock, key) struct FileLock *lock; LONG key; { lock->fl_Link= 0; lock->fl_Key= key; lock->fl_Access= SHARED_LOCK; /* only mode allowed */ lock->fl_Task= PipePort; /* set during handler init */ lock->fl_Volume= CptrtoBPTR (DevNode); /* also set during init */ } /*--------------------------------------------------------------------------- ** FillFIB() fills a FileInfoBlock with the specified information. Note ** that handlers must store BSTR's in the FileInfoBlock. */ static void FillFIB (fib, DiskKey, FileName, Protection, Type, Size, NumBlocks, Datep) struct FileInfoBlock *fib; LONG DiskKey; char *FileName; /* null-terminated */ LONG Protection; LONG Type; LONG Size; LONG NumBlocks; struct DateStamp *Datep; { fib->fib_DiskKey= DiskKey; fib->fib_DirEntryType= Type; CstrtoBSTR (FileName, fib->fib_FileName, sizeof (fib->fib_FileName)); fib->fib_Protection= Protection; fib->fib_EntryType= Type; /* ??? */ fib->fib_Size= Size; fib->fib_NumBlocks= NumBlocks; CopyMem (Datep, &fib->fib_Date, sizeof (struct DateStamp)); fib->fib_Comment[0]= '\0'; /* empty BSTR */ } SHAR_EOF cat << \SHAR_EOF > pipesched.h /**************************************************************************** ** File: pipesched.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added "lockct" check in CheckWaiting(). */ typedef enum iotype { PIPEREAD, PIPEWRITE, PIPERW } IOTYPE; struct pipewait { BYTE *buf; /* the next position for read/write */ ULONG len; /* the remaining length t read/write */ IOTYPE reqtype; /* PIPEREAD or PIPEWRITE only */ }; struct tapwait { struct DosPacket *clientpkt; /* the client's packet */ struct FileHandle *handle; /* the associated filehandle */ }; union pktinfo { struct pipewait pipewait; /* for packet waiting on pipe */ struct tapwait tapwait; /* for packet waiting on tap */ }; typedef struct waitingdata { PIPELISTNODE link; /* for list use */ struct DosPacket *pkt; /* the packet we are waiting on */ union pktinfo pktinfo; /* data pertaining to the waiting request */ } WAITINGDATA; extern void StartPipeIO ( /* pipe, pkt, iotype */ ); extern void CheckWaiting ( /* pipe */ ); extern struct DosPacket *AllocPacket ( /* ReplyPort */ ); extern void FreePacket ( /* pkt */ ); extern void StartTapIO ( /* pkt, Type, Arg1, Arg2, Arg3, Handler */ ); extern void HandleTapReply ( /* pkt */ ); SHAR_EOF cat << \SHAR_EOF > prelude_Mount /* An example MOUNTLIST file enabling a 5" disk to be mounted as DF1: and an interactive serial port mounted as AUX: */ DF1: Device = trackdisk.device Unit = 1 Flags = 1 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0 LowCyl = 0 ; HighCyl = 39 Buffers = 5 BufMemType = 3 # /* This is provided as an example of an alternative type of non-filing device mount. Please note that L:aux-handler is not provided, and thus this mount does not work. */ AUX: Handler = L:aux-handler Stacksize = 700 Priority = 5 # P: Handler = L:pipe-handler Stacksize = 3000 Priority = 5 # SHAR_EOF
qix@s.cc.purdue.edu.UUCP (07/08/87)
Here is part 2 of 2 of the source to Ed Puckett's pipe handler. This is the latest version including the bug fix he posted to the net. Binaries available in comp.binaries.amiga. # 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 # Xshar: Extended Shell Archiver. # This is part 2 out of 2. # This archive created: Wed Jul 8 01:44:10 1987 # By: Craig Norborg (Purdue University Computing Center) # Run the following text with /bin/sh to create: # loader_Mount # pipe-handler.h # pipebuf.h # pipecreate.c # pipedir.h # pipelists.c # pipelists.h # pipename.c # pipename.h # pipesched.c # prelude.asm # tap_demo cat << \SHAR_EOF > loader_Mount /* An example MOUNTLIST file enabling a 5" disk to be mounted as DF1: and an interactive serial port mounted as AUX: */ DF1: Device = trackdisk.device Unit = 1 Flags = 1 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0 LowCyl = 0 ; HighCyl = 39 Buffers = 5 BufMemType = 3 # /* This is provided as an example of an alternative type of non-filing device mount. Please note that L:aux-handler is not provided, and thus this mount does not work. */ AUX: Handler = L:aux-handler Stacksize = 700 Priority = 5 # P: Handler = L:pipe-handler-loader Stacksize = 3000 Priority = 5 # SHAR_EOF cat << \SHAR_EOF > pipe-handler.h /**************************************************************************** ** File: pipe-handler.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added shared locks for individual pipes. ** PIPEDATA structure modified to include ** a FileLock structure. ** 07-Feb-87 Added #if's forautomatic pipe naming "feature" ** for pipes specified with empty names. ** 12-Feb-87 Added ParentDir packet handling. ** 12-Feb-87 Fixed bug in OpenPipe() and PipeLock(): ** they previously ignored the lock passed in ** packet. Bug uncovered when pipes became ** lockable, and thus assignable. ** 26-Mar-87 Fixed bug in ClosePipe() in pipecreate.c: not ** closing r/w mode properly (extraneous else). ** 27-Mar-87 Added PipeDupLock() to pipedir.c and the case ** for it in pipe-handler.c. This was missing ** in the original version! ** 28-Mar-87 Added code to handler() to remove ':' from ** end of handler name. This caused problems ** with Examine(); it expects no ending ':'. */ /*--------------------------------------------------------------------------- ** Compilation Flags ** ----------------- ** DEBUG : add code to open a window for debugging information. ** Messages are output as requests come in, etc. DEBUG is ** active if defined at all. ** ** (The following are active only if #defined nonzero) ** ** CON_TAP_ONLY : only CON: pipe taps are allowed. The full CON: ** specification must be given, though, like CON:0/0/100/100/z. ** ** PIPEDIR : include code so that the handler looks like a directory. ** This allows "Dir" and "List" to work, as well as "CD". ** The functions in pipedir.c are unnecessary if false. ** ** UPDATE_PIPEDATE : if PIPEDIR is true, then this controls whether or not ** the handler's date is updated with each access to a pipe, ** or is just left at its startup time. ** ** AUTONAME : include code so that specifying a null pipe name causes ** the handler to select a new, as yet unused, name. ** Unfortunately, this causes inconsistent behaviour for Lock(), ** since a null name indicates a lock is desired on the handler. ** Thus locking PIPE: and opening PIPE: reference different ** objects. */ #define CON_TAP_ONLY 0 #define PIPEDIR 1 #define UPDATE_PIPEDATE 1 #define AUTONAME 0 #define ALLOCMEM_FLAGS MEMF_PUBLIC /*--------------------------------------------------------------------------- */ typedef struct pipedata { PIPELISTNODE link; /* for list handling */ char name[PIPENAMELEN]; /* the pipe's name */ PIPEBUF *buf; /* see pipebuf.c */ BYTE flags; /* see values below */ PIPELISTHEADER readerlist; /* list of waiting read requests */ PIPELISTHEADER writerlist; /* list of waiting write requests */ BPTR tapfh; /* file handle of tap, 0 if none */ #if PIPEDIR ULONG lockct; /* number of extant locks */ struct FileLock *lock; /* this pipe's lock - see note above */ struct DateStamp accessdate; /* date last accessed */ #endif PIPEDIR } PIPEDATA; #define OPEN_FOR_READ (1 << 0) #define OPEN_FOR_WRITE (1 << 1) /* flags for pipedata struct */ /*--------------------------------------------------------------------------- ** PIPEKEYs are similar to file handles. Each successful pipe open request ** has a PIPEKEY associated with it, which in turn refers to the pipe. ** The filehandle returned to the client has the address of the PIPEKEY ** stored in its Arg1 field, so that read, write and close packets will ** identify the pipe and its mode of opening. */ typedef struct pipekey { PIPEDATA *pipe; int openmode; /* Type field of original open request */ IOTYPE iotype; /* (somewhat redundant) see pipesched.h */ } PIPEKEY; extern struct DeviceNode *DevNode; extern struct MsgPort *PipePort; extern char HandlerName[]; extern PIPELISTHEADER pipelist; extern PIPELISTHEADER tapwaitlist; extern struct MsgPort *TapReplyPort; #if PIPEDIR extern struct DateStamp PipeDate; #endif PIPEDIR #define BPTRtoCptr(Bp) ((char *) ((ULONG) (Bp) << 2)) #define CptrtoBPTR(Cp) ((BPTR) ((ULONG) (Cp) >> 2)) #define ReplyPkt(pkt) PutMsg ((pkt)->dp_Port, (pkt)->dp_Link) extern void handler ( /* StartPkt */ ); extern PIPEDATA *FindPipe ( /* name */ ); /*--------------------- ** references to system */ extern struct Library *OpenLibrary (); extern void CloseLibrary (); extern struct Task *FindTask (); extern struct MsgPort *CreatePort (); extern ULONG Wait (); extern struct Message *GetMsg (); extern void PutMsg (); extern BYTE *AllocMem (); extern void FreeMem (); extern void CopyMem (); extern struct MsgPort *DeviceProc (); extern int IoErr (); #if PIPEDIR extern struct DateStamp *DateStamp (); #endif PIPEDIR extern struct Library *AbsExecBase; /*--------------------------------- ** these are new to the 1.2 release */ #ifndef MODE_READWRITE # define MODE_READWRITE 1004 #endif MODE_READWRITE #ifndef MODE_READONLY # define MODE_READONLY MODE_OLDFILE #endif MODE_READONLY #ifndef ACTION_END # define ACTION_END 1007 /* not really new, just missing */ #endif ACTION_END SHAR_EOF cat << \SHAR_EOF > pipebuf.h /**************************************************************************** ** File: pipebuf.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ #define MAX_PIPELEN (1L << 24) typedef struct pipebuf { ULONG head, /* index of first character */ tail; /* index of last character */ BYTE full; /* flag - takes care of full/empty ambiguity */ ULONG len; /* length of buffer */ BYTE buf[1]; /* buffer proceeds from here */ } PIPEBUF; #define PipebufEmpty(pb) (((pb)->head == (pb)->tail) && (! (pb)->full)) #define PipebufFull(pb) (((pb)->head == (pb)->tail) && ((pb)->full)) #define FreePipebuf(pb) (FreeMem ((pb), sizeof (PIPEBUF) - 1 + (pb)->len)) extern PIPEBUF *AllocPipebuf ( /* len */ ); extern ULONG MoveFromPipebuf ( /* pb, dest, amt */ ); extern ULONG MoveToPipebuf ( /* pb, src, amt */ ); SHAR_EOF cat << \SHAR_EOF > pipecreate.c /**************************************************************************** ** File: pipecreate.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added lock initialization to OpenPipe() ** for locks on individual pipes. ** 12-Feb-87 Fixed bug in OpenPipe(): previously ignored ** lock passed in packet. Bug uncovered when ** pipes became lockable, and thus assignable. ** 26-Mar-87 Fixed bug in ClosePipe(): not closing r/w ** mode properly (extraneous else). */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #if PIPEDIR # include "pipedir.h" #endif PIPEDIR #ifdef DEBUG # include "pipedebug.h" #endif DEBUG /*--------------------------------------------------------------------------- ** pipecreate.c ** ------------ ** This module handles opens and closes for pipes. ** ** Visible Functions ** ----------------- ** void OpenPipe (pkt, tapfh) ** void ClosePipe (pkt) ** void DiscardPipe (pipe) ** ** Macros (in pipecreate.h) ** ------------------------ ** - none - ** ** Local Functions ** --------------- ** int TapFormsLoop (tapfh, pipe) ** void OpenTap (pkt, tapname) ** void CloseTap (tapfh) */ /*--------------------------------------------------------------------------- ** OpenPipe() handles open requests. The DosPacket from the client and the ** filehandle of the tap are sent. If tapfh is 0, but the name sent ** indicates a tap is desired (see ParsePipeName() in pipename.c), then ** an OpenTap() request is initiated and OpenPipe() is immediately exited. ** Later, when the request returns (to HandleTapReply()), OpenPipe() is ** called again with the same client packet and the newly returned tapfh. ** If tapfh is nonzero, or if it is zero but no tap is desired, then ** the an attempt to open the pipe is made. If a existent pipe with a tap is ** to be opened and a new tapfh is given, the old tap is closed. ** If the name's syntax is incorrect, then the request is returned ** unsuccessful. Otherwise, if the pipe named by the request does not ** already exist, a new pipe is created (if there is enough memory). ** If it does exist, but it is already open for the mode requested, an error ** is returned (a maximum of one reader and one writer is allowed). ** A successful open returns the client's filehandle with its Arg1 field ** pointing to a PIPEKEY, which in turn identifies the pipe and open mode. ** Unless an OpenTap() is required, the packet is returned to the cleint ** by this function. If an OpenTap() is required, it will be returned by the ** the later call to this function when the tap open request is returned. ** Note: the code which checks if the lock sent in the packet relies on ** the fact that the pipe-handler does not allow subdirectories. If a lock ** on a pipe is passed in, then that pipe is opened. Otherwise, the name is ** parsed without reference to the lock. */ void OpenPipe (pkt, tapfh) struct DosPacket *pkt; BPTR tapfh; { void OpenTap(), CloseTap(); LONG openmode; struct FileHandle *handle; struct FileLock *lock; char *pipename = NULL, *tapname = NULL; ULONG pipesize; PIPEKEY *pipekey = NULL; PIPEDATA *pipe; int TapFormsLoop(); pkt->dp_Res1= 0; /* error, for now */ if (! ParsePipeName (BPTRtoCptr (pkt->dp_Arg3), &pipename, &pipesize, &tapname)) { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } if ( (tapfh == 0) && (tapname != NULL) && (tapname[0] != '\0') ) { OpenTap (pkt, tapname); /* start tap open request */ return; /* HandleTapReply() re-calls when request returns */ } openmode= pkt->dp_Type; lock= (struct FileLock *) BPTRtoCptr (pkt->dp_Arg2); pipe= NULL; if ( (lock == NULL) || ((pipe= (PIPEDATA *) lock->fl_Key) == NULL) ) { if (pipename[0] == '\0') #if AUTONAME pipename= get_autoname ((openmode == MODE_NEWFILE) || (openmode == MODE_READWRITE)); #else !AUTONAME { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } #endif AUTONAME pipe= FindPipe (pipename); } else /* packet's lock was on the pipe */ { if (pipename[0] != '\0') { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } pipename= pipe->name; } handle= (struct FileHandle *) BPTRtoCptr (pkt->dp_Arg1); if ((pipekey= (PIPEKEY *) AllocMem (sizeof (PIPEKEY), ALLOCMEM_FLAGS)) == NULL) { pkt->dp_Res2= ERROR_NO_FREE_STORE; goto OPENREPLY; } if (pipe == NULL) /* then PIPE NOT FOUND */ { if (openmode == MODE_READONLY) { pkt->dp_Res2= ERROR_OBJECT_NOT_FOUND; goto OPENREPLY; } pkt->dp_Res2= ERROR_NO_FREE_STORE; /* in case of AllocMem error */ if ((pipe= (PIPEDATA *) AllocMem (sizeof (PIPEDATA), ALLOCMEM_FLAGS)) == NULL) goto OPENMEMERR1; if ((pipe->buf= AllocPipebuf (pipesize)) == NULL) goto OPENMEMERR2; if ((pipe->lock= (struct FileLock *) AllocMem (sizeof (struct FileLock), ALLOCMEM_FLAGS)) == NULL) { FreePipebuf (pipe->buf); OPENMEMERR2: FreeMem (pipe, sizeof (PIPEDATA)); OPENMEMERR1: goto OPENREPLY; } l_strcpy (pipe->name, pipename); pipekey->pipe= pipe; pipekey->openmode= openmode; if (openmode == MODE_READONLY) { pipekey->iotype= PIPEREAD; pipe->flags |= OPEN_FOR_READ; } else if (openmode == MODE_NEWFILE) { pipekey->iotype= PIPEWRITE; pipe->flags= OPEN_FOR_WRITE; } else /* MODE_READWRITE */ { pipekey->iotype= PIPERW; pipe->flags= (OPEN_FOR_READ | OPEN_FOR_WRITE); } InitList (&pipe->readerlist); InitList (&pipe->writerlist); pipe->tapfh= tapfh; #if PIPEDIR pipe->lockct= 0; InitLock (pipe->lock, pipe); #endif PIPEDIR InsertTail (&pipelist, pipe); /* at tail for directory's sake */ #ifdef DEBUG OS ("*** created pipe '"); OS (pipe->name); OS ("' [buflen "); OL (pipe->buf->len); OS ("]\n"); #endif DEBUG } else /* PIPE WAS FOUND */ { if (TapFormsLoop (tapfh, pipe)) { pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; goto OPENREPLY; } pipekey->pipe= pipe; pipekey->openmode= openmode; pkt->dp_Res2= ERROR_OBJECT_IN_USE; /* in case of openmode error */ if (openmode == MODE_READONLY) { if (pipe->flags & OPEN_FOR_READ) goto OPENREPLY; pipekey->iotype= PIPEREAD; pipe->flags |= OPEN_FOR_READ; } else if (openmode == MODE_NEWFILE) { if (pipe->flags & OPEN_FOR_WRITE) goto OPENREPLY; pipekey->iotype= PIPEWRITE; pipe->flags |= OPEN_FOR_WRITE; } else /* MODE_READWRITE */ { if (pipe->flags & (OPEN_FOR_READ | OPEN_FOR_WRITE)) goto OPENREPLY; pipekey->iotype= PIPERW; pipe->flags= (OPEN_FOR_READ | OPEN_FOR_WRITE); } if (tapfh != 0) { if (pipe->tapfh != 0) CloseTap (pipe->tapfh); /* close old tap first */ pipe->tapfh= tapfh; } } handle->fh_Arg1= (LONG) pipekey; /* for identification on Read, Write, Close */ pkt->dp_Res1= 1; pkt->dp_Res2= 0; /* for successful open */ OPENREPLY: if (pkt->dp_Res1 == 0) /* then there was an error */ { if (pipekey != NULL) FreeMem (pipekey, sizeof (PIPEKEY)); if (tapfh != 0) CloseTap (tapfh); } #if PIPEDIR else SetPipeDate (pipe); #endif PIPEDIR ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** This routine checks for "the old loop in the pipe trick" (86). If the ** handler has a loop through its tap, the handler will endlessly pass ** packets to itself. This would be disastrous if the handler is running at ** high priority. */ static int TapFormsLoop (tapfh, pipe) BPTR tapfh; PIPEDATA *pipe; { struct FileHandle *handle; PIPEKEY *tapkey; PIPEDATA *tappipe; int numchecks; /* protection */ for (numchecks= 0; ((tapfh != 0) && (numchecks < 10000)); ++numchecks) { handle= (struct FileHandle *) BPTRtoCptr (tapfh); if (handle->fh_Type == PipePort) /* then the tap is a pipe, too */ { if ( ((tapkey= (PIPEKEY *) handle->fh_Arg1) == NULL) || ((tappipe= tapkey->pipe) == NULL) ) return FALSE; if (tappipe == pipe) return TRUE; tapfh= tappipe->tapfh; } else return FALSE; } return FALSE; } /*--------------------------------------------------------------------------- ** The previous open performed on a pipe is terminated. The PIPEKEY ** allocated for the client when the pipe was opened is freed. Then, ** CheckWaiting() is called -- it will discard the pipe if it becomes empty ** and is not opened for read or write. */ void ClosePipe (pkt) struct DosPacket *pkt; { PIPEKEY *pipekey; PIPEDATA *pipe; void DeletePipe(); pipekey= (PIPEKEY *) pkt->dp_Arg1; pipe= pipekey->pipe; if ((pipekey->iotype == PIPEREAD) || (pipekey->iotype == PIPERW)) pipe->flags &= ~OPEN_FOR_READ; if ((pipekey->iotype == PIPEWRITE) || (pipekey->iotype == PIPERW)) pipe->flags &= ~OPEN_FOR_WRITE; FreeMem (pipekey, sizeof (PIPEKEY)); CheckWaiting (pipe); /* will discard if empty */ pkt->dp_Res1= 1; pkt->dp_Res2= 0; ReplyPkt (pkt); } /*--------------------------------------------------------------------------- ** Remove a pipe from the pipe list and release its memory. The pipe is ** assumed empty and having no clients. */ void DiscardPipe (pipe) PIPEDATA *pipe; { #ifdef DEBUG OS ("*** discarding pipe '"); OS (pipe->name); OS ("'\n"); #endif DEBUG Delete (&pipelist, pipe); FreePipebuf (pipe->buf); FreeMem (pipe->lock, sizeof (struct FileLock)); if (pipe->tapfh != 0) CloseTap (pipe->tapfh); FreeMem (pipe, sizeof (PIPEDATA)); } /*--------------------------------------------------------------------------- ** An open request for a tap is performed. A WAITINGDATA structure is ** allocated to hold the client packet until later. HandleTapReply() will ** deal with the reply and, if successful, re-call OpenPipe(). */ static void OpenTap (pkt, tapname) struct DosPacket *pkt; char *tapname; { char *Bname; struct FileHandle *handle; WAITINGDATA *wd; struct DosPacket *tappkt; struct MsgPort *Handler; struct FileLock *Lock; void StartTapIO(); if ( (tapname == NULL) || ((Bname= (char *) AllocMem (OPENTAP_STRSIZE, ALLOCMEM_FLAGS)) == NULL) ) goto OPENTAPERR; if ((handle= (struct FileHandle *) AllocMem (sizeof (struct FileHandle), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) goto OPENTAPERR1; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) goto OPENTAPERR2; if ((tappkt= AllocPacket (TapReplyPort)) == NULL) goto OPENTAPERR3; if ((Handler= DeviceProc (tapname)) == NULL) { FreePacket (tappkt); OPENTAPERR3: FreeMem (wd, sizeof (WAITINGDATA)); OPENTAPERR2: FreeMem (handle, sizeof (struct FileHandle)); OPENTAPERR1: FreeMem (Bname, OPENTAP_STRSIZE); OPENTAPERR: pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; ReplyPkt (pkt); return; } Lock= (struct FileLock *) IoErr (); CstrtoBSTR (tapname, Bname, OPENTAP_STRSIZE); handle->fh_Pos= -1; handle->fh_End= -1; handle->fh_Type= Handler; /* initialize file handle */ wd->pkt= tappkt; wd->pktinfo.tapwait.clientpkt= pkt; wd->pktinfo.tapwait.handle= handle; /* for HandleTapReply() */ StartTapIO ( tappkt, MODE_NEWFILE, CptrtoBPTR (handle), CptrtoBPTR (Lock), CptrtoBPTR (Bname), Handler ); InsertHead (&tapwaitlist, wd); } /*--------------------------------------------------------------------------- ** A close request for a tap filehandle is initiated. When HandleTapReply() ** gets the reply, it merely discards it. */ static void CloseTap (tapfh) BPTR tapfh; { struct FileHandle *taphandle; struct DosPacket *tappkt; WAITINGDATA *wd; void StartTapIO(); taphandle= (struct FileHandle *) BPTRtoCptr (tapfh); if ((tappkt= AllocPacket (TapReplyPort)) == NULL) goto CLOSETAPERR; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) { FreePacket (tappkt); CLOSETAPERR: FreeMem (taphandle, sizeof (struct FileHandle)); #ifdef DEBUG OS ("!!! ERROR - CloseTap() failed\n"); #endif DEBUG return; } wd->pkt= tappkt; /* don't need ...tapwait.clientpkt */ wd->pktinfo.tapwait.handle= taphandle; /* for HandleTapReply() */ StartTapIO ( tappkt, ACTION_END, taphandle->fh_Arg1, 0, 0, taphandle->fh_Type ); InsertHead (&tapwaitlist, wd); } SHAR_EOF cat << \SHAR_EOF > pipedir.h /**************************************************************************** ** File: pipedir.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.2 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added modifications for allowing shared locks ** on individual pipes. ** 12-Feb-87 Added PipeParentDir. ** 12-Feb-87 Fixed bug in PipeLock(): previously ignored ** lock passed in packet. Bug uncovered when ** pipes became lockable, and thus assignable. ** 27-Mar-87 Added PipeDupLock(). This was missing ** in the original version! */ extern void SetPipeDate ( /* pipe */ ); extern void PipeLock ( /* pkt */ ); extern void PipeDupLock ( /* pkt */ ); extern void PipeUnLock ( /* pkt */ ); extern void PipeExamine ( /* pkt */ ); extern void PipeExNext ( /* pkt */ ); extern void PipeParentDir ( /* pkt */ ); extern void InitLock ( /* lock, key */ ); SHAR_EOF cat << \SHAR_EOF > pipelists.c /**************************************************************************** ** File: pipelists.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ #include <exec/types.h> #include "pipelists.h" /*--------------------------------------------------------------------------- ** pipelists.c ** ----------- ** This module contains functions and macros for list manipulation. ** To use its functions, a PIPELISTNODE must be part of the structure to be ** inserted in a list. A list is identified by a PIPELISTHEADER (not a ** pointer to, but an actual PIPELISTHEADER structure). ** These routines, as implemented, use the fact that a PIPELISTHEADER ** and a PIPELISTNODE have the same struture. Loops are started with the ** scanning pointer referencing the header. This makes processing uniform, ** even when the list is empty. ** ** Visible Functions ** ----------------- ** void InsertHead (headerp, nodep) ** void InsertTail (headerp, nodep) ** void Delete (headerp, nodep) ** ** Macros (in pipelists.h) ** ----------------------- ** InitList (headerp) ** FirstItem (headerp) ** NextItem (nodep) ** ** Local Functions ** --------------- ** - none - */ /*--------------------------------------------------------------------------- ** Insert the node pointed to by "nodep" at the head (front) of the list ** identified by "headerp". */ void InsertHead (headerp, nodep) PIPELISTHEADER *headerp; PIPELISTNODE *nodep; { nodep->next= headerp->head; headerp->head= nodep; } /*--------------------------------------------------------------------------- ** Insert the node pointed to by "nodep" at the tail (end) of the list ** identified by "headerp". */ void InsertTail (headerp, nodep) PIPELISTHEADER *headerp; PIPELISTNODE *nodep; { register PIPELISTNODE *l; for (l= (PIPELISTNODE *) headerp; l->next != NULL; l= l->next) ; l->next= nodep; nodep->next= NULL; } /*--------------------------------------------------------------------------- ** Delete the node pointed to by "nodep" from the list identified by ** "headerp". If the node is not found in the list, nothing is done. */ void Delete (headerp, nodep) PIPELISTHEADER *headerp; PIPELISTNODE *nodep; { PIPELISTNODE *l; for (l= (PIPELISTNODE *) headerp; l->next != NULL; l= l->next) if (l->next == nodep) { l->next= l->next->next; break; } } SHAR_EOF cat << \SHAR_EOF > pipelists.h /**************************************************************************** ** File: pipelists.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) */ typedef struct pipelistnode /* must be first member of list items */ { struct pipelistnode *next; } PIPELISTNODE; typedef struct pipelistheader { struct pipelistnode *head; } PIPELISTHEADER; #define InitList(headerp) ((void) ((headerp)->head= NULL)) #define FirstItem(headerp) ((headerp)->head) #define NextItem(nodep) (((PIPELISTNODE *) (nodep))->next) extern void InsertHead ( /* headerp, nodep */ ); extern void InsertTail ( /* headerp, nodep */ ); extern void Delete ( /* headerp, nodep */ ); SHAR_EOF cat << \SHAR_EOF > pipename.c /**************************************************************************** ** File: pipename.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added conditional compilation for autoname. */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" /*--------------------------------------------------------------------------- ** pipename.c ** ---------- ** This module contains functions related to the parsing of the pipe names. ** ** Visible Functions ** ----------------- ** int ParsePipeName (Bname, nmp, sizep, tapnmp) ** void BSTRtoCstr (BSTRp, str, maxsize) ** void CstrtoBSTR (str, BSTRp, maxsize) ** int inrange (x, lower, upper) ** char uppercase (c) ** char *findchar (str, ch) ** void l_strcpy (to, from) ** char *strdiff (str1, str2) ** char *get_autoname (newflag) (if AUTONAME is true) ** ** Macros (in pipename.h) ** ---------------------- ** isnumeral (c) ** ** Local Functions ** --------------- ** int ParseNum (str, nump) */ /*--------------------------------------------------------------------------- ** ParsePipeName() parses the string "Bname" into three parts: a pipe name, ** a size specification and a tap name. (Bname must be the byte address of a ** BSTR, i.e., a string whose first byte is its length.) The three parts are ** separated by the character PIPE_SPEC_CHAR (defined in pipename.h). ** Assuming that PIPE_SPEC_CHAR is '/', and that '[]' are metacharacters ** which enclose optional parts, the syntax for Bname is [D:][p][/n][/[t]]. ** Here, "D" represents a device name, "p" represents a pipe name, ** "n" represents a number and "t" represents a tap name. ** ParsePipeName() returns nonzero iff "Bname" conforms to the syntax ** and the following restrictions. ** "D:" represents a device name. If it occurs, it is ignored. Notice ** that tap names which contain a ":" force a device name to be specified for ** the pipe. Otherwise, everything up to and including the ":" in the tap ** name will be ignored. ** *nmp returns pointing to a copy of "p", even if it is empty. Default ** pipe names are handled by calling get_autoname(). (This is done by ** OpenPipe() if *nmp returns empty.) ** "n" must begin with a digit. If "n" begins with "0x", it is parsed ** as a hexadecimal number. If it begins with "0" but not "0x", it is parsed ** as an octal number. Otherwise, it is parsed as a decimal number. If the ** size specifier ("/t" above) is not given, *sizep is set to DEFAULT_PIPELEN. ** If the compile-time flag CON_TAP_ONLY is set, "t" may only be a "CON:" ** file specifier, such as "CON:10/10/400/120/TapWindow". If CON_TAP_ONLY is ** not set, string is accepted. If "t" is empty (but the PIPE_SPEC_CHAR was ** given), then a defualt tap name is formed by appending "p" to ** DEFAULT_TAPNAME_PREFIX. If the tap name specifier ("/[t]" above) is not ** given, *tapnmp is set to NULL. */ static char default_tapname_prefix[] = DEFAULT_TAPNAME_PREFIX; static char namebuf[sizeof (default_tapname_prefix) + PIPENAMELEN]; int ParsePipeName (Bname, nmp, sizep, tapnmp) BYTE *Bname; /* reference to BSTR name sent to handler */ char **nmp; /* reference to pipe name pointer */ ULONG *sizep; /* size longword pointer */ char **tapnmp; /* reference to tap name pointer, returns NULL if none */ { char *cp; int ParseNum(); l_strcpy (namebuf, default_tapname_prefix); *nmp= namebuf + (sizeof (default_tapname_prefix) - 1); *sizep= DEFAULT_PIPELEN; *tapnmp= NULL; BSTRtoCstr (Bname, *nmp, PIPENAMELEN); if (*(cp= findchar (*nmp, ':')) == ':') l_strcpy (*nmp, ++cp); /* get rid of "devname:" prefix */ if ( *(cp= findchar (*nmp, PIPE_SPEC_CHAR)) ) /* true if not '\0' */ { *(cp++)= '\0'; /* terminate pipe name */ if (isnumeral (*cp)) { if ( (! ParseNum (cp, sizep)) || (*sizep <= 0) ) return FALSE; if ( *(cp= findchar (cp, PIPE_SPEC_CHAR)) == '\0' ) return TRUE; /* no tap name, but successful anyway */ ++cp; /* skip separator */ } if ( *(*tapnmp= cp) == '\0' ) /* first character of tap name */ *tapnmp= namebuf; /* use default prefix prepended to pipe name */ #if CON_TAP_ONLY else { if ( *(strdiff ("CON:", *tapnmp)) ) /* true if not '\0' */ return FALSE; /* only CON: . . . allowed */ } #endif CON_TAP_ONLY } return TRUE; } /*--------------------------------------------------------------------------- ** BSTRtoCstr() converts the BSTR pointed to by "BSTRp" (a byte address) to ** a null-terminated string, storing the result in the locations pointed to ** by "str". At most "maxsize" bytes will be stored. */ void BSTRtoCstr (BSTRp, str, maxsize) register BYTE *BSTRp; register char *str; unsigned maxsize; { register int i; register int limit; if ((limit= *(BSTRp++)) > ((int) maxsize - 1)) /* leave room for '\0' */ limit= (int) maxsize - 1; for (i= 0; i < limit; ++i) *(str++)= *(BSTRp++); *str= '\0'; } /*--------------------------------------------------------------------------- ** CstrtoBSTR() converts the null-terminated string pointed to by "str" to ** a BSTR located at the byte address "BSTRp". At most "maxsize" bytes will ** be stored. */ void CstrtoBSTR (str, BSTRp, maxsize) register char *str; BYTE *BSTRp; unsigned maxsize; { register char *bp; register int i, limit; bp= BSTRp + 1; limit= maxsize - 1; for (i= 0; i < limit; ++i) if ( (*(bp++)= *(str++)) == '\0' ) break; BSTRp[0]= i; } /*--------------------------------------------------------------------------- ** inrange() returns nonzero iff x is in the range [lower, upper]. ** uppercase() returns the uppercase version of the ASCII character sent. ** These are not implemented as macros to avoid hard-to-find bugs like ** uppercase(c++), where the side-effect occurs more than once. */ int inrange (x, lower, upper) register int x; register int lower; register int upper; { return ((x >= lower) && (x <= upper)); } char uppercase (c) register char c; { return (char) (inrange (c, 'a', 'z') ? (c + ('A' - 'a')) : c); } /*--------------------------------------------------------------------------- ** The null-terminated string "str" is scanned for the character "ch". If ** found, a pointer to its first occurrence in "str" is returned. Otherwise, ** a pointer to the terminating '\0' in "str" is returned. */ char *findchar (str, ch) register char *str; register char ch; { while ((*str != '\0') && (*str != ch)) ++str; return str; /* return position of ch, or end if not found */ } /*--------------------------------------------------------------------------- ** This is just like strcpy(). Its is defined here to avoid including other ** libraries. */ void l_strcpy (to, from) register char *to; register char *from; { STRCPYLOOP: if (*(to++)= *(from++)) goto STRCPYLOOP; } /*--------------------------------------------------------------------------- ** strdiff() returns a pointer to the first difference in the two null- ** terminated strings "str1" and "str2". If no differnce is found, or if ** "str1" is shorter than "str2", then a pointer to '\0' is returned. ** The returned pointer is to a character in "str1". */ char *strdiff (str1, str2) register char *str1; register char *str2; { while ( *str1 && (uppercase (*str1) == uppercase (*str2)) ) { ++str1; ++str2; } return str1; /* return position of first difference, or end of str1 */ } /*--------------------------------------------------------------------------- ** get_autoname() returns a pointer to "autoname". If "newflag" is nonzero, ** autoname is first updated so that it does not conflict with any existing ** pipe name. This is done by looking for a block of ASCII digits in ** "autoname", and incrementing their effective value. "autoname" MUST ** contain such a block of digits. */ #if AUTONAME static char autoname[] = AUTONAME_INIT; char *get_autoname (newflag) BYTE newflag; { char *cp, *cpc; PIPEDATA *FindPipe(); if (newflag) /* then create a new unique pipe name */ { cp= findchar (autoname, '\0'); while (! isnumeral (*cp)) /* find last numeral */ --cp; do { ++(*cp); /* "increment" name */ for (cpc= cp; (! isnumeral (*cpc)); ) /* ripple carry */ { *(cpc--)= '0'; if (! isnumeral (*cpc)) break; /* no more digits */ ++(*cpc); } } while (FindPipe (autoname) != NULL); /* repeat until name is unique */ } return autoname; } #endif AUTONAME /*--------------------------------------------------------------------------- ** ParseNum() parses the null-terminated string pointed to by "str" into a ** number, and stores its value in *nump. ParseNum() returns nonzero iff ** successful. Both '\0' and PIPE_SPEC_CHAR are acceptable terminators for ** the number. ** If the number begins with "0x", it is interpreted as hexadecimal. ** If it begins with "0" but not "0x", it is interpreted as octal. ** Otherwise, it is interpreted as decimal. */ static int ParseNum (str, nump) char *str; ULONG *nump; { int radix = 10; char *digits = "0123456789ABCDEF"; LONG value; if ((*str == '0') && (uppercase (*(str + 1)) == 'X')) { radix= 16; str += 2; } else if (*str == '0') { radix= 8; ++str; } for (*nump= 0; TRUE; ++str) { value= (LONG) findchar (digits, uppercase (*str)) - (LONG) digits; if (! inrange (value, 0, (radix - 1))) break; if (*nump > ((MAX_PIPELEN - value) / radix)) return FALSE; *nump *= radix; *nump += value; } return ( (*str == PIPE_SPEC_CHAR) || (*str == '\0') ); } SHAR_EOF cat << \SHAR_EOF > pipename.h /**************************************************************************** ** File: pipename.h ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added conditional compilation for autoname. */ /*--------------------------------------------------------------------------- ** PIPENAMELEN : this is the maximum length of names ParsePipeName() ** can handle. ** ** DEFAULT_PIPELEN : the default pipe size returned by ParsePipeName() ** if no size is specified. ** ** PIPE_SPEC_CHAR : this is the character used by ParsePipeName() as an ** identifier for specifiers. See pipename.c ** ** DEFAULT_TAPNAME_PREFIX : the prefix for default tap names. See pipename.c ** ** AUTONAME_INIT : Initial value used by get_autoname() to form ** default pipe names. It MUST contain a block of ** digits. See pipename.c. ** This is only used if AUTONAME is true. */ #define PIPENAMELEN 108 #define DEFAULT_PIPELEN 4096 #define PIPE_SPEC_CHAR '/' #define DEFAULT_TAPNAME_PREFIX "CON:10/15/300/70/" #if AUTONAME # define AUTONAME_INIT "$00000000" #endif AUTONAME #define isnumeral(c) inrange ((c), '0', '9') extern int ParsePipeName ( /* Bname, nmp, sizep, tapnmp */ ); extern void BSTRtoCstr ( /* BSTRp, str, maxsize */ ); extern void CstrtoBSTR ( /* str, BSTRp, maxsize */ ); extern int inrange ( /* x, lower, upper */ ); extern char uppercase ( /* c */ ); extern char *findchar ( /* str, ch */ ); extern void l_strcpy ( /* to, from */ ); extern char *strdiff ( /* str1, str2 */ ); #if AUTONAME extern char *get_autoname ( /* newflag */ ); #endif AUTONAME SHAR_EOF cat << \SHAR_EOF > pipesched.c /**************************************************************************** ** File: pipesched.c ** Program: pipe-handler - an AmigaDOS handler for named pipes ** Version: 1.1 ** Author: Ed Puckett qix@mit-oz ** ** Copyright 1987 by EpAc Software. All Rights Reserved. ** ** History: 05-Jan-87 Original Version (1.0) ** 07-Feb-87 Added "lockct" check in CheckWaiting(). */ #include <libraries/dos.h> #include <libraries/dosextens.h> #include <libraries/filehandler.h> #include <exec/exec.h> #include "pipelists.h" #include "pipename.h" #include "pipebuf.h" #include "pipecreate.h" #include "pipesched.h" #include "pipe-handler.h" #if PIPEDIR # include "pipedir.h" #endif PIPEDIR #ifdef DEBUG # include "pipedebug.h" #endif DEBUG /*--------------------------------------------------------------------------- ** pipesched.c ** ----------- ** This module handles pipe I/O scheduling. ** ** Visible Functions ** ----------------- ** void StartPipeIO (pkt, iotype) ** void CheckWaiting (pipe) ** struct DosPacket *AllocPacket (ReplyPort) ** void FreePacket (pkt) ** void StartTapIO (pkt, Type, Arg1, Arg2, Arg3, Handler) ** void HandleTapReply (pkt) ** ** Macros (in pipesched.h) ** ----------------------- ** - none - ** ** Local Functions ** --------------- ** void EndPipeIO (pipe, wd) */ /*--------------------------------------------------------------------------- ** A pipe I/O request is begun. A WAITINGDATA structure is allocated and ** the request is stored in it. It is then stored in the appropriate list ** (readerlist or writerlist) of the pipe. Finally, CheckWaiting is called ** to service requests for that pipe. ** Notice that CheckWaiting() is only called when a new I/O request ** comes in, or when the pipe is closed. At no other time will the state of ** the pipe change in such a way that more requests for it can be honored. */ void StartPipeIO (pkt, iotype) struct DosPacket *pkt; IOTYPE iotype; /* assumed only PIPEREAD or PIPEWRITE */ { PIPEKEY *pipekey; PIPEDATA *pipe; WAITINGDATA *wd; if ((iotype != PIPEREAD) && (iotype != PIPEWRITE)) { pkt->dp_Res2= ERROR_ACTION_NOT_KNOWN; SPIOEXIT: pkt->dp_Res1= -1; ReplyPkt (pkt); return; } pipekey= (PIPEKEY *) pkt->dp_Arg1; pipe= pipekey->pipe; if ((wd= (WAITINGDATA *) AllocMem (sizeof (WAITINGDATA), ALLOCMEM_FLAGS)) == NULL) { pkt->dp_Res2= ERROR_NO_FREE_STORE; goto SPIOEXIT; } pkt->dp_Res2= ERROR_INVALID_LOCK; /* in case not open for iotype */ if (iotype == PIPEREAD) { if ((pipekey->iotype != PIPEREAD) && (pipekey->iotype != PIPERW)) goto SPIOEXIT; InsertTail (&pipe->readerlist, wd); } else /* PIPEWRITE */ { if ((pipekey->iotype != PIPEWRITE) && (pipekey->iotype != PIPERW)) goto SPIOEXIT; InsertTail (&pipe->writerlist, wd); } wd->pkt= pkt; wd->pktinfo.pipewait.reqtype= iotype; wd->pktinfo.pipewait.buf= (BYTE *) pkt->dp_Arg2; /* buffer */ wd->pktinfo.pipewait.len= (ULONG) pkt->dp_Arg3; /* length */ CheckWaiting (pipe); } /*--------------------------------------------------------------------------- ** Read requests for the pipe are satisfied until the pipe is empty or no ** more requests are left. Then, write requests are satisifed until the pipe ** is full or no more requests are left. This alternating process is ** repeated until no further changes are possible. ** Finished requests are sent to EndPipeIO() so that replies may be sent ** to their owners. Aftereward, if the pipe is empty and is not open for ** either read or write, then it is discarded. If it is open for read, but ** is empty and has no write requests and is not open for write, then all ** remaining read requests are returned in their current state. (This ** implements EOF.) A pipe with a positive "lockct" will not be discarded. ** UnLock() is expected to call here so that a previously locked, empty pipe ** will be discarded. */ void CheckWaiting (pipe) PIPEDATA *pipe; { BYTE change; WAITINGDATA *wd; ULONG amt; void EndPipeIO(); #if PIPEDIR SetPipeDate (pipe); #endif PIPEDIR for (change= TRUE; change; ) { change= FALSE; while ( (! (PipebufEmpty (pipe->buf))) && ((wd= (WAITINGDATA *) FirstItem (&pipe->readerlist)) != NULL) ) { amt= MoveFromPipebuf (pipe->buf, wd->pktinfo.pipewait.buf, wd->pktinfo.pipewait.len); if (amt) { wd->pktinfo.pipewait.buf += amt; wd->pktinfo.pipewait.len -= amt; change= TRUE; } if (wd->pktinfo.pipewait.len == 0L) /* then finished with request */ EndPipeIO (pipe, wd); } /* end of readerlist loop */ while ( (! (PipebufFull (pipe->buf))) && ((wd= (WAITINGDATA *) FirstItem (&pipe->writerlist)) != NULL) ) { amt= MoveToPipebuf (pipe->buf, wd->pktinfo.pipewait.buf, wd->pktinfo.pipewait.len); if (amt) { wd->pktinfo.pipewait.buf += amt; wd->pktinfo.pipewait.len -= amt; change= TRUE; } if (wd->pktinfo.pipewait.len == 0L) /* then finished with request */ EndPipeIO (pipe, wd); } /* end of writerlist loop */ } if ( PipebufEmpty (pipe->buf) && (! (pipe->flags & OPEN_FOR_WRITE)) && (FirstItem (&pipe->writerlist) == NULL) ) /* then EOF */ { while ((wd= (WAITINGDATA *) FirstItem (&pipe->readerlist)) != NULL) EndPipeIO (pipe, wd); if (! (pipe->flags & OPEN_FOR_READ)) /* readerlist is now empty */ #if PIPEDIR if (pipe->lockct == 0) #endif PIPEDIR DiscardPipe (pipe); } } /*--------------------------------------------------------------------------- ** This routine returns a finished pipe I/O request. If it is a write ** request to a pipe with a tap, then the same write request is sent to the ** tap, and the reply is deferred until HandleTapReply() gets the tap request ** reply. (This lets the user stop a pipe by typing a character into a ** tap window.) */ static void EndPipeIO (pipe, wd) PIPEDATA *pipe; WAITINGDATA *wd; { struct DosPacket *pkt, *tappkt; struct FileHandle *taphandle; pkt= wd->pkt; pkt->dp_Res1= pkt->dp_Arg3 - wd->pktinfo.pipewait.len; pkt->dp_Res2= 0; if (wd->pktinfo.pipewait.reqtype == PIPEREAD) { Delete (&pipe->readerlist, wd); ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); } else /* must be PIPEWRITE -- reqtype is new PIPERW */ { Delete (&pipe->writerlist, wd); if (pipe->tapfh != 0) /* then write to the pipe tap */ { if ((tappkt= AllocPacket (TapReplyPort)) == NULL) { ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); #ifdef DEBUG OS ("!!! ERROR - Could not allocate packet for tap write\n"); #endif DEBUG } else { wd->pkt= tappkt; /* reuse wd for tap write request */ wd->pktinfo.tapwait.clientpkt= pkt; /* don't need ...tapwait.handle */ taphandle= (struct FileHandle *) BPTRtoCptr (pipe->tapfh); StartTapIO ( tappkt, ACTION_WRITE, taphandle->fh_Arg1, pkt->dp_Arg2, pkt->dp_Arg3, taphandle->fh_Type ); InsertHead (&tapwaitlist, wd); /* for HandleTapReply() */ } } else /* otherwise, return finished packet */ { ReplyPkt (pkt); FreeMem (wd, sizeof (WAITINGDATA)); } } } /*--------------------------------------------------------------------------- ** An exec Message and a DosPacket are allocated, and they are initialized. ** A pointer to the packet is returned, or NULL if it could not be allocated. */ struct DosPacket *AllocPacket (ReplyPort) struct MsgPort *ReplyPort; { struct Message *msg; struct DosPacket *pkt; if ((msg = (struct Message *) AllocMem (sizeof (struct Message), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) return NULL; if ((pkt = (struct DosPacket *) AllocMem (sizeof (struct DosPacket), (ALLOCMEM_FLAGS | MEMF_CLEAR))) == NULL) { FreeMem (msg, sizeof (struct Message)); return NULL; } msg->mn_Node.ln_Type= NT_MESSAGE; msg->mn_Node.ln_Name= (char *) pkt; msg->mn_ReplyPort= ReplyPort; pkt->dp_Link= msg; pkt->dp_Port= ReplyPort; return pkt; } /*--------------------------------------------------------------------------- ** A DosPacket/exec Message pair is freed. */ void FreePacket (pkt) struct DosPacket *pkt; { if (pkt != NULL) { if (pkt->dp_Link != NULL) FreeMem (pkt->dp_Link, sizeof (struct Message)); FreeMem (pkt, sizeof (struct DosPacket)); } } /*--------------------------------------------------------------------------- ** The indicated fields are filled into the packet and it is sent. */ void StartTapIO (pkt, Type, Arg1, Arg2, Arg3, Handler) struct DosPacket *pkt; LONG Type; LONG Arg1; LONG Arg2; LONG Arg3; struct MsgPort *Handler; { pkt->dp_Type= Type; pkt->dp_Arg1= Arg1; pkt->dp_Arg2= Arg2; pkt->dp_Arg3= Arg3; PutMsg (Handler, pkt->dp_Link); } /*--------------------------------------------------------------------------- ** Handle replies from tap I/O requests. These were initiated by OpenTap(), ** CloseTap() and EndPipeIO(). */ void HandleTapReply (pkt) struct DosPacket *pkt; { WAITINGDATA *wd; for (wd= (WAITINGDATA *) FirstItem (&tapwaitlist); wd != NULL; wd= (WAITINGDATA *) NextItem (wd)) if (wd->pkt == pkt) { Delete (&tapwaitlist, wd); break; } if (wd == NULL) { #ifdef DEBUG OS ("!!! ERROR - WAITINGDATA not found in HandleTapReply()\n"); #endif DEBUG FreePacket (pkt); return; /* not found - this should never happen */ } switch (pkt->dp_Type) { case MODE_READWRITE: case MODE_READONLY: case MODE_NEWFILE: /* for a tap open request */ if (pkt->dp_Res1) /* then successful */ OpenPipe (wd->pktinfo.tapwait.clientpkt, pkt->dp_Arg1); else /* couldn't open tap */ { FreeMem (wd->pktinfo.tapwait.handle, sizeof (struct FileHandle)); pkt->dp_Res1= 0; pkt->dp_Res2= ERROR_INVALID_COMPONENT_NAME; ReplyPkt (wd->pktinfo.tapwait.clientpkt); } FreeMem (BPTRtoCptr (pkt->dp_Arg3), OPENTAP_STRSIZE); break; case ACTION_END: /* for a tap close request */ FreeMem (wd->pktinfo.tapwait.handle, sizeof (struct FileHandle)); break; case ACTION_WRITE: /* for a tap write request */ ReplyPkt (wd->pktinfo.tapwait.clientpkt); /* return to client */ break; #ifdef DEBUG default: /* should never happen */ OS ("!!! ERROR - bad packet type in HandleTapReply(), type ="); OL (pkt->dp_Type); NL; #endif DEBUG } FreePacket (pkt); FreeMem (wd, sizeof (WAITINGDATA)); } SHAR_EOF cat << \SHAR_EOF > prelude.asm ; prelude.asm XREF _handler StartModule: DC.L (EndModule-StartModule+3)/4 ; for BCPL linking EntryPoint: LEA SPsave(PC),A0 MOVE.L SP,(A0) ; save initial SP LSL.L #2,D1 ; convert to byte pointer MOVE.L D1,-(SP) ; startup packet pointer ; --- Now, call the loaded handler code. ; --- It is sent the byte address of the startup packet, to which ; --- it should reply. JSR _handler ; call handler code MOVEA.L SPsave(PC),SP ; restore SP RTS SPsave: DC.L 0 ; trailing definitions for BCPL linking CNOP 0,4 ; align to lonword boundary DC.L 0 ; End Marker DC.L 1 ; Global 1 DC.L EntryPoint-StartModule ; Offset DC.L 1 ; Highest Global Used EndModule: END SHAR_EOF cat << \SHAR_EOF > tap_demo dir >P:a/P:b/P:c/P:d/CON:0/0/400/100/a->b->c->d list P: dir >P:a/CON:10/10/100/50/a list P: dir >P:b/CON:20/20/100/50/b list P: dir >P:c/CON:30/30/100/50/c list P: dir >P:d/CON:40/40/100/50/d list P: type P:a list P: type P:b list P: type P:c list P: type P:d list P: SHAR_EOF