/*
 *   This simple little program monitors the files that the `child'
 *   process opens; it can be used for user-level debugging and bug
 *   reporting.  (What files does it *really* try to open?)
 *
 *   The procedure we follow is relatively simple.  We first spawn a
 *   full-fledged process to record the output, since we want to use
 *   the current CLI process to execute our monitored program.  This
 *   process opens either a window or a file, depending on the flags,
 *   to send the output to.
 *
 *   Next, we plug in the WaitPkt ptr of our task with a pointer to a
 *   routine that will actually send a message to our spawned process
 *   that there is information, and then wait for a reply.
 * 
 *   Next, we execute the program we want to monitor.  This is
 *   synchronous, so we wait for it to return.
 *
 *   As soon as this process returns, we can safely restore the old
 *   WaitPkt value, and notify the monitoring process that it can quit.
 *   (The monitoring process will wait for a carriage return from the
 *   window opened before exiting, if and only if no output file was
 *   specified.)
 *
 *   And then we exit.
 *
 *   Acknowledgement to Leo Schwab (for CreateProc hints) and Phillip
 *   Lindsay (for MonProc hints); sections of code lifted freely from
 *   both, but comments removed for brevity.
 *
 *   Compile with Aztec C.
 */
#include <exec/types.h>
#include <libraries/dosextens.h>
#include <functions.h>
struct PhonySegList {
   BPTR psl_NextSeg;
   UWORD psl_JMP;
   LONG (*psl_EntryPoint)();
};
struct PhonySegList template = {0,0x4EF9,0};
char *filename ;
struct MsgPort *mainport, *replyport ;
struct MsgPort *monport ;
struct Lock *currdir ;
#define START (1)
#define DONE (2)
#define FILENAME (3)
struct mymsg {
   struct Message msg ;
   int type ;
} msg ;
#define ACTION_FIND_INPUT (1005)
#define ACTION_FIND_OUTPUT (1006)
struct FileHandle *doswin ;
struct DosPacket *pkt ;
long delayval ;
LONG coprocess()
{
   register struct mymsg *m ;
   register long foo ;
   struct Lock *olddir ;
#ifdef AZTEC_C
   geta4();
#endif
   olddir = CurrentDir(currdir) ;
   mainport = &(((struct Process *)FindTask(0L))->pr_MsgPort) ;
   while (1) {
      while ((m = (struct mymsg *)GetMsg(mainport))==0)
         WaitPort(mainport) ;
      switch(m->type) {
case START:
         m->type = -1 ;
         if ((mainport = CreatePort(0L, 0L)))
            if ((doswin =
     Open((filename?filename:"CON:0/0/320/100/MonFiles"),MODE_NEWFILE))==0)
                DeletePort(mainport) ;
            else
                m->type = 0 ;
         break ;
case FILENAME:
         Write(doswin, "`", 1L) ;
         foo = ((long)(pkt->dp_Arg3)) << 2 ;
         Write(doswin, ((char *)foo)+1, (long)*((unsigned char *)foo)) ;
         Write(doswin, pkt->dp_Type==ACTION_FIND_INPUT?"' (r)":"' (w)", 5L) ;
         if (pkt->dp_Res1)
            Write(doswin, " succeeded\n", 11L) ;
         else
            Write(doswin, " failed\n", 8L) ;
         m->type = 0 ;
         if (delayval > 3)
            Delay(delayval) ;
         break ;
case DONE:
default:
         m->type = -1 ;
      }
      if (m->type) {
         if (filename == 0)
            Delay(100L) ;
         if (doswin) {
            Close(doswin);
            doswin = 0 ;
         }
         if (mainport) {
            DeletePort(mainport) ;
            mainport = 0 ;
         }
         CurrentDir(olddir) ;
         Forbid() ;
         ReplyMsg(m) ;
         return(0) ;
      } else {
         ReplyMsg(m) ;
      }
   }
}
#ifdef AZTEC_C
long PWait() ;
#else
#define PWait PacketWait
#endif
/*
 *   This is the task that actually replaces PacketWait.
 */
#define DOS_MASK (1L << 8)
struct Message *PacketWait() {
   register struct Message *dmsg ;
#ifdef AZTEC_C
   geta4() ;
#endif
   SetSignal(-1L, DOS_MASK) ;
   while ((dmsg = GetMsg(monport))==0)
      Wait(DOS_MASK) ;
   pkt = (struct DosPacket *)(dmsg->mn_Node.ln_Name) ;
   if (pkt->dp_Type == ACTION_FIND_INPUT ||
       pkt->dp_Type == ACTION_FIND_OUTPUT)
      putmsg(FILENAME) ;
   return dmsg ;
}
int putmsg(what)
int what ;
{
   msg.type = what ;
   msg.msg.mn_Node.ln_Type = NT_MESSAGE;
   msg.msg.mn_Node.ln_Pri = 0;
   msg.msg.mn_ReplyPort = replyport;
   PutMsg(mainport, &msg);
   WaitPort(replyport) ;
   GetMsg(replyport) ;
   return msg.type ;
}
main(argc, argv)
int argc ;
char *argv[] ;
{
   struct PhonySegList *fakelist = 0 ;
   struct Process *me ;
   LONG priority;
   APTR opktwait ;
   register char *p ;
   register int option ;

   if (argc > 1 && argv[1][0] == '-') {
      argv++ ;
      argc-- ;
      option = argv[0][1] ;
      p = argv[0] + 2 ;
      if (*p == 0 && argc > 1) {
         argv++ ;
         argc-- ;
         p = *argv ;
      }
      switch (option) {
case 'o': case 'O':
         filename = p ;
         break ;
case 'd': case 'D':
         delayval = atol(p) ;
         break ;
default:
         Write(Output(), "No such option\n", 15L) ;
      }
   }
   if (((replyport = CreatePort(0L, 0L))==0) ||
       ((fakelist = AllocMem((LONG)sizeof(*fakelist), 0L))==0))
      goto xit;
   CopyMem(&template, fakelist,(LONG)sizeof(template));
   fakelist->psl_EntryPoint = coprocess;
   me = (struct Process *)FindTask(0L);
   monport = &(me->pr_MsgPort) ;
   priority = me->pr_Task.tc_Node.ln_Pri;
   currdir = Lock("", SHARED_LOCK) ;
   if ((struct MsgPort *)
          CreateProc("MonFiles2", priority, ((LONG)fakelist) >> 2, 4096L)==0)
      goto xit;
   if (putmsg(START))
      goto xit ;
   Forbid() ;
   opktwait = me->pr_PktWait ;
   me->pr_PktWait = (APTR)PWait ;
   Permit() ;
#ifdef AZTEC_C
   fexecv(argv[1], argv+1) ;
#else
   I don't know how to do this in Lattice yet
   The spawned process has to execute in the *same CLI*
#endif
   Forbid() ;
   me->pr_PktWait = opktwait ;
   Permit() ;
   putmsg(DONE) ;
   if (currdir)
      UnLock(currdir) ;
xit:
   if (fakelist) FreeMem(fakelist,(LONG)sizeof(*fakelist));
   if (replyport) DeletePort(replyport);
}
#ifdef AZTEC_C
_wb_parse() {}
#asm
	XREF	_PacketWait
	XDEF	_PWait
_PWait:
	movem.l	a2/a3/a4,-(sp)
	jsr	_PacketWait
	movem.l	(sp)+,a2/a3/a4
	rts
#endasm
#endif
