Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!utgpu!water!watmath!clyde!cbosgd!ihnp4!inuxc!iuvax!pur-ee!j.cc.purdue.edu!h.cc.purdue.edu!s.cc.purdue.edu!qix
From: qix@s.cc.purdue.edu.UUCP
Newsgroups: comp.sources.amiga
Subject: P: a pipe-handler (shar 2 of 2)
Message-ID: <466@s.cc.purdue.edu>
Date: Wed, 8-Jul-87 02:51:34 EDT
Article-I.D.: s.466
Posted: Wed Jul  8 02:51:34 1987
Date-Received: Sat, 11-Jul-87 10:45:19 EDT
Sender: doc@s.cc.purdue.edu
Reply-To: doc@s.cc.purdue.edu (Craig Norborg)
Distribution: world
Organization: Purdue University Computing Center
Lines: 1834
Approved: doc@s.cc.purdue.edu


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

#include   "pipelists.h"
#include   "pipename.h"
#include   "pipebuf.h"
#include   "pipecreate.h"
#include   "pipesched.h"
#include   "pipe-handler.h"

#if PIPEDIR
# include   "pipedir.h"
#endif PIPEDIR

#ifdef DEBUG
# include   "pipedebug.h"
#endif DEBUG



/*---------------------------------------------------------------------------
** 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   

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

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

#include   "pipelists.h"
#include   "pipename.h"
#include   "pipebuf.h"
#include   "pipecreate.h"
#include   "pipesched.h"
#include   "pipe-handler.h"

#if PIPEDIR
# include   "pipedir.h"
#endif PIPEDIR

#ifdef DEBUG
# include   "pipedebug.h"
#endif DEBUG



/*---------------------------------------------------------------------------
** 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