Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!seismo!ut-sally!husc6!think!ames!ptsfa!ihnp4!inuxc!iuvax!pur-ee!j.cc.purdue.edu!h.cc.purdue.edu!s.cc.purdue.edu!qix
From: qix@ihlpa.UUCP (Ed Puckett)
Newsgroups: comp.sources.amiga
Subject: P: a pipe-handler (shar 1 of 2)
Message-ID: <465@s.cc.purdue.edu>
Date: Wed, 8-Jul-87 02:50:46 EDT
Article-I.D.: s.465
Posted: Wed Jul  8 02:50:46 1987
Date-Received: Sat, 11-Jul-87 06:03:14 EDT
Sender: doc@s.cc.purdue.edu
Reply-To: doc@s.cc.purdue.edu (Craig Norborg)
Distribution: world
Organization: Purdue University Computing Center
Lines: 1888
Approved: doc@s.cc.purdue.edu


    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:
	
	
	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 .c
    ECHO "File .c does not exist."
    SKIP END
ENDIF

ECHO "-- compiling....c"

IF NOT EXISTS "Ram:cctempdir"
    MAKEDIR Ram:cctempdir
ENDIF

:c/LC1    -oRam:cctempdir/cctemp.q -i:include/ -i:include/lattice/ 

IF NOT EXISTS "Ram:cctempdir/cctemp.q"
    ECHO "Compile failed."
    QUIT 20
ENDIF

:c/LC2 -v -o.o Ram:cctempdir/cctemp

DELETE Ram:cctempdir all

ECHO "-- done compiling ''. --"

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   
#include   
#include   
#include   

#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   
#include   
#include   
#include   

#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   
#include   
#include   

#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   
#include   
#include   
#include   

#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