/*
** $VER: exec-handler.c 1.0 (15 Jun 1995)
**
** exec-handler - job execution
**
** (C) Copyright 1995 Marius Gröger
**     All Rights Reserved.
*/

/*F*/ /* history */
/* $HISTORY:
**
** 15 Jun 1995 : 001.000 :  created
*/
/*E*/

#define DEBUG 0

/*F*/ /* includes */

   /* system protoypes and pragmas */
   /* ensure to use the symbol SysBase and not the hardcoded AbsExecBase */
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>

   /* system header files */
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/semaphores.h>
#include <devices/trackdisk.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dos.h>
#include <dos/dostags.h>

   /* compiler header files */
#include <string.h>

   /* private header files */
#include "debug.h"
#include "loadseg.h"
#include "compiler.h"
#include "exec-handler_rev.h"

/*E*/

/*F*/ /* global static storage */

typedef struct GlobalData
{
   struct Library   *gd_SysBase;
   struct Library   *gd_DOSBase;
   struct Library   *gd_UtilityBase;
   struct MsgPort   *gd_PktPort;
   struct MsgPort   *gd_ThreadsPort;
   struct DosList   *gd_DosList;
   struct Process   *gd_We;
   volatile UWORD    gd_OpenCnt;       /* for now, this can only be 0 or 1 */
} *GD;

#ifdef __SASC
     /* redirect all shared library bases to our device base */
#  define SysBase      gd->gd_SysBase
#  define DOSBase      gd->gd_DOSBase
#  define UtilityBase  gd->gd_UtilityBase
     /* This macro declares a local variable which temporary gets
        SysBase directly from AbsExecBase. */
#  define LOCALSYSBASE struct { VOID *gd_SysBase; } *gd = (VOID*)0x4
#else
#  error Please define library bases for your compiler
#endif
/*E*/
/*F*/ /* handler declarations */

#define STREAMNAME_TEMPLATE "NAME,S=STACKSIZE/K/N,CWD=CURRENTWORKDIR/K,WIN=WINDOW/K,ARGS=ARGUMENTS/K,HOME=HOMEDIR/K,PRI=PRIORITY/K/N"

struct StreamOpts
{
   UBYTE *so_Name;
   LONG  *so_StackSize;
   UBYTE *so_CurrentWorkDir;
   UBYTE *so_Window;
   UBYTE *so_Arguments;
   UBYTE *so_HomeDir;
   LONG  *so_Priority;
};

#define BCPL_MAXNAME 256
struct PrivateStreamData
{
   struct Message          psd_ExecMessage;   /* will be sent to children */
   GD                      psd_GlobalData;    /* and they'll need this */
   struct Process         *psd_LoadSegThread;
   struct StreamOpts       psd_Param;         /* ReadArgs() argument */
   struct SignalSemaphore  psd_Protect;       /* arbitrate buffer mgmnt */
   volatile int            psd_ExecuteIt : 1; /* is ready to be executed */
   volatile int            psd_Error : 1;     /* error in LoadSegThread() */
   volatile int            psd_Ready : 1;     /* file has been closed */
   volatile BPTR           psd_SegList;       /* AmigaDOS segment list */
   volatile UBYTE         *psd_Buffer;        /* current Write() buffer */
   volatile LONG           psd_Len;           /* current buffer's size */
   volatile LONG           psd_Pos;           /* pointer from readstub() */
   ULONG                   psd_BufferSignal;  /* simple IPC between handler */
                                              /* and LoadSegThread() */
   ULONG                   psd_ExecSignal;    /* simple IPC between handler */
                                              /* and child task */
   LONG                    psd_StackSize;     /* execution stacksize */
   UBYTE                  *psd_Window;        /* execution I/O window */
   BPTR                    psd_CWD;           /* execution cwd */
   BPTR                    psd_HomeDir;       /* execution PROGDIR: */
   UBYTE                  *psd_ProcName;      /* execution process's name */
   LONG                    psd_ChildRC;       /* execution process's retval */
   UBYTE                  *psd_Arguments;     /* executions proc's args */
   LONG                    psd_Priority;      /* execution proc's priority */
};

typedef struct PrivateStreamData *PSD;

#define DEFAULT_STACKSIZE 4096
#define DEFAULT_WINDOW "CON:0/0/640/150/Output/AUTO/WAIT/CLOSE"
#define DEFAULT_NAME "Object"
#define DEFAULT_CURRENTWORKDIR ""
#define DEFAULT_HOMEDIR ""
#define DEFAULT_PRIORITY 0
#define DEFAULT_ARGUMENTS ""

/*E*/

/*F*/ /* prototypes */
STATIC struct DosPacket *WaitDosPacket(GD gd, struct MsgPort *port);
STATIC struct DosPacket *GetDosPacket(GD gd, struct MsgPort *port);
STATIC VOID ReplyDosPacket(GD gd, struct MsgPort *port, struct DosPacket *Packet,LONG Res1,LONG Res2);
STATIC VOID ReplyDosPacketQ(GD gd, struct MsgPort *port, struct DosPacket *Packet);

STATIC BOOL GetXDULONG(GD gd, UBYTE *s, LONG *number);
STATIC VOID Stream2RDArgs(UBYTE *buffer);
STATIC BOOL StreamSpecs(GD gd, PSD psd, BPTR streamname, LONG *res);
STATIC GD creategd(VOID);
STATIC BOOL openres(GD gd);
STATIC VOID closeres(GD gd);
GLOBAL LONG main(VOID);

STATIC VOID Action(GD gd, struct DosPacket *Pkt);
STATIC VOID ACTION_FindOutput(GD gd, struct DosPacket *Pkt);
STATIC VOID ACTION_End(GD gd, struct DosPacket *Pkt);
STATIC VOID ACTION_Write(GD gd, struct DosPacket *Pkt);

STATIC VOID executeJob(GD gd, PSD psd);

STATIC SAVEDS VOID LoadSegThread(VOID);

/*E*/

/* ----------------------------- Entry Point ------------------------------ */

/*F*/ GLOBAL LONG SAVEDS main(VOID)
/*
** don't remove GLOBAL, if you don't want to the complete
** source to get optimized out of existence :-)
*/
{
   GD gd;
   struct DosPacket *Pkt;
   LONG rc = RETURN_OK;
   ULONG sigPktPort, sigThreadsPort, sigmask;

   d(("this is the EXEC handler, compiled on "__DATE__"\n"));

   if (gd = creategd())
   {
         /* started from CLI really is no good... */
      if ((gd->gd_We = (struct Process *)FindTask(NULL))->pr_CLI) return RETURN_FAIL;

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

      if (openres(gd))
      {
         gd->gd_DosList = (struct DosList *)BADDR(Pkt->dp_Arg3);
         /* I _don't_ enter the gd->gd_PktPort into the DosList node,
         ** but rather return it in dp_Arg4. This is a new 2.0 feature.
         */
         Pkt->dp_Arg4 = (LONG)gd->gd_PktPort;
         ReplyDosPacket(gd, gd->gd_PktPort, Pkt, DOSTRUE, Pkt->dp_Res2);

         d(("replied startup-packet...\n"));

         sigPktPort = 1L << gd->gd_PktPort->mp_SigBit;
         sigThreadsPort = 1L << gd->gd_ThreadsPort->mp_SigBit;

         d(("main loop\n"));

         do {
            sigmask = Wait(sigPktPort | sigThreadsPort);

            if (sigmask & sigThreadsPort)
            {
               PSD psd;

               psd = (PSD)GetMsg(gd->gd_ThreadsPort);
               d(("ThreadPort event, got psd at 0x%lx\n",psd));

                  /* ensure loadseg thread has finished */
               ObtainSemaphore(&psd->psd_Protect);

                  /* freeing PrivateStreamData block */
               if (psd->psd_CWD) UnLock(psd->psd_CWD);
               if (psd->psd_HomeDir) UnLock(psd->psd_HomeDir);
               if (psd->psd_ProcName) FreeVec(psd->psd_ProcName);
               if (psd->psd_Window) FreeVec(psd->psd_Window);
               if (psd->psd_Arguments) FreeVec(psd->psd_Arguments);
               ReleaseSemaphore(&psd->psd_Protect);

               FreeVec(psd);

                  /* let us out of this... */
               gd->gd_OpenCnt--;
            }

            if (sigmask & sigPktPort)
            {
               Pkt = GetDosPacket(gd, gd->gd_PktPort);

               Action(gd, Pkt);

               ReplyDosPacketQ(gd, gd->gd_PktPort, Pkt);
            }
         } while (gd->gd_OpenCnt > 0);
      }
      else
      {
         ReplyDosPacket(gd, &gd->gd_We->pr_MsgPort, Pkt, DOSFALSE, ERROR_NO_FREE_STORE);
         rc = RETURN_FAIL;
      }

      d(("freeing resources\n"));

      closeres(gd);
   }
   else rc = RETURN_FAIL;

   d(("returning\n"));
   return rc;
}
const STATIC UBYTE version[]=VERSTAG;
/*E*/

/*F*/ STATIC GD creategd(VOID)
{
   GD new;
   LOCALSYSBASE;

   if (new = AllocVec(sizeof(struct GlobalData), MEMF_ANY | MEMF_CLEAR))
      new->gd_SysBase = SysBase;

   return new;
}
/*E*/
/*F*/ STATIC BOOL openres(GD gd)
{
   BOOL rc = FALSE;

   d(("entered\n"));

   DOSBase = NULL;
   UtilityBase = NULL;
   gd->gd_PktPort = NULL;
   gd->gd_ThreadsPort = NULL;

   if (SysBase->lib_Version >= 37)
   {
      if (DOSBase = OpenLibrary("dos.library", 37))
      {
         if (UtilityBase = OpenLibrary("utility.library", 37))
         {
            if (gd->gd_PktPort = CreateMsgPort())
            {
               if (gd->gd_ThreadsPort = CreateMsgPort())
               {
                  d(("all global resources allocated\n"));
                  rc = TRUE;
               }
               else d(("no threads port\n"));
            }
            else d(("no handler port\n"));
         }
         else d(("no utility V37\n"));
      }
      else d(("no dos V37\n"));
   }
   else d(("no exec V37\n"));

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

   return rc;
}
/*E*/
/*F*/ STATIC VOID closeres(GD gd)
{
   d(("freeing ports\n"));
   if (gd->gd_ThreadsPort) DeleteMsgPort(gd->gd_ThreadsPort);
   if (gd->gd_PktPort) DeleteMsgPort(gd->gd_PktPort);

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

   d(("closing utility\n"));
   if (UtilityBase) CloseLibrary(UtilityBase);

   FreeVec(gd);
}
/*E*/

/* ----------------------- Packet Support Routines ------------------------ */

/*F*/ STATIC struct DosPacket *GetDosPacket(GD gd, struct MsgPort *port)

{
   return (struct DosPacket *)(GetMsg(port)->mn_Node.ln_Name);
}
/*E*/
/*F*/ STATIC struct DosPacket *WaitDosPacket(GD gd, struct MsgPort *port)

{
   WaitPort(port);
   return GetDosPacket(gd, port);
}
/*E*/
/*F*/ STATIC VOID ReplyDosPacketQ(GD gd, struct MsgPort *port, struct DosPacket *Packet)
{
   struct MsgPort *reply = Packet->dp_Port;
   Packet->dp_Port = port;
   Packet->dp_Link->mn_Node.ln_Name = (char *)Packet;
   PutMsg(reply, Packet->dp_Link);
}
/*E*/
/*F*/ STATIC VOID ReplyDosPacket(GD gd, struct MsgPort *port, struct DosPacket *Packet, LONG Res1,LONG Res2)
{
   Packet->dp_Res1 = Res1;
   Packet->dp_Res2 = Res2;
   ReplyDosPacketQ(gd, port, Packet);
}
/*E*/

/* ------------------------- Stream Name Handling ------------------------- */

/*F*/ STATIC UBYTE *clonestr(GD gd, UBYTE *source)
{
   UBYTE *new;

   if (new = AllocVec((ULONG)(strlen(source)+2), MEMF_PUBLIC | MEMF_CLEAR))
      strcpy((char*)new, (char*)source);

   return new;
}
/*E*/
/*F*/ STATIC BOOL GetXDULONG(GD gd, UBYTE *s, LONG *number)
{

#define IsDigit(c) ((c) >= '0' && (c) <= '9'))
#define IsXDigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F'))

   BOOL hex, rc = TRUE;
   ULONG num;
   UBYTE c;
   WORD len;

   if (len = strlen(s))
   {
      if (!Strnicmp(s, "0x", 2))
      {
         hex = TRUE;
         s += 2;
      }
      else if (!Strnicmp(s, "$", 2))
      {
         hex = TRUE;
         s++;
      }
      else hex = FALSE;

      for(num=0;*s;s++)
      {
         c = ToUpper((ULONG)*s);
         if (hex)
         {
            if (c >= '0' && c <= '9') num = num * 16 + c - '0';
            else if (c >= 'A' && c <= 'F') num = num * 16 + c - '0';
            else break;
         }
         else
         {
            if (c >= '0' && c <= '9') num = num * 10 + c - '0';
            else break;
         }
      }

      if (*s)
      {
         if (c == 'K') num *= 1024;
         else if (c == 'M') num *= 1024*1024;
         else rc = FALSE;
      }
   }
   else rc = FALSE;

   if (rc) *number = num;

   return rc;
}
/*E*/
/*F*/ STATIC VOID Stream2RDArgs(UBYTE *buffer)
{
   UBYTE *p;
   enum { s_conv, s_esc, s_quote } s;

   for (p = buffer, s = s_conv; *p; p++)
      switch(s)
      {
         case s_conv:                      /* conversion state */
            if (*p == '.') *p = '=';
            else if (*p == '\\') *p = '/';
            else if (*p == '/') *p = '\040';
            else if (*p == '"') s = s_quote;
         break;
         case s_esc:                       /* (asterisk-)escape state */
            s = s_quote;
         break;
         case s_quote:                     /* quotation state */
            if (*p == '*') s = s_esc;
            if (*p == '"') s = s_conv;
         break;
      }
}
/*E*/
/*F*/ STATIC BOOL StreamSpecs(GD gd, PSD psd, BPTR streamname, LONG *res)
{
   UBYTE *temp, *bstr, *start;
   BOOL rc = FALSE;
   struct RDArgs *inrda, *rda;

   memset(&psd->psd_Param, '\0', (int)sizeof(psd->psd_Param));

   bstr = (UBYTE *)BADDR(streamname);

   if (temp = AllocVec((ULONG)bstr[0]+2, MEMF_ANY))
   {
      if (inrda = AllocDosObject(DOS_RDARGS, NULL))
      {
         strncpy(temp, bstr+1, bstr[0]);
         temp[bstr[0]] = '\n';
         temp[bstr[0]+1] = '\0';
         d(("temp is  (%s)\n",temp));

         if (start = (UBYTE*)strchr((char*)temp, ':'))
         {
            Stream2RDArgs(++start);

            inrda->RDA_Source.CS_Buffer = start;
            inrda->RDA_Source.CS_Length = strlen(start);
            inrda->RDA_Source.CS_CurChr = 0;

            d(("readargs(%s)\n",start));
            if (rda = ReadArgs((UBYTE*)STREAMNAME_TEMPLATE, (LONG*)&psd->psd_Param, inrda))
            {
               rc = TRUE;

               d(("readargs ok\n"));

               psd->psd_StackSize = (psd->psd_Param.so_StackSize != NULL) ?
                             (*psd->psd_Param.so_StackSize) : DEFAULT_STACKSIZE;

               if (psd->psd_Param.so_CurrentWorkDir != NULL)
               {
                  if ((psd->psd_CWD = Lock(psd->psd_Param.so_CurrentWorkDir,
                                                                  ACCESS_READ)) == NULL)
                  {
                     *res = IoErr();
                     rc = FALSE;
                  }
               }
               else
               {
                  if ((psd->psd_CWD = Lock(DEFAULT_CURRENTWORKDIR, ACCESS_READ)) == NULL)
                  {
                     *res = IoErr();
                     rc = FALSE;
                  }
               }

               if (psd->psd_Param.so_HomeDir != NULL)
               {
                  if ((psd->psd_HomeDir = Lock(psd->psd_Param.so_HomeDir, ACCESS_READ)) == NULL)
                  {
                     *res = IoErr();
                     rc = FALSE;
                  }
               }
               else
               {
                  if ((psd->psd_HomeDir = Lock(DEFAULT_HOMEDIR, ACCESS_READ)) == NULL)
                  {
                     *res = IoErr();
                     rc = FALSE;
                  }
               }

               if (rc)
                  if (!(psd->psd_Window = clonestr(gd, psd->psd_Param.so_Window ?
                                     psd->psd_Param.so_Window : (UBYTE*)DEFAULT_WINDOW)))
                  {
                     *res = ERROR_NO_FREE_STORE;
                     rc = FALSE;
                  }

               if (rc)
                  if (!(psd->psd_ProcName = clonestr(gd, psd->psd_Param.so_Name ?
                                     psd->psd_Param.so_Name : (UBYTE*)DEFAULT_NAME)))
                  {
                     *res = ERROR_NO_FREE_STORE;
                     rc = FALSE;
                  }

               if (rc)
                  if (!(psd->psd_Arguments = clonestr(gd, psd->psd_Param.so_Arguments ?
                                     psd->psd_Param.so_Arguments : (UBYTE*)DEFAULT_ARGUMENTS)))
                  {
                     *res = ERROR_NO_FREE_STORE;
                     rc = FALSE;
                  }
                  else
                     psd->psd_Arguments[strlen((char*)psd->psd_Arguments)] = '\n';

               if (rc)
                  if (psd->psd_Param.so_StackSize)
                  {
                     if (*psd->psd_Param.so_StackSize < 4096)
                     {
                        *res = ERROR_INVALID_COMPONENT_NAME;
                        rc = FALSE;
                     }
                     else psd->psd_StackSize = *psd->psd_Param.so_StackSize;
                  }
                  else
                     psd->psd_StackSize = DEFAULT_STACKSIZE;

               if (rc)
                  if (psd->psd_Param.so_Priority)
                  {
                     if ((*psd->psd_Param.so_Priority < -128) || (*psd->psd_Param.so_Priority > 127))
                     {
                        *res = ERROR_INVALID_COMPONENT_NAME;
                        rc = FALSE;
                     }
                     else psd->psd_Priority = *psd->psd_Param.so_Priority;
                  }
                  else
                     psd->psd_Priority = DEFAULT_PRIORITY;

               FreeArgs(rda);
            }
            else
            {
               d(("ReadArgs() failed\n"));
               *res = IoErr();
            }
         }
         else
         {
            d(("no proper name\n"));
            *res = ERROR_INVALID_COMPONENT_NAME;
         }

         FreeDosObject(DOS_RDARGS, inrda);
      }
      else
      {
         d(("no RDArgs\n"));
         *res = ERROR_NO_FREE_STORE;
      }

      FreeVec(temp);
   }
   else
   {
      d(("no more memory\n"));
      *res = ERROR_NO_FREE_STORE;
   }

   return rc;
}
/*E*/

/* -------------------------- Packet Processing --------------------------- */

/*F*/ STATIC VOID Action(GD gd, struct DosPacket *Pkt)
{
   d(("got packet %ld\n",Pkt->dp_Type));

   Pkt->dp_Res1 = 0;
   Pkt->dp_Res2 = 0;

   switch (Pkt->dp_Type)
   {
      case ACTION_FINDOUTPUT:
         ACTION_FindOutput(gd, Pkt);
      break;

      case ACTION_WRITE:
         ACTION_Write(gd, Pkt);
      break;

      case ACTION_SEEK:
      case ACTION_SET_FILE_SIZE:
         Pkt->dp_Res1 = -1;
         Pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
      break;

      case ACTION_SET_PROTECT:
         Pkt->dp_Res1 = DOSTRUE;
         Pkt->dp_Res2 = 0;
      break;

      case ACTION_END:
         ACTION_End(gd, Pkt);
         Pkt->dp_Res1 = DOSTRUE;
         Pkt->dp_Res2 = 0;
      break;

      case ACTION_IS_FILESYSTEM:
         Pkt->dp_Res1 = DOSFALSE;
         Pkt->dp_Res2 = 0;
      break;

      default:
         Pkt->dp_Res1 = DOSFALSE;
         Pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
      break;
   }

   d(("Packet %ld, Res1 = %ld, Res2 = %ld\n",Pkt->dp_Type, Pkt->dp_Res1, Pkt->dp_Res2));
}
/*E*/
/*F*/ STATIC VOID ACTION_FindOutput(GD gd, struct DosPacket *Pkt)
{
   struct FileHandle *FH;
   PSD psd;

   d(("ActionFindOutput\n"));

   Pkt->dp_Res1 = DOSFALSE;                  /* pessimistic assumption */
   FH = (struct FileHandle *)BADDR(Pkt->dp_Arg1);
   FH->fh_Port = (struct MsgPort *)FALSE;    /* means we are not interactive */
   FH->fh_Arg1 = 0L;                         /* error preset */

   if (psd = AllocVec(sizeof(struct PrivateStreamData),MEMF_PUBLIC | MEMF_CLEAR))
   {
      d(("alloced psd at 0x%lx\n",psd));

      if (StreamSpecs(gd, psd, (BPTR)Pkt->dp_Arg3, &Pkt->dp_Res2))
      {
            /* initialize non-zero fields: */
         psd->psd_ExecMessage.mn_Length = sizeof(struct PrivateStreamData);
         psd->psd_GlobalData = gd;
         InitSemaphore(&psd->psd_Protect);
         psd->psd_BufferSignal = SIGBREAKF_CTRL_F;
         psd->psd_ExecSignal = SIGBREAKF_CTRL_E;
         if (psd->psd_LoadSegThread = CreateNewProcTags(NP_Entry, LoadSegThread,
                                             NP_Name, "LoadSeg Thread", TAG_DONE))
         {
            FH->fh_Arg1 = (LONG)psd;   /* ok: Open() should succeed */
            Pkt->dp_Res1 = DOSTRUE;
            Pkt->dp_Res2 = 0L;
            PutMsg(&psd->psd_LoadSegThread->pr_MsgPort, &psd->psd_ExecMessage);
            gd->gd_OpenCnt++;
         }
         else
         {
            d(("no loadseg-thread\n"));
            Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
         }
      }
      else
      {
         d(("no StreamSpecs()\n"));
         Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
      }

      if (Pkt->dp_Res1 == DOSFALSE)
         FreeVec (psd);
   }
   else
   {
      d(("no psd\n"));
      Pkt->dp_Res2 = ERROR_NO_FREE_STORE;
   }
}
/*E*/
/*F*/ STATIC VOID ACTION_End(GD gd, struct DosPacket *Pkt)
{
   PSD psd;

   psd = (PSD )Pkt->dp_Arg1;

   ObtainSemaphore(&psd->psd_Protect);

      /* this tells loadseg thread there won't come any more data */
   psd->psd_Ready = 1;

   d(("ActionEnd (psd 0x%lx)\n",psd));

      /* signal loadseg thread we are ready with this file */
   d(("signalling loadseg thread we are ready\n"));
   Signal(&psd->psd_LoadSegThread->pr_Task, psd->psd_BufferSignal);

   ReleaseSemaphore(&psd->psd_Protect);
}
/*E*/
/*F*/ STATIC VOID ACTION_Write(GD gd, struct DosPacket *Pkt)
{
   PSD psd;

   psd = (PSD )Pkt->dp_Arg1;
   d(("ActionWrite (psd 0x%lx)\n",psd));

      /* arbitrate buffer management values */
   ObtainSemaphore(&psd->psd_Protect);

   if (!psd->psd_Error)
   {
      psd->psd_Buffer = (UBYTE*)Pkt->dp_Arg2;
      psd->psd_Len = (LONG)Pkt->dp_Arg3;
      psd->psd_Pos = 0;
      /*SetSignal(0, psd->psd_BufferSignal);*/
      d(("buffer is 0x%lx, %ld\n", psd->psd_Buffer, psd->psd_Len));

         /* signal loadseg thread we have new data */
      d(("signalling loadseg thread we have data\n"));
      Signal(&psd->psd_LoadSegThread->pr_Task, psd->psd_BufferSignal);

         /* release buffer management values to loadseg thread */
      d(("waiting for loadseg thread to consume data\n"));
      ReleaseSemaphore(&psd->psd_Protect);

         /*
         ** now I wait for loadseg thread to consume the buffer
         ** or to error exit
         */
      Wait(psd->psd_BufferSignal);

         /* arbitrate buffer management values */
      ObtainSemaphore(&psd->psd_Protect);
      d(("ok.\n"));

      /* if (psd->psd_Error) isn't neccessary at this point */

      Pkt->dp_Res1 = psd->psd_Pos;

      psd->psd_Buffer = NULL;
      psd->psd_Len = 0;
      psd->psd_Pos = 0;
   }
   else
   {
      d(("error flag set!\n"));
      Pkt->dp_Res1 = 0;
   }
      /* release buffer management values */
   ReleaseSemaphore(&psd->psd_Protect);
}
/*E*/

/* -------------------------- Program Execution --------------------------- */

/*F*/ STATIC ASM SAVEDS LONG exitcode(REG(d0) LONG childRC, REG(d1) PSD psd)
{
   GD gd = psd->psd_GlobalData;

   psd->psd_ChildRC = childRC;

   Signal(&psd->psd_LoadSegThread->pr_Task, psd->psd_ExecSignal);

   return childRC;
}
/*E*/
/*F*/ STATIC VOID executeJob(GD gd, PSD psd)
{
   BPTR oldcwd, oldhome;
   BPTR input, output;
   struct MsgPort *oldtask, *task;

   d(("executing parameters:\n"));
   d(("  ˇ psd:         0x%lx\n",psd));
   d(("  ˇ seglist:     (BPTR)0x%lx is 0x%lx\n",psd->psd_SegList,BADDR(psd->psd_SegList)));
   d(("  ˇ priority:    %ld\n",psd->psd_Priority));
   d(("  ˇ stacksize:   0x%lx\n",psd->psd_StackSize));
   d(("  ˇ i/o window:  %s\n",psd->psd_Window));
   d(("  ˇ procname:    %s\n",psd->psd_ProcName));
   d(("  ˇ arguments:   %s",psd->psd_Arguments));
#if DEBUG != 0
   {  char name[100];
   NameFromLock(psd->psd_CWD, name, sizeof(name));
   d(("  ˇ cwd:         %s\n",name));
   NameFromLock(psd->psd_HomeDir, name, sizeof(name));
   d(("  ˇ homedir:     %s\n",name));
   }
#endif

   if (input = Open((STRPTR)psd->psd_Window, MODE_NEWFILE))
   {
         /* I make use of the implicit inheritance of the CWD, because I like
         ** it more that the system allocates and frees the lock rather that
         ** I allocate it and the system frees it.
         ** The same goes for the home dir.
         */
      oldtask = SetConsoleTask(task = ((struct FileHandle *)BADDR(input))->fh_Type);
      oldcwd = CurrentDir(psd->psd_CWD);
      oldhome = SetProgramDir(psd->psd_HomeDir);

      if (output = Open((STRPTR)"*", MODE_OLDFILE))
      {
         SetSignal(0,psd->psd_ExecSignal);
         if (CreateNewProcTags(
                  NP_StackSize, psd->psd_StackSize,
                  NP_Name, gd->gd_We->pr_Task.tc_Node.ln_Name,
                  NP_CommandName, psd->psd_ProcName,
                  NP_Seglist, psd->psd_SegList,
                  NP_Arguments, psd->psd_Arguments,
                  NP_FreeSeglist, FALSE,
                  NP_ExitCode, exitcode,
                  NP_ExitData, psd,
                  NP_Input, input,
                  NP_CloseInput, FALSE,
                  NP_Output, output,
                  NP_CloseOutput, FALSE,
                  NP_ConsoleTask, task,
                  NP_Cli, TRUE,
                  NP_Priority, psd->psd_Priority,
                  NP_CopyVars, FALSE,
                  NP_WindowPtr, NULL,
                  TAG_DONE))
         {
               /* Wait for child to return */
            Wait(psd->psd_ExecSignal);
         }
         else
         {
            d(( "couldn't launch child, IoErr()=%ld\n",IoErr()));
         }
         Close(output);
      }

      SetProgramDir(oldhome);
      CurrentDir(oldcwd);
      SetConsoleTask(oldtask);

      Close(input);
   }
}
/*E*/

/* ---------------------------- LoadSeg Thread ---------------------------- */

/*F*/ STATIC GD initloadsegthread(PSD *psd)
{
   LOCALSYSBASE;
   struct Process *we;

   we = (struct Process*)FindTask(NULL);
   WaitPort(&we->pr_MsgPort);
   *psd = (PSD )GetMsg(&we->pr_MsgPort);

   return (*psd)->psd_GlobalData;
}
/*E*/

/*F*/ STATIC ASM SAVEDS LONG readstub(REG(d1) PSD psd,
                                      REG(a0) UBYTE *buffer,
                                      REG(d0) LONG len,
                                      REG(a6) GD gd)
{
   LONG rlen, latchlen = len;

   d(("buffer = 0x%lx, len = %ld (psd 0x%lx, gd 0x%lx)\n", buffer, len, psd, gd));

      /* arbitrate buffer management values */
   ObtainSemaphore(&psd->psd_Protect);

      /* as long as we have somthing to read: */
   while((len > 0) && !psd->psd_Ready)
   {
      if (psd->psd_Pos == psd->psd_Len)
      {
            /* release buffer management values to daddy */
         d(("waiting for daddy to give me a buffer\n"));
         ReleaseSemaphore(&psd->psd_Protect);

         Wait(psd->psd_BufferSignal);

           /* arbitrate buffer management values */
         ObtainSemaphore(&psd->psd_Protect);

            /* no data in buffer: there was some error */
         if (psd->psd_Pos == psd->psd_Len)
         {
            d(("shit, daddy failed giving me a buffer\n"));
            break;
         }
         d(("ok, daddy gave me a buffer\n"));
      }

         /* determine the data size to transfer */
      if (psd->psd_Len - psd->psd_Pos >= len)
         rlen = len;
      else
         rlen = psd->psd_Len - psd->psd_Pos;
      d(("copying: 0x%lx -> 0x%lx, %ld bytes\n", &psd->psd_Buffer[psd->psd_Pos], buffer, rlen));
      CopyMem((VOID*)&psd->psd_Buffer[psd->psd_Pos], buffer, rlen);

         /* adjust buffer management pointers */
      psd->psd_Pos += rlen;
      len -= rlen;
      buffer += rlen;

         /* if all data is consumed, we tell daddy */
      if (psd->psd_Pos == psd->psd_Len)
      {
         d(("signalling daddy to carry on\n"));
         SetSignal(0, psd->psd_BufferSignal);
         Signal(&gd->gd_We->pr_Task, psd->psd_BufferSignal);
      }
   }

      /* release buffer management values */
   ReleaseSemaphore(&psd->psd_Protect);

   d(("exiting, read %ld bytes\n", latchlen - len));

   return latchlen-len;
}
/*E*/
/*F*/ STATIC ASM SAVEDS VOID *allocstub(REG(d0) BPTR size,
                                        REG(d1) ULONG flags,
                                        REG(a6) GD gd)
{
   VOID *p;

   d(("%ld/%lx\n",size, flags));

   if (!(p = AllocMem(size, flags | MEMF_CLEAR)))
      SetIoErr(ERROR_NO_FREE_STORE);

   d(("pointer = 0x%lx\n",p));

   return p;
}
/*E*/
/*F*/ STATIC ASM SAVEDS VOID freestub(REG(a1) VOID *buffer,
                                      REG(d0) ULONG size,
                                      REG(a6) GD gd)
{
   d(("freestub 0x%lx, %ld\n",buffer,size));

   FreeMem(buffer, size);
}
/*E*/

/*F*/ STATIC SAVEDS VOID LoadSegThread(VOID)
{
   PSD psd;
   GD gd;

   d(("loadsegthread, awaiting startup message\n"));
   gd = initloadsegthread(&psd);

   d(("doing MyLoadSeg()\n"));

   psd->psd_SegList = MyLoadSeg((LSCB)allocstub, (LSCB)freestub, (LSCB)readstub, psd, gd);

   d(("seglist is at (BPTR)0x%lx\n",psd->psd_SegList));

      /* for safety, we wake up daddy (even if he doesn't sleep) */
   Signal(&gd->gd_We->pr_Task, psd->psd_BufferSignal);

      /* Access PSD */
   ObtainSemaphore(&psd->psd_Protect);

      /* this should alway be true */
   if (psd->psd_Ready)
   {
      if (psd->psd_SegList == 0)
      {
         d(("no seglist!\n"));
         psd->psd_Error = 1;
      }
      else
      {
         d(("executing job at 0x%lx\n",psd));
         executeJob(gd, psd);
      }
   }
   else d(("unexcpected state\n"));

   /*if (psd->psd_SegList) UnLoadSeg(psd->psd_SegList);
   */

   d(("unloading seglist\n"));
   /*d(("issue CTRL-C to unload seglist\n"));
   Wait(SIGBREAKF_CTRL_C);*/
   if (psd->psd_SegList) MyUnLoadSeg(psd->psd_SegList, (LSCB)freestub, gd);
   psd->psd_SegList = NULL;

   d(("telling daddy we're done!\n"));
   PutMsg(gd->gd_ThreadsPort, &psd->psd_ExecMessage);

   d(("exiting\n"));
   ReleaseSemaphore(&psd->psd_Protect);
}
/*E*/

