[comp.sources.amiga] P: a pipe-handler

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