home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / fish / disks / d1056.lha / Programs / APipe-Handler / pgmpipe.c < prev    next >
C/C++ Source or Header  |  1994-11-08  |  25KB  |  864 lines

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