;/* "C:Execute" me to compile me with SAS/C 6
Set file StdIO-Handler

sc:c/sc MOD NOICON VERBOSE STRMERGE UNSCHAR NOSTKCHK IGNORE=73 MOD CSRC $file.c
sc:c/slink NOICONS FROM $file.o TO $file LIB lib:amiga.lib,lib:sc.lib,lib:debug.lib VERBOSE SMALLCODE SMALLDATA NODEBUG
makedoc $file.c AUTODOC $file.doc BEGIN "/**LL******" END "******" CONVERTCOMMENTS
delete $file.o
cd /
lha -r a Release:StdIO-Handler_v1.2.lha StdIO-Handler
UnSet file
Quit
*/

/*
** $PROJECT: StdIO-Handler
**
** $VER: StdIO-Handler.c 1.2 (05.04.95)
**
** by
**
** Stefan Ruppert , Windthorststraße 5 , 65439 Flörsheim , GERMANY
**
** (C) Copyright 1995
** All Rights Reserved !
**
** $HISTORY:
**
** 05.04.95 : 001.002 : now I get the Process from the MsgPort structure
** 17.01.95 : 001.001 : works fine I think, also async
** 16.01.95 : 000.001 : initial
*/

/*FS*/ /* $STARTDEFINE: "BumpRev defines"*/
#define VERSION  1
#define REVISION 2
#define DATE "5.4.95"
#define VERS "StdIO-Handler 1.2"
#define VSTRING "StdIO-Handler 1.2 (5.4.95)\r\n"
#define VERSTAG "\0$VER: StdIO-Handler 1.2 (5.4.95)"
/*FE*/ /* $ENDDEFINE:   */

/*GB*** StdIO-Handler ********************************************************

$VER: StdIO-Handler.doc

NAME
    StdIO-Handler - Handler for StdIO access

FUNCTION
    In december 1994 I got a PIPE package and I was very supprised about the
    easy implementation of these pipes. So I used this very often with gzip
    and tar. But after some tries, to use the pipes with standard Amiga CLI
    programs, I found no trick to redirect the input and output to the pipes.
    At this point I had a idea of writing a transparent handler. Thus the
    StdIO-Handler was born, using some example code from the Device-Handler
    by Matthias Scheler and Marius Gröger (Thanks for the available code !).
    The only thing you have to do, is to copy the STDIO file to your
    Devs:DosDrivers/ directory and the handler to your L: directory.
    After this you can use it like all other devices.
    Note: If you use this handler with the pipes, you can't use tools, which
          Seek's in the files (see the appropriate pipes doc) !

    Example Mount File :

    \* Standard-IO Handler *\
    Handler     = L:StdIO-Handler
    Stacksize   = 1000
    Priority    = 5
    GlobVec     = -1


EXAMPLES
    Output the current direcory in sorted order :
    List | Sort Stdio: to Stdio:

    Output all files and subdirs last modified on august 1994 :
    List ALL | search stdio: Aug-94 NONUM

    List ALL | search stdio: -s--rw-d NONUM

AUTHOR
    Stefan Ruppert
    Windthorststraße 5
    65439 Flörsheim am Main
    GERMANY
    EMail: ruppert@vs3.informatik.fh-wiesbaden.de

******************************************************************************
*
*/

/*FS*/ /*"Includes"*/
#include <exec/memory.h>
#include <exec/execbase.h>
#include <devices/trackdisk.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dos.h>

#define __USE_SYSBASE 42

#include <clib/exec_protos.h>
#include <pragmas/exec_pragmas.h>

#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>

#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>

#include <string.h>

#include <debug.h>
#include <register.h>
/*FE*/

/*FS*/ /*"Structures"*/
typedef struct GlobalData {
   struct Library *gd_SysBase;
   struct Library *gd_DOSBase;
   struct Library *gd_UtilityBase;
   struct MsgPort *gd_Port;
   struct MsgPort *gd_ReplyPort;
   struct DosList *gd_DosList;
   struct Process *gd_We;
} *GD;

struct StdIOData
{
   struct StandardPacket Packet;
   struct MsgPort *Handler;
   struct DosPacket *Pkt;
   BPTR StdIO;
};

#define SysBase (gd->gd_SysBase)
#define DOSBase (gd->gd_DOSBase)
#define UtilityBase (gd->gd_UtilityBase)
/*FE*/

/*FS*/ /*"entry()"*/
static ULONG main(void);
static RegCall GetA4 ULONG entry(REGA0 UBYTE *line,REGD0 ULONG len)
{
   return(main());
}
const static UBYTE version[]= VERSTAG;
/*FE*/

/*FS*/ static struct DosPacket *WaitDosPacket(GD gd, struct MsgPort *port)
{
   WaitPort(port);
   return (struct DosPacket *)(GetMsg(port)->mn_Node.ln_Name);
}
/*FE*/
/*FS*/ static void ReplyDosPacket(GD gd, struct MsgPort *port, struct DosPacket *Packet,LONG Res1,LONG Res2)
{
   struct MsgPort *reply = Packet->dp_Port;

   ENTERING;

   Packet->dp_Port = port;
   Packet->dp_Link->mn_Node.ln_Name = (char *)Packet;
   Packet->dp_Res1 = Res1;
   Packet->dp_Res2 = Res2;

   PutMsg (reply, Packet->dp_Link);

   LEAVING;
}
/*FE*/

/*FS*/ static void SendDosPacket(GD gd,struct DosPacket *Pkt)
{
   struct StdIOData *stdio = (struct StdIOData *) Pkt->dp_Arg1;

   ENTERING;

   stdio->Pkt = Pkt;

   stdio->Packet.sp_Pkt.dp_Port = gd->gd_ReplyPort;
   stdio->Packet.sp_Pkt.dp_Res1 = 0;
   stdio->Packet.sp_Pkt.dp_Res2 = 0;
   stdio->Packet.sp_Pkt.dp_Arg2 = Pkt->dp_Arg2;
   stdio->Packet.sp_Pkt.dp_Arg3 = Pkt->dp_Arg3;
   PutMsg(stdio->Handler,&stdio->Packet.sp_Msg);

   LEAVING;
}
/*FE*/

/*FS*/ static BOOL openres(GD gd)
{
   BOOL rc = FALSE;

   DB(("entered\n"));

   DOSBase          =
   UtilityBase      = NULL;

   gd->gd_Port      =
   gd->gd_ReplyPort = NULL;

   if (SysBase->lib_Version >= 37)
   {
      if (DOSBase = OpenLibrary("dos.library", 37))
      {
         if (UtilityBase = OpenLibrary("utility.library", 37))
         {
            if (gd->gd_Port = CreateMsgPort())
            {
               if(gd->gd_ReplyPort = CreateMsgPort())
               {
                 rc = TRUE;
               } else
                  DB(("no reply port\n"));
            }
            else DB(("no device port\n"));
         }
         else DB(("no utility V37\n"));
      }
      else DB(("no dos V37\n"));
   }
   else DB(("no exec V37\n"));

   DB(("left: %ld\n",rc));

   return rc;
}
/*FE*/
/*FS*/ static void closeres(GD gd)
{
   DB(("freeing port\n"));
   if (gd->gd_Port) DeleteMsgPort(gd->gd_Port);

   if (gd->gd_ReplyPort) DeleteMsgPort(gd->gd_ReplyPort);

   DB(("closing dos\n"));
   if (DOSBase) CloseLibrary(DOSBase);

   DB(("closing utility\n"));
   if (UtilityBase) CloseLibrary(UtilityBase);
}
/*FE*/

/*FS*/ static ULONG main(void)
{
   struct GlobalData GlobalData;
   struct GlobalData *gd;

   struct DosPacket *Pkt;
   BOOL Done;
   ULONG OpenCnt;

   gd = &GlobalData;

   DB(("Hello, this is the StdIO: Device\n"));

   SysBase = *(struct Library**)4L;

   if ((gd->gd_We = (struct Process *)FindTask(NULL))->pr_CLI) return RETURN_FAIL;

   DB(("waiting for startup-packet...\n"));
   Pkt = WaitDosPacket(gd, &gd->gd_We->pr_MsgPort);

   if (openres(gd))
   {
      struct Process *proc;
      struct FileHandle *fh;
      struct StdIOData *retio;
      ULONG portmask   = (1<<gd->gd_Port->mp_SigBit);
      ULONG replymask  = (1<<gd->gd_ReplyPort->mp_SigBit);
      ULONG recvd;

      gd->gd_DosList = (struct DosList *)BADDR(Pkt->dp_Arg3);
      gd->gd_DosList->dol_Task = gd->gd_Port;

      DB(("replying startup-packet...\n"))
      ReplyDosPacket(gd, gd->gd_Port, Pkt, DOSTRUE, Pkt->dp_Res2);

      Done=FALSE;
      OpenCnt=0L;

      DB(("entering mainloop\n"));

      while (!Done)
      {
         recvd = Wait(portmask | replymask);

         if(recvd & replymask)
         {
            while((retio = (struct StdIOData *) GetMsg(gd->gd_ReplyPort)))
            {
               LONG Bytes = retio->Packet.sp_Pkt.dp_Res1;

               DB(("replied stdiodata : %lx , Pkt : %lx\n",retio,retio->Pkt));

               if (Bytes < 0L)
                  ReplyDosPacket(gd, gd->gd_Port,retio->Pkt, -1, retio->Packet.sp_Pkt.dp_Res2);
               else
                  ReplyDosPacket(gd, gd->gd_Port,retio->Pkt, Bytes, 0);
            }
         }

         if(recvd & portmask)
         {
            struct Message *msg;

            while((msg = GetMsg(gd->gd_Port)))
            {
               Pkt = (struct DosPacket *) msg->mn_Node.ln_Name;
               DB(("got packet %ld\n",Pkt->dp_Type));

               Pkt->dp_Res1 = DOSFALSE;
               Pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;

               switch (Pkt->dp_Type)
               {
               case ACTION_FINDINPUT:
               case ACTION_FINDOUTPUT:
                  fh = BADDR(Pkt->dp_Arg1);
                  fh->fh_Arg1 = 0L;


                  proc = (struct Process *) Pkt->dp_Port->mp_SigTask;
                  if((Pkt->dp_Port->mp_Flags == PA_SIGNAL) && (proc->pr_Task.tc_Node.ln_Type == NT_PROCESS))
                  {
                     struct StdIOData *stdio;
                     if((stdio = AllocVec(sizeof(struct StdIOData),MEMF_ANY)))
                     {
                        if(Pkt->dp_Type == ACTION_FINDINPUT)
                        {
                           stdio->StdIO = proc->pr_CIS;
                           stdio->Packet.sp_Pkt.dp_Type = ACTION_READ;
                        } else
                        {
                           stdio->StdIO = proc->pr_COS;
                           stdio->Packet.sp_Pkt.dp_Type = ACTION_WRITE;
                        }
                        fh  = BADDR(stdio->StdIO);
                        stdio->Handler = fh->fh_Type;
                        stdio->Packet.sp_Pkt.dp_Link          = &stdio->Packet.sp_Msg;
                        stdio->Packet.sp_Pkt.dp_Arg1          = fh->fh_Arg1;
                        stdio->Packet.sp_Msg.mn_Node.ln_Name  = (STRPTR)&stdio->Packet.sp_Pkt;
                        stdio->Packet.sp_Msg.mn_Node.ln_Type  = NT_MESSAGE;
                        stdio->Packet.sp_Msg.mn_Length        = sizeof(struct StandardPacket);
                        stdio->Packet.sp_Msg.mn_ReplyPort     = gd->gd_ReplyPort;

                        fh = BADDR(Pkt->dp_Arg1);
                        fh->fh_Arg1 = (LONG) stdio;

                        DB(("process : %s , stdio : %lx , type %ld \n",proc->pr_Task.tc_Node.ln_Name,stdio->StdIO,stdio->Packet.sp_Pkt.dp_Type));
                        OpenCnt++;
                        ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSTRUE, 0);
                     } else
                        ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_NO_FREE_STORE);
                  } else
                     ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_OBJECT_NOT_FOUND);
                  break;
               case ACTION_END:
                  {
                     struct StdIOData *stdio = (struct StdIOData *) Pkt->dp_Arg1;

                     if(stdio)
                     {
                        OpenCnt--;
                        FreeVec(stdio);
                        ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSTRUE, 0);
                     }
                  }
                  break;
               case ACTION_WRITE:
               case ACTION_READ:
                  {
                     struct StdIOData *stdio = (struct StdIOData *)Pkt->dp_Arg1;

                     if(stdio && stdio->Packet.sp_Pkt.dp_Type == Pkt->dp_Type)
                        SendDosPacket(gd,Pkt);
                  }
                  break;
               case ACTION_IS_FILESYSTEM:
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE, 0);
                  break;
               case ACTION_SEEK:
               case ACTION_SET_FILE_SIZE:
                  ReplyDosPacket(gd, gd->gd_Port,Pkt, -1,ERROR_ACTION_NOT_KNOWN);
                  break;
               case ACTION_DIE:
                  DB(("opencnt = %ld\n",OpenCnt));

                  if (OpenCnt==0L)
                  {
                     if (IsListEmpty(&gd->gd_Port->mp_MsgList))
                     {
                        gd->gd_DosList->dol_Task = NULL;

                        ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSTRUE, 0);

                        Done=TRUE;
                     }
                     else ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_OBJECT_IN_USE);
                  }
                  else ReplyDosPacket(gd, gd->gd_Port,Pkt, DOSFALSE,ERROR_OBJECT_IN_USE);
                  break;
               default:
                  ReplyDosPacket(gd, gd->gd_Port, Pkt, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
                  break;
               }
            }
         }
      }
   }
   else
      ReplyDosPacket(gd, &gd->gd_We->pr_MsgPort, Pkt, DOSFALSE, ERROR_NO_FREE_STORE);

   closeres(gd);

   DB(("returning\n"));

   return(RETURN_OK);
}
/*FE*/

