home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 620.lha / APipe_v37.4 / pgmpipe.c < prev    next >
C/C++ Source or Header  |  1992-02-21  |  25KB  |  846 lines

  1. /*
  2. **  Pgmpipe.c --- main module of APipe-Handler
  3. **
  4. **  Copyright (C) 1991 by Per Bojsen.  All Rights Reserved.
  5. **
  6. **  Permission is granted to any individual or institution to use, copy,
  7. **  modify, and distribute this software, provided that this complete
  8. **  copyright and permission notice is maintained, intact, in all copies
  9. **  and supporting documentation.
  10. **
  11. **  This software is provided on an "as is" basis without express or
  12. **  implied warranty.
  13. **
  14. **  NOTE: The code is reentrant.  Be careful when modifying it.
  15. **
  16. **  $Id: pgmpipe.c,v 1.6 92/02/20 23:46:11 bojsen Exp Locker: bojsen $
  17. **
  18. **  $Log:    pgmpipe.c,v $
  19. **  Revision 1.6  92/02/20  23:46:11  bojsen
  20. **  Fixed bug in handling multiple outstanding read packets add EOF.
  21. **  
  22. **  Revision 1.5  91/11/24  17:01:25  bojsen
  23. **  Made extraction of command line from file name more robust; the code now
  24. **  handles the case where the handler name is not prepended.
  25. **
  26. **  Revision 1.4  91/11/24  02:22:39  bojsen
  27. **  First released version.  Changed references to ChildProcess() to
  28. **  _ChildProcess().
  29. **
  30. **  Revision 1.3  91/10/01  02:35:30  bojsen
  31. **  Added code to clone current dir of requester process.  Changed code
  32. **  to obtain pointer to requester process.
  33. **
  34. **  Revision 1.2  91/09/24  23:59:44  bojsen
  35. **  Fixed typo in #include line.
  36. **
  37. **  Revision 1.1  91/09/24  23:54:06  bojsen
  38. **  Initial revision
  39. **
  40. */
  41.  
  42. #include <exec/types.h>
  43. #include <exec/memory.h>
  44. #include <utility/tagitem.h>
  45. #include <clib/macros.h>
  46. #include <dos/dos.h>
  47. #include <dos/dosextens.h>
  48. #include <dos/dostags.h>
  49. #include <dos/var.h>
  50. #ifdef __SASC
  51. #include <proto/exec.h>
  52. #include <proto/dos.h>
  53. #endif /* __SASC */
  54. #include <string.h>
  55. #include "APipe-Handler_rev.h"
  56.  
  57. /*
  58.  *  Version tagging.
  59.  */
  60. STATIC char VersionTag[] = VERSTAG;
  61.  
  62. /*
  63.  *  Debugging.
  64.  */
  65. #ifdef DEBUG
  66. #define DPrintF(args) do { KPrintF args; } while (0)
  67. #else /* !DEBUG */
  68. #define DPrintF(args)
  69. #endif /* !DEBUG */
  70.  
  71. #define AbsExecBase 4L
  72. #define LIB_VERSION_SUPPORTED 37L
  73. #define PIPEBUFFER_SIZE 4096
  74.  
  75. /* defines for read/write status */
  76. #define PPIPE_NOTOPEN   0
  77. #define PPIPE_OPEN      4
  78. #define PPIPE_READ      1
  79. #define PPIPE_READOPEN  (PGMPIPE_READ | PGMPIPE_OPEN)
  80. #define PPIPE_WRITE     2
  81. #define PPIPE_WRITEOPEN (PGMPIPE_WRITE | PGMPIPE_OPEN)
  82. #define PPIPE_RWMASK    3
  83.  
  84. /* defines for fh_Arg1 field of filehandles */
  85. #define PPIPE_READER  1
  86. #define PPIPE_WRITER  2
  87.  
  88. /* defines for closing variable */
  89. #define PPIPE_C_OPEN          0
  90. #define PPIPE_C_RDRCLOSING    1
  91. #define PPIPE_C_WRTCLOSING    2
  92. #define PPIPE_C_CLOSINGMASK   3
  93. #define PPIPE_C_RDRCLOSED     4
  94. #define PPIPE_C_WRTCLOSED     8
  95. #define PPIPE_C_CLOSEDMASK  0xC
  96.  
  97. /*
  98.  *  Parameter packet to child process.
  99.  */
  100. struct ChildMsg
  101. {
  102.   struct Message  ExecMsg;
  103.   char           *CmdLine;   /* the command line to execute */
  104.   LONG            StackSize; /* use this stacksize for child's child */
  105.   BPTR            PathList;  /* path list for child */
  106.   ULONG           Flags;     /* flags for child */
  107.   LONG            RC;        /* return code from child process */
  108. };
  109.  
  110. /* Defines for ChildMsg.Flags */
  111. #define CHMF_PIPE 1L /* stdin of child is a pipe (of PIPE: type) */
  112. #define CHMF_PATH 2L /* path is passed in in PathList */
  113.  
  114. /*
  115.  *  Command path element.
  116.  */
  117. struct PathEntry
  118. {
  119.   BPTR pe_NextPathEntry;
  120.   BPTR pe_PathLock;
  121. };
  122.  
  123. /*
  124.  *  The library bases must be global by AmigaOS programming standards,
  125.  *  and the pragma libcall requires them.
  126.  */
  127. struct ExecBase *SysBase;
  128. struct DosLibrary *DOSBase;
  129.  
  130.  
  131. /*
  132.  *  Prototypes for external functions.
  133.  */
  134. extern int _ChildProcess(void);
  135.  
  136. /*
  137.  *  Prototypes for local functions.
  138.  */
  139. ULONG CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  140.                    ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes);
  141. ULONG CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  142.                  ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes);
  143. STRPTR GetCommandLine(STRPTR FileSpec, BSTR HandlerName);
  144. LONG ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  145.                  struct Process *Requester, struct FileHandle *ChildIO,
  146.                  LONG IoDirection);
  147. BPTR CloneProcessIO(struct Process *Friend, LONG IoDirection);
  148. BPTR ClonePathList(struct CommandLineInterface *Peer);
  149. void FreePathList(BPTR);
  150.  
  151. void
  152. PgmPipe()
  153. {
  154.   struct DosLibrary *l_DOSBase;
  155.  
  156.   SysBase = *((struct ExecBase **) AbsExecBase); /* `open' exec.library */
  157.  
  158.   if (l_DOSBase =
  159.         (struct DosLibrary *) OpenLibrary("dos.library", LIB_VERSION_SUPPORTED))
  160.   {
  161.     struct Process *ourProc, *theirProc;
  162.     struct DosList *ourDevNode;
  163.     struct DosPacket *packet, *writePkt = NULL, *readPkt = NULL;
  164.     struct DosPacket *writeCls = NULL, *readCls = NULL;
  165.     struct MsgPort *childPort = NULL;
  166.     struct MsgPort *oldConTask = NULL;
  167.     struct ChildMsg *childMsg = NULL;
  168.     struct FileHandle *fhIncoming, *fhOutgoing;
  169.     UBYTE *pipeBuffer;
  170.     UBYTE *pbStart, *pbEnd;
  171.     ULONG bytesReady, bytesR = 0, bytesW = 0;
  172.     UWORD closing = PPIPE_C_RDRCLOSED | PPIPE_C_WRTCLOSED;
  173.     UWORD readwrite = PPIPE_NOTOPEN;
  174.     ULONG waitSigMask;
  175.     struct MinList readQ, writeQ;
  176. #ifdef DEBUG
  177.     ULONG packetNum = 0;
  178. #endif /* DEBUG */
  179.  
  180.     NewList((struct List *) &readQ);
  181.     NewList((struct List *) &writeQ);
  182.  
  183.     DOSBase = l_DOSBase; /* patch the global library base */
  184.  
  185.     ourProc = (struct Process *) FindTask(NULL);
  186.     waitSigMask = 1L << ourProc->pr_MsgPort.mp_SigBit;
  187.     packet = WaitPkt(); /* get parameter packet */
  188.  
  189.     if ((pipeBuffer = AllocMem(PIPEBUFFER_SIZE, 0)) == NULL)
  190.       ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  191.     else
  192.     {
  193.       pbStart = pbEnd = pipeBuffer;
  194.       bytesReady = 0;
  195.  
  196.       /*
  197.        *  Currently we don't use the parameters provided in the parameter
  198.        *  packet.  Since we want a new process for each instance of reference
  199.        *  to our handler we don't patch the DeviceList node, but use the
  200.        *  node to get the handler name.
  201.        */
  202.  
  203.       ourDevNode = (struct DosList *) BADDR(packet->dp_Arg3);
  204.  
  205.       ReplyPkt(packet, DOSTRUE, packet->dp_Arg2);
  206.  
  207.       do
  208.       {
  209.         struct Node *node;
  210.         char *cmdLine;
  211.         LONG ioerr;
  212.  
  213.         if (readPkt && bytesReady == 0 && writePkt == NULL &&
  214.             closing & PPIPE_C_WRTCLOSED)
  215.         {
  216.           ReplyPkt(readPkt, bytesR, 0);
  217.           while (readPkt = (struct DosPacket *)
  218.                              (node = RemHead((struct List *) &readQ),
  219.                               node ? node->ln_Name : NULL))
  220.             ReplyPkt(readPkt, 0, 0);
  221.         }
  222.  
  223. #ifdef DEBUG
  224.         if (readPkt || writePkt)
  225.           DPrintF(("Event loop: readPkt == 0x%08lx writePkt == 0x%08lx\n",
  226.                    readPkt, writePkt));
  227. #endif /* DEBUG */
  228.  
  229.         if (packet = WaitPkt())
  230.         {
  231.           DPrintF(("Packet %ld, port 0x%08lx: ", ++packetNum, packet->dp_Port));
  232.  
  233.           switch (packet->dp_Type)
  234.           {
  235.             struct TagItem adoTags[2];
  236.  
  237.             case ACTION_FINDINPUT:
  238.               DPrintF(("ACTION_FINDINPUT 0x%08lx 0x%08lx %s\n",
  239.                        packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  240.                        (char *) (packet->dp_Arg3 << 2) + 1));
  241.  
  242.               if (readwrite != PPIPE_NOTOPEN)
  243.               {
  244.                 ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  245.                 break;
  246.               }
  247.               else
  248.                 readwrite = PPIPE_READ;
  249.  
  250.             case ACTION_FINDOUTPUT:
  251. #ifdef DEBUG
  252.               if (packet->dp_Type == ACTION_FINDOUTPUT)
  253.                 DPrintF(("ACTION_FINDOUTPUT 0x%08lx 0x%08lx %s\n",
  254.                          packet->dp_Arg1 << 2, packet->dp_Arg2 << 2,
  255.                          (char *) (packet->dp_Arg3 << 2) + 1));
  256. #endif /* DEBUG */
  257.  
  258.               if (readwrite & PPIPE_OPEN)
  259.               {
  260.                 ReplyPkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE);
  261.                 break;
  262.               }
  263.               else if (readwrite != PPIPE_READ)
  264.                 readwrite = PPIPE_WRITE;
  265.  
  266.               fhIncoming = (struct FileHandle *) BADDR(packet->dp_Arg1);
  267.  
  268.               /*
  269.                *  Obtain command line to execute.
  270.                */
  271.               cmdLine = GetCommandLine((STRPTR) BADDR(packet->dp_Arg3) + 1,
  272.                                        ourDevNode->dol_Name);
  273.  
  274.               /*
  275.                *  Find out who sent the packet.
  276.                */
  277.               if ((packet->dp_Port->mp_Flags & PF_ACTION) == PA_SIGNAL)
  278.                 theirProc = (struct Process *) packet->dp_Port->mp_SigTask;
  279.               else
  280.               {
  281.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_STREAM_NAME);
  282.                 readwrite = PPIPE_NOTOPEN;
  283.                 break;
  284.               }
  285.  
  286.               adoTags[0].ti_Tag = ADO_FH_Mode;
  287.               adoTags[0].ti_Data =
  288.                 readwrite == PPIPE_READ ? MODE_NEWFILE : MODE_OLDFILE;
  289.               adoTags[1].ti_Tag = TAG_DONE;
  290.  
  291.               if ((fhOutgoing = AllocDosObject(DOS_FILEHANDLE, adoTags))
  292.                     == NULL)
  293.               {
  294.                 ReplyPkt(packet, DOSFALSE, ERROR_NO_FREE_STORE);
  295.                 readwrite = PPIPE_NOTOPEN;
  296.                 break;
  297.               }
  298.  
  299.               fhOutgoing->fh_Link = NULL;
  300.               fhOutgoing->fh_Port = fhIncoming->fh_Port = (struct MsgPort *) 0;
  301.               fhOutgoing->fh_Type = &ourProc->pr_MsgPort;
  302.  
  303.               if (readwrite == PPIPE_READ)
  304.               {
  305.                 fhIncoming->fh_Arg1 = PPIPE_READER;
  306.                 fhOutgoing->fh_Arg1 = PPIPE_WRITER;
  307.               }
  308.               else
  309.               {
  310.                 fhIncoming->fh_Arg1 = PPIPE_WRITER;
  311.                 fhOutgoing->fh_Arg1 = PPIPE_READER;
  312.               }
  313.  
  314.               oldConTask = SetConsoleTask(theirProc->pr_ConsoleTask);
  315.  
  316.               DPrintF(("Getting ready to spawn child . . .  "));
  317.  
  318.               if (ioerr = ExecCommand(cmdLine, &childPort, theirProc,
  319.                                       fhOutgoing, readwrite))
  320.               {
  321.                 SetConsoleTask(oldConTask);
  322.                 ReplyPkt(packet, DOSFALSE, ioerr);
  323.                 FreeDosObject(DOS_FILEHANDLE, fhOutgoing);
  324.                 readwrite = PPIPE_NOTOPEN;
  325.                 break;
  326.               }
  327.               SetConsoleTask(oldConTask);
  328.  
  329.               DPrintF(("Child spawned.\n"));
  330.  
  331.               waitSigMask |= 1L << childPort->mp_SigBit;
  332.  
  333.               closing = PPIPE_C_OPEN;
  334.               readwrite |= PPIPE_OPEN;
  335.  
  336.               ReplyPkt(packet, DOSTRUE, packet->dp_Res2);
  337.  
  338.               break;
  339.  
  340.             case ACTION_READ:
  341.               DPrintF(("ACTION_READ %ld 0x%08lx %ld\n", packet->dp_Arg1,
  342.                        packet->dp_Arg2, packet->dp_Arg3));
  343.  
  344.               if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  345.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  346.               else if (closing & PPIPE_C_RDRCLOSED)
  347.                 ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  348.               else if (packet->dp_Arg1 == PPIPE_WRITER)
  349.                 ReplyPkt(packet, 0, 0);
  350.               else if (readPkt == NULL)
  351.               {
  352.                 readPkt = packet;
  353.                 bytesR = 0;
  354.               }
  355.               else
  356.                 AddTail((struct List *) &readQ,
  357.                         (struct Node *) packet->dp_Link);
  358.               break;
  359.  
  360.             case ACTION_WRITE:
  361.               DPrintF(("ACTION_WRITE %ld 0x%08lx %ld\n", packet->dp_Arg1,
  362.                        packet->dp_Arg2, packet->dp_Arg3));
  363.  
  364.               if (packet->dp_Arg2 == NULL || packet->dp_Arg3 < 0)
  365.                 ReplyPkt(packet, DOSFALSE, ERROR_BAD_NUMBER);
  366.               else if (closing & PPIPE_C_WRTCLOSED)
  367.                 ReplyPkt(packet, DOSFALSE, ERROR_INVALID_LOCK);
  368.               else if (packet->dp_Arg1 == PPIPE_READER)
  369.                 ReplyPkt(packet, 0, 0);
  370.               else if (writePkt == NULL)
  371.               {
  372.                 writePkt = packet;
  373.                 bytesW = 0;
  374.               }
  375.               else
  376.                 AddTail((struct List *) &writeQ,
  377.                         (struct Node *) packet->dp_Link);
  378.               break;
  379.  
  380.             case ACTION_END:
  381.               DPrintF(("ACTION_END %ld\n", packet->dp_Arg1));
  382.  
  383.               if (packet->dp_Arg1 == PPIPE_READER &&
  384.                   (closing & PPIPE_C_RDRCLOSED) == 0)
  385.               {
  386.                 closing |= PPIPE_C_RDRCLOSING;
  387.                 readCls = packet;
  388.               }
  389.               else if (packet->dp_Arg1 == PPIPE_WRITER &&
  390.                        (closing & PPIPE_C_WRTCLOSED) == 0)
  391.               {
  392.                 closing |= PPIPE_C_WRTCLOSING;
  393.                 writeCls = packet;
  394.               }
  395.               else
  396.                 ReplyPkt(packet, DOSTRUE, 0);
  397.               break;
  398.  
  399.             case ACTION_IS_FILESYSTEM:
  400.               DPrintF(("ACTION_IS_FILESYSTEM\n"));
  401.  
  402.               ReplyPkt(packet, DOSFALSE, 0);
  403.               break;
  404.  
  405.             default:
  406.               DPrintF(("%ld\n", packet->dp_Type));
  407.  
  408.               ReplyPkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
  409.           }
  410.  
  411.           DPrintF(("\n"));
  412.         }
  413.  
  414.         do
  415.         {
  416.           if (readPkt)
  417.           {
  418.             if (bytesReady > 0)
  419.               bytesR += CopyFromFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  420.                                      pbEnd, &bytesReady,
  421.                                      (UBYTE *) readPkt->dp_Arg2 + bytesR,
  422.                                      readPkt->dp_Arg3 - bytesR);
  423.             if (bytesR < readPkt->dp_Arg3 && writePkt)
  424.             {
  425.               ULONG bytes = MIN(writePkt->dp_Arg3 - bytesW,
  426.                                 readPkt->dp_Arg3 - bytesR);
  427.  
  428.               CopyMem((UBYTE *) writePkt->dp_Arg2 + bytesW,
  429.                       (UBYTE *) readPkt->dp_Arg2 + bytesR, bytes);
  430.               bytesR += bytes;
  431.               if ((bytesW += bytes) == writePkt->dp_Arg3)
  432.               {
  433.                 ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  434.                 writePkt = NULL;
  435.               }
  436.             }
  437.             if (bytesR == readPkt->dp_Arg3)
  438.             {
  439.               ReplyPkt(readPkt, readPkt->dp_Arg3, 0);
  440.               readPkt = NULL;
  441.             }
  442.           }
  443.           if (readPkt == NULL &&
  444.               (readPkt = (struct DosPacket *)
  445.                            (node = RemHead((struct List *) &readQ),
  446.                             node ? node->ln_Name : NULL)))
  447.           {
  448.             bytesR = 0;
  449.           }
  450.           if (writePkt == NULL &&
  451.               (writePkt = (struct DosPacket *)
  452.                             (node = RemHead((struct List *) &writeQ),
  453.                              node ? node->ln_Name : NULL)))
  454.           {
  455.             bytesW = 0;
  456.           }
  457.         }
  458.         while (readPkt && (writePkt || bytesReady > 0));
  459.  
  460.         if (readPkt == NULL && closing & PPIPE_C_RDRCLOSING)
  461.         {
  462.           closing &= ~PPIPE_C_RDRCLOSING;
  463.           closing |= PPIPE_C_RDRCLOSED;
  464.         }
  465.  
  466.         while (writePkt &&
  467.                (closing & PPIPE_C_RDRCLOSED ||
  468.                 PIPEBUFFER_SIZE - bytesReady >= writePkt->dp_Arg3 - bytesW))
  469.         {
  470.           if (closing & PPIPE_C_RDRCLOSED)
  471.           {
  472.             ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  473.             writePkt = NULL;
  474.           }
  475.           else
  476.           {
  477.             ULONG bytes = CopyToFIFO(pipeBuffer, PIPEBUFFER_SIZE, &pbStart,
  478.                                      &pbEnd, &bytesReady,
  479.                                      (UBYTE *) writePkt->dp_Arg2 + bytesW,
  480.                                      writePkt->dp_Arg3 - bytesW);
  481.  
  482.             if ((bytesW += bytes) == writePkt->dp_Arg3)
  483.             {
  484.               ReplyPkt(writePkt, writePkt->dp_Arg3, 0);
  485.               writePkt = NULL;
  486.             }
  487.           }
  488.           if (writePkt == NULL &&
  489.               (writePkt = (struct DosPacket *)
  490.                             (node = RemHead((struct List *) &writeQ),
  491.                              node ? node->ln_Name : NULL)))
  492.           {
  493.             bytesW = 0;
  494.           }
  495.         }
  496.  
  497.         if (writePkt == NULL && closing & PPIPE_C_WRTCLOSING)
  498.         {
  499.           closing &= ~PPIPE_C_WRTCLOSING;
  500.           closing |= PPIPE_C_WRTCLOSED;
  501.         }
  502.       }
  503.       while ((closing & PPIPE_C_RDRCLOSED) == 0 ||
  504.              (closing & PPIPE_C_WRTCLOSED) == 0);
  505.  
  506.       /* clean up from child process */
  507.       if (childPort)
  508.       {
  509.         waitSigMask = 1L << childPort->mp_SigBit;
  510.  
  511.         while ((childMsg = (struct ChildMsg *) GetMsg(childPort)) == NULL)
  512.           Wait(waitSigMask);
  513.  
  514.         DeleteMsgPort(childPort);
  515.       }
  516.       if (childMsg) /* means the child process existed */
  517.       {
  518.         if (readCls)
  519.           ReplyPkt(readCls, DOSTRUE, 0);
  520.         if (writeCls)
  521.           ReplyPkt(writeCls, DOSTRUE, 0);
  522.         FreePathList(childMsg->PathList);
  523.         FreeVec(childMsg); /* no use for return code currently */
  524.       }
  525.       FreeMem(pipeBuffer, PIPEBUFFER_SIZE);
  526.     }
  527.  
  528.     CloseLibrary((struct Library *) l_DOSBase);
  529.  
  530.     DPrintF(("Handler exiting.\n"));
  531.   }
  532. } /* PgmPipe */
  533.  
  534.  
  535. STATIC ULONG
  536. CopyFromFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE *End,
  537.              ULONG *FIFOFill, UBYTE *Dest, ULONG NumBytes)
  538. {
  539.   ULONG bytesCopied = 0, bytes;
  540.  
  541.   DPrintF(("CopyFromFIFO: 0x%08lx %ld", Dest, NumBytes));
  542.  
  543.   if (*FIFOFill > 0 && NumBytes > 0)
  544.   {
  545.     bytes = *Start >= End ? FIFO + FIFOSize - *Start : *FIFOFill;
  546.  
  547.     CopyMem(*Start, Dest, bytesCopied = MIN(bytes, NumBytes));
  548.     NumBytes -= bytesCopied;
  549.     *Start += bytesCopied;
  550.     *FIFOFill -= bytesCopied;
  551.   }
  552.   if (*FIFOFill > 0 && NumBytes > 0)
  553.   {
  554.     bytes = End - FIFO; /* invariant: *Start == FIFO + FIFOSize */
  555.  
  556.     CopyMem(FIFO, Dest + bytesCopied, bytes = MIN(bytes, NumBytes));
  557.     *Start = FIFO + bytes;
  558.     *FIFOFill -= bytes;
  559.     bytesCopied += bytes;
  560.   }
  561.  
  562.   DPrintF((" %ld\n\n", bytesCopied));
  563.  
  564.   return bytesCopied;
  565. } /* CopyFromFIFO */
  566.  
  567.  
  568. STATIC ULONG
  569. CopyToFIFO(UBYTE *FIFO, ULONG FIFOSize, UBYTE **Start, UBYTE **End,
  570.            ULONG *FIFOFill, UBYTE *Src, ULONG NumBytes)
  571. {
  572.   ULONG bytesCopied = 0, bytes;
  573.  
  574.   DPrintF(("CopyToFIFO: 0x%08lx %ld", Src, NumBytes));
  575.  
  576.   if (*FIFOFill == 0)
  577.     *Start = *End = FIFO; /* normalize for efficiency */
  578.  
  579.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  580.   {
  581.     bytes = *End <= *Start ? FIFOSize - *FIFOFill : FIFO + FIFOSize - *End;
  582.  
  583.     CopyMem(Src, *End, bytesCopied = MIN(bytes, NumBytes));
  584.     NumBytes -= bytesCopied;
  585.     *End += bytesCopied;
  586.     *FIFOFill += bytesCopied;
  587.   }
  588.   if (*FIFOFill < FIFOSize && NumBytes > 0)
  589.   {
  590.     bytes = *Start - FIFO; /* invariant: *End == FIFO + FIFOSize */
  591.  
  592.     CopyMem(Src + bytesCopied, FIFO, bytes = MIN(bytes, NumBytes));
  593.     *End = FIFO + bytes;
  594.     *FIFOFill += bytes;
  595.     bytesCopied += bytes;
  596.   }
  597.  
  598.   DPrintF((" %ld\n\n", bytesCopied));
  599.  
  600.   return bytesCopied;
  601. } /* CopyFromFIFO */
  602.  
  603.  
  604. #define GetCICh(String, Char) \
  605.   ((Char) = *(String), (Char) += (Char) >= 'A' && (Char) <= 'Z' ? 0x20 : 0)
  606.  
  607. STATIC STRPTR
  608. GetCommandLine(STRPTR FileSpec, BSTR HandlerName)
  609. {
  610.   UBYTE *cp1 = FileSpec, *cp2 = BADDR(HandlerName);
  611.   LONG hNmLen = *cp2++;
  612.   LONG ch1, ch2;
  613.  
  614.   while (hNmLen-- > 0 && GetCICh(cp1, ch1) == GetCICh(cp2, ch2))
  615.     cp1++, cp2++;
  616.  
  617.   return hNmLen < 0 && *cp1 == ':' ? cp1 + 1 : FileSpec;
  618. } /* GetCommandLine */
  619.  
  620.  
  621. #define FAILATCMD        "FailAt 2147483647\n"
  622. #define STRLEN_FAILATCMD 18
  623.  
  624. /*
  625.  *  Run a command asynchronously.
  626.  */
  627.  
  628. STATIC LONG
  629. ExecCommand(char *CmdLine, struct MsgPort **ParentPort,
  630.             struct Process *Requester, struct FileHandle *ChildIO,
  631.             LONG IoDirection)
  632. {
  633.   char *commandLine;
  634.   struct ChildMsg *childMsg;
  635.   struct TagItem processTags[9];
  636.   struct Process *child, *thisProcess = (struct Process *) FindTask(NULL);
  637.   struct CommandLineInterface *cli = BADDR(Requester->pr_CLI);
  638.   BPTR childOI;
  639.   struct LocalVar *pVar;
  640.   BPTR pathList, homeDir;
  641.   LONG ioErr;
  642.  
  643.   /*
  644.    *  Copy shell variables, aliases, and path from requester process.
  645.    *  NOTE: This is not completely safe!
  646.    */
  647.   Forbid();
  648.   for (pVar = (struct LocalVar *) Requester->pr_LocalVars.mlh_TailPred;
  649.        pVar->lv_Node.ln_Pred;
  650.        pVar = (struct LocalVar *) pVar->lv_Node.ln_Pred)
  651.   {
  652.     if (FindVar(pVar->lv_Node.ln_Name, pVar->lv_Node.ln_Type) == NULL)
  653.       SetVar(pVar->lv_Node.ln_Name, pVar->lv_Value, pVar->lv_Len,
  654.              pVar->lv_Flags | pVar->lv_Node.ln_Type & ~LVF_IGNORE);
  655.   }
  656.  
  657.   pathList = ClonePathList(cli);
  658.  
  659.   Permit();
  660.  
  661.   if ((childOI = CloneProcessIO(Requester, IoDirection)) == NULL)
  662.   {
  663.     ioErr = IoErr();
  664.     FreePathList(pathList);
  665.  
  666.     return ioErr;
  667.   }
  668.  
  669.   if ((homeDir = DupLock(Requester->pr_CurrentDir)) == NULL)
  670.   {
  671.     ioErr = IoErr();
  672.     Close(childOI);
  673.     FreePathList(pathList);
  674.  
  675.     return ioErr;
  676.   }
  677.  
  678.   /*
  679.    *  Create parent's port.
  680.    */
  681.   if ((*ParentPort = CreateMsgPort()) == NULL)
  682.   {
  683.     UnLock(homeDir);
  684.     Close(childOI);
  685.     FreePathList(pathList);
  686.  
  687.     return ERROR_NO_FREE_STORE;
  688.   }
  689.  
  690.   /*
  691.    *  Build command line from argument vector.  First count the length.
  692.    *
  693.    *  We insert a `FailAt 2147483647' (0x7fffffff) so the shell doesn't
  694.    *  print `Command foo failed' when we call System() to execute the
  695.    *  command.
  696.    */
  697.   if ((childMsg = AllocVec(sizeof(struct ChildMsg) + strlen(CmdLine) +
  698.                            STRLEN_FAILATCMD + 1, MEMF_PUBLIC)) == NULL)
  699.   {
  700.     DeleteMsgPort(*ParentPort);
  701.     *ParentPort = NULL;
  702.     UnLock(homeDir);
  703.     Close(childOI);
  704.     FreePathList(pathList);
  705.  
  706.     return ERROR_NO_FREE_STORE;
  707.   }
  708.  
  709.   childMsg->ExecMsg.mn_Node.ln_Type = NT_MESSAGE;
  710.   childMsg->ExecMsg.mn_Node.ln_Pri = 0;
  711.   childMsg->ExecMsg.mn_ReplyPort = *ParentPort;
  712.   childMsg->ExecMsg.mn_Length = sizeof(struct ChildMsg);
  713.   childMsg->CmdLine = commandLine = (char *) (childMsg + 1);
  714.  
  715.   /* Setup the grandchild's [sic] stack */
  716.   childMsg->StackSize = cli ? cli->cli_DefaultStack << 2 :
  717.                               Requester->pr_StackSize;
  718.   childMsg->PathList = pathList;
  719.   childMsg->Flags = pathList ? CHMF_PATH : 0;
  720.   childMsg->RC = 0;
  721.  
  722.   strcpy(commandLine, FAILATCMD);
  723.   strcpy(commandLine + STRLEN_FAILATCMD, CmdLine);
  724.  
  725.   processTags[0].ti_Tag = NP_Entry;
  726.   processTags[0].ti_Data = (Tag) _ChildProcess;
  727.   processTags[1].ti_Tag = NP_Input;
  728.   processTags[2].ti_Tag = NP_Output;
  729.   processTags[3].ti_Tag = NP_StackSize;
  730.   processTags[3].ti_Data = thisProcess->pr_StackSize;
  731.   processTags[4].ti_Tag = NP_Cli;
  732.   processTags[4].ti_Data = TRUE;
  733.   processTags[5].ti_Tag = NP_Name;
  734.   processTags[5].ti_Data = (Tag) "Kicker Process";
  735.   processTags[6].ti_Tag = NP_Priority;
  736.   processTags[6].ti_Data = Requester->pr_Task.tc_Node.ln_Pri;
  737.   processTags[7].ti_Tag = NP_CurrentDir;
  738.   processTags[7].ti_Data = homeDir;
  739.   processTags[8].ti_Tag = TAG_DONE;
  740.  
  741.   if (IoDirection == PPIPE_READ)
  742.   {
  743.     processTags[1].ti_Data = childOI;
  744.     processTags[2].ti_Data = MKBADDR(ChildIO);
  745.   }
  746.   else /* IoDirection == PPIPE_WRITE */
  747.   {
  748.     processTags[1].ti_Data = MKBADDR(ChildIO);
  749.     processTags[2].ti_Data = childOI;
  750.   }
  751.  
  752.   /*
  753.    *  Start our `kicker' process.  This process consists of the _ChildProcess()
  754.    *  function above.  The _ChildProcess() function then executes the command.
  755.    */
  756.   if ((child = CreateNewProc(processTags)) == NULL)
  757.   {
  758.     FreeVec(childMsg);
  759.     DeleteMsgPort(*ParentPort);
  760.     *ParentPort = NULL;
  761.     UnLock(homeDir);
  762.     Close(childOI);
  763.     FreePathList(pathList);
  764.  
  765.     return ERROR_NO_FREE_STORE;
  766.   }
  767.  
  768.   /* now pass the child the startup message */
  769.   PutMsg(&child->pr_MsgPort, (struct Message *) childMsg);
  770.  
  771.   return 0;
  772. } /* ExecCommand */
  773.  
  774.  
  775. STATIC BPTR
  776. CloneProcessIO(struct Process *Friend, LONG IoDirection)
  777. {
  778.   BPTR origFH;
  779.   BPTR lock;
  780.   BPTR fh;
  781.  
  782.   if (IoDirection == PPIPE_READ)
  783.     origFH = Friend->pr_CIS;
  784.   else
  785.     origFH = Friend->pr_COS;
  786.  
  787.   if ((lock = DupLockFromFH(origFH)) == NULL ||
  788.       (fh = OpenFromLock(lock)) == NULL)
  789.   {
  790.     if (lock)
  791.       UnLock(lock);
  792.     fh = Open("CONSOLE:", MODE_OLDFILE);
  793.   }
  794.  
  795.   return fh;
  796. } /* CloneProcessIO */
  797.  
  798.  
  799. /*
  800.  *  NOTE: This routine should be called from within a Forbid();
  801.  *        but it is still unsafe due to the call to DupLock().
  802.  */
  803. STATIC BPTR
  804. ClonePathList(struct CommandLineInterface *Peer)
  805. {
  806.   BPTR pathList = NULL;
  807.   struct PathEntry *pathEntry, *pathET;
  808.  
  809.   if (Peer)
  810.   {
  811.     for (pathEntry = BADDR(Peer->cli_CommandDir),
  812.          pathET = (struct PathEntry *) &pathList;
  813.          pathEntry;
  814.          pathEntry = BADDR(pathEntry->pe_NextPathEntry))
  815.     {
  816.       struct PathEntry *newPE;
  817.  
  818.       if (newPE = AllocMem(sizeof(struct PathEntry), MEMF_PUBLIC))
  819.       {
  820.         newPE->pe_NextPathEntry = NULL;
  821.         newPE->pe_PathLock = DupLock(pathEntry->pe_PathLock);
  822.         pathET->pe_NextPathEntry = MKBADDR(newPE);
  823.         pathET = newPE;
  824.       }
  825.       else
  826.         break;
  827.     }
  828.   }
  829.  
  830.   return pathList;
  831. } /* ClonePathList */
  832.  
  833.  
  834. STATIC void
  835. FreePathList(BPTR PathList)
  836. {
  837.   struct PathEntry *pathEntry;
  838.  
  839.   while (pathEntry = BADDR(PathList))
  840.   {
  841.     PathList = pathEntry->pe_NextPathEntry;
  842.     UnLock(pathEntry->pe_PathLock);
  843.     FreeMem(pathEntry, sizeof(struct PathEntry));
  844.   }
  845. } /* FreePathList */
  846.