/*
   COMPILE WITH:

 lc1 -b0 -cfiks -d2 -r -rr -j121i -oQUAD:pm.q pm.c
 go  quad:pm.q
 lc2 -v -opm.o quad:pm.q

Gets a CXERR 26, Line: 155 out of LC2.
My LC1 and LC2 are both V5.01. Needs AREXX include files.
Commenting out the if on lines 99-102 will cause it to work.
*/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */ 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 * VERY loosely based on the input.device example by Rob Peck, 12/1/85
 */

#include "popcli.h"
#include "popbug.h"
#include "cback.h"
#include <rexx/storage.h>


#ifdef BUGMACROS
#define __BugWindow (*(BPTR *)"BUG") /* 4 bytes, big enuf for a BPTR */
#endif

/************************************************************************/
/* the main program to do the popcli stuff                              */
/************************************************************************/

#undef global   /* Since it really is local in this routine */

void _main()
   {
   struct cbackstr     *cback;

   struct MsgPort      *port;
   int                 stay = 0;
   int                 shutdown;
   short               timeout;
   struct OURMSG       *msg;
   register CommandBlock *cb;
   int                 key;
   struct Process      *us;

   BPTR  nullfh;
   ULONG sig, portsig, timersig;
   struct timerequest *timerreq;
   struct MsgPort     *timerport;
   struct MsgPort     *inputDevPort;
   struct IOStdReq    *inputRequestBlock;
   struct Interrupt    handlerStuff;
   GLOBAL_DATA         global;
   struct Process     *SyncProcess;
   int                 SyncSignal;
   char               *cmd;
   int                 cmdlen;
   struct RexxMsg     *amsg;
   struct OURMSG       pmsg;   /* for AREXX message parsing */

   int                 rc;

   memset( (char *)&global, 0, sizeof(global) );
   
   DOSBase = (struct DosBase *)OpenLibrary("dos.library",0);

   global.blanksignum  = -1;
   timerreq            = NULL;
   timerport           = NULL;
   inputDevPort        = NULL;
   inputRequestBlock   = NULL;
   msg                 = NULL;

   timeout             = DEFTIME;

   /* Fetch our arguments from the wild blue yonder */
   us = (struct Process *)FindTask(0);
   us->pr_ConsoleTask = NULL; 
   WaitPort( &(us->pr_MsgPort) );
   cback = (struct cbackstr *)GetMsg( &(us->pr_MsgPort) );

   global.stdout = cback->stdout;

   /* Bit of a kludge... remove the trailing "\n" so we don't have to */
   /* muck with it in popparse(). Put a null on, too; and we can't    */
   /* overwrite because there may not be enough room.                 */
   cmdlen = cback->cmdlen;
   cmd = AllocMem( cmdlen + 1, 0 );
   memcpy( cmd, cback->cmd, cmdlen );
/* !!!!! IF YOU COMMENT OUT THIS IF STATEMENT, THE CXERR 26 GOES AWAY! */
   if (cmd[cmdlen-1] == '\n')
      cmd[cmdlen-1] = '\0';
   else
      cmd[cmdlen] = '\0';

   SyncProcess = cback->SyncProcess;
   SyncSignal = cback->SyncSignal;

   FreeMem( (char *)cback, cback->msgpart.mn_Length );

   /* now see if we are already installed */
   if ((port = FindPort(PORTNAME)) == NULL)
      {
      stay = 1; /* remember to hang around when we are done */
#ifdef BUGMACROS
      __BugWindow = Open(BUGWINDOW,MODE_NEWFILE);
#endif
      BUG(("Setting stay to 1 - first time here\n"));

      /* not installed, we need to install our own port */
      if ((port = MyCreatePort(&global, PORTNAME)) == NULL)
         goto abort;

      /* Tell everybody we're here... */
      popbanner( &global );
      }
#ifdef BUGMACROS
   else
      __BugWindow = Open("*", MODE_OLDFILE);
#endif

   /* Save port for handler to use */
   global.execport = port;

   /* now send the parameter to the waiting program */
   if ((msg = (struct OURMSG *)
              AllocMem(sizeof(struct OURMSG), MEMF_CLEAR|MEMF_PUBLIC)) == NULL)
      goto abort;
   /* fill in the message information */
   msg->msgpart.mn_Length = sizeof(struct OURMSG);

   if (cmd[cmdlen-1] == '\n')
      cmd[cmdlen-1] = '\0';      

   rc = popparse( &global, cmd, msg );

   /* We're done with the command line */
   FreeMem( cmd, cmdlen+1 );

   if (rc == SHUTDOWN)
   {
      /* Print shutting down message */
      poptrailer( &global );
   }

   /* Get stack size and stuff from our parent, if we're the server */
   if (stay)
      copyproc( &global, us, SyncProcess );

   /* Let the original window go away */
   if (global.stdout)
      Close(global.stdout);
   global.stdout = 0;

   /* We got everything, so it's safe to let the parent go away */
   if (SyncSignal != -1)
   {
      Signal((struct Task *)SyncProcess, 1<<SyncSignal);
      SyncSignal = -1;
   }

   global.blankscreen = NULL;
   if (stay && msg->interval == 0)
      msg->interval = DEFTIME;

   PutMsg(port,(struct Message *)msg);

   msg = NULL;

   /* If we're told to go away */
   if ((rc == SHUTDOWN) || (rc == LEAVE) || !stay)
   {
      goto abort;
   }

   global.noevents = 0;

   /* set the input and output streams to 0 so execute doen't complain */
   nullfh = Open("NIL:", MODE_NEWFILE);

   if (((inputDevPort = MyCreatePort(&global,NULL)) == NULL)                 ||

      ((inputRequestBlock =
          CreateIOReq(&global, inputDevPort, sizeof(struct IOStdReq))) == NULL)     ||

      ((timerport = MyCreatePort(&global,NULL)) == NULL)                     ||

      ((timerreq  = (struct timerequest *)
          CreateIOReq(&global,timerport, sizeof(struct timerequest))) == NULL) ||

      ((global.blanksignum = AllocSignal(-1)) == -1)                       ||

      ((GfxBase = (GBasePtr)
                  OpenLibrary("graphics.library", 0)) == NULL)             ||

      ((IntuitionBase = (IBasePtr)
                        OpenLibrary("intuition.library", 0)) == NULL)      ||
      OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)timerreq, 0)  ||

      OpenDevice("input.device",0,(struct IORequest *)inputRequestBlock,0))

      goto abort;

   handlerStuff.is_Data = (APTR)&global;
   handlerStuff.is_Code = (VOID(*)())myhandler;
   handlerStuff.is_Node.ln_Pri = 51;

   timersig            = (1 << timerport->mp_SigBit);
   portsig             = (1 << port->mp_SigBit);
   global.unblanksig   = 1 << global.blanksignum;

   inputRequestBlock->io_Command = IND_ADDHANDLER;
   inputRequestBlock->io_Data    = (APTR)&handlerStuff;

   DoIO((struct IORequest *)inputRequestBlock);

   global.buddy = (struct Task *)us;

   QueueTimer(&global, timerreq, TIMEINTERVAL);

   /* All other fields in newscreen are 0 */
   global.newscreen.Width    = 320;
   global.newscreen.Height   = 30;
   global.newscreen.Depth    = 1;
   global.newscreen.BlockPen = 1;
   global.newscreen.Type     = CUSTOMSCREEN;

   shutdown = 0;
   while(!shutdown)
      {
      sig = Wait( portsig | global.unblanksig | timersig );

      /* see if we have anything to do */
      while ((msg = (struct OURMSG *)GetMsg(port)) != NULL)
      {
	 if ((msg->msgpart.mn_Node.ln_Name) &&
             !strcmp(msg->msgpart.mn_Node.ln_Name, "REXX"))
	 {
	    /* we've got an ARexx message to parse. */
	    amsg = (struct RexxMsg *)msg;
	    rc = popparse( &global, amsg->rm_Args[0], &pmsg );
	    amsg->rm_Result1 = (rc == LEAVE) ? 5 : 0;
	    amsg->rm_Result2 = 0;
	    ReplyMsg( (struct Message *)amsg );

            /* So our normal msg processing code can handle it */
	    msg = &pmsg;
         }
	 else
	    amsg = NULL;

         /* See which type of message we have */
         switch( msg->type ) {

         /*----------------------------------------------------*/
         case MSG_ADDKEY:
            BUG(("*Add Key %d\n", msg->key));

            /* Because of parsing, we stick the interval information */
            /* in with the MSG_ADDKEY. There should be a better way  */
            /* to do this.                                           */
            if (msg->interval)
            {
               timeout = msg->interval;
               BUG(("Interval == %d ticks\n", timeout));
            }

            /* If we already have defined this key, then we can   */
            /* update its command, else add the key.              */
            cb = global.keytab[msg->key];

            if (!cb)
            {
               /* Its not there, we need to add the key */
               if (cb = (CommandBlock *)
                             AllocMem(sizeof(CommandBlock),MEMF_PUBLIC))
               {

                  /* Give new key the default command */
                  strcpy(cb->command, DEFCMD);
                  cb->cmdflags = 0;
                  cb->minmem   = msg->minmem;
                  cb->minchip  = msg->minchip;
                  global.keytab[msg->key] = cb;

                  BUG((" creating <%d> at 0x%08lx", msg->key, (long)cb));
               }
            }

            /* Update the key command, for new or old key */
            if (cb && msg->command[0])
            {
               strcpy(cb->command, msg->command);
               cb->cmdflags = msg->cmdflags;
            }

#ifdef BUGMACROS
            if (cb)
               BUG(("  New command = `%s'\n", cb->command));
#endif
            break;

         /*----------------------------------------------------*/
         case MSG_DELKEY:
            BUG(("*Delete Key %d\n", msg->key));

            cb = global.keytab[msg->key];
	    if (cb)
	    {
               BUG(("  killing <%d> at 0x%08lx\n", msg->key, (long)cb));

               global.keytab[msg->key] = NULL;
               FreeMem( (char *)cb, sizeof(CommandBlock) );
            }
            break;

         /*----------------------------------------------------*/
         case MSG_EXECUTE:
            BUG(("*Execute for key %d\n", msg->key));
            cb = global.keytab[msg->key];

	    if (cb)
	    {
               BUG(("   Executing `%s'\n", cb->command));

               if (((cb->minmem  == 0) ||
                          ((AvailMem(MEMF_PUBLIC)>>10) > cb->minmem )) &&
                   ((cb->minchip == 0) ||
                          ((AvailMem(MEMF_CHIP  )>>10) > cb->minchip)))
               {
                  if (!(cb->cmdflags & FLAG_NOWB2F))
                     WBenchToFront();

                  if (! Execute(cb->command, nullfh, nullfh))
                     DisplayBeep(NULL);
               }
               else
                  DisplayBeep(NULL);
            }
	    break;

         /*----------------------------------------------------*/
         case MSG_SHUTDOWN:
            BUG(("Shut UP!!\n"));

            /* Cause us to drop out of loop when we're thru */
            shutdown = 1;
            break;

         /*----------------------------------------------------*/
         case MSG_BLANK:
            if (global.blankscreen == NULL)
            {
               if ( (global.blankscreen = OpenScreen(&global.newscreen)) != NULL)
               {
                  BUG(("Screen OFF!!!!!\n"));
                  SetRGB4(&(global.blankscreen->ViewPort), 0, 0, 0, 0);
                  OFF_DISPLAY
               }
            }
            break;

         /*----------------------------------------------------*/
         case MSG_UNBLANK:
            if (global.blankscreen)
            {
               CloseScreen(global.blankscreen);
	       ON_DISPLAY
               global.blankscreen = NULL;
               global.noevents = 0;
            }
            break;

         /*----------------------------------------------------*/
         case MSG_SETTIME:
            timeout = msg->interval;
            break;

         /*----------------------------------------------------*/
         default:
            BUG(("HEY, I got an unknown message at %08lx!!!!!\n", msg));
         }

         if (!amsg)
            FreeMem((char *)msg, msg->msgpart.mn_Length);
      }

      if ((sig & global.unblanksig) && global.blankscreen)
         {
         BUG(("Screen On\n"));

         CloseScreen(global.blankscreen);
	 ON_DISPLAY
         global.blankscreen = NULL;
         }

      if (sig & timersig)
         {
         /* get rid of the message */
         (void)GetMsg(timerport);
         QueueTimer(&global, timerreq, TIMEINTERVAL);

         if ((global.noevents++ >= timeout) && (global.blankscreen == NULL))
            {
            if ( (global.blankscreen = OpenScreen(&global.newscreen)) != NULL)
               {
               BUG(("Screen OFF!!!!!\n"));
               SetRGB4(&(global.blankscreen->ViewPort), 0, 0, 0, 0);
               OFF_DISPLAY
               }
            }
         }

      /* Force our screen to front on a regular basis to handle something   */
      /* that might put their stuff in front of us while we are supposed to */
      /* Be blanking the screen.                                            */
      if (global.blankscreen)
         {
         BUG(("Screen still off\n"));
#if 1
         if (((IBasePtr)IntuitionBase)->FirstScreen != global.blankscreen)
            ScreenToFront(global.blankscreen);
#endif
         OFF_DISPLAY
         }
      }

   BUG(("Normal POPCLI shutdown beginning...\n"));

abort:

   if (stay)
      BUG(("In `abort'...\n"));

   if (msg) FreeMem((char *)msg, msg->msgpart.mn_Length);
   if (timerreq != NULL)
      {
      if (timerreq->tr_node.io_Device != NULL)
         {
         AbortIO( (struct IORequest *)timerreq );
         CloseDevice((struct IORequest *)timerreq);
         }
      DeleteIOReq(&global, (struct IOStdReq *)timerreq);
      }

   if (inputRequestBlock != NULL)
      {
      if (inputRequestBlock->io_Device != NULL)
         {
         inputRequestBlock->io_Command = IND_REMHANDLER;
         inputRequestBlock->io_Data = (APTR)&handlerStuff;
         DoIO((struct IORequest *)inputRequestBlock);

         CloseDevice((struct IORequest *)inputRequestBlock);
         }
      DeleteIOReq(&global, inputRequestBlock);
      }

   if (timerport != NULL)          MyDeletePort(&global, timerport);
   if (global.blanksignum != -1)   FreeSignal(global.blanksignum);
   if (global.blankscreen != NULL) CloseScreen(global.blankscreen);
   if (IntuitionBase != NULL)      CloseLibrary((struct Library *)IntuitionBase);
   if (GfxBase != NULL)            CloseLibrary((struct Library *)GfxBase);
   if (inputDevPort != NULL)       MyDeletePort(&global, inputDevPort);
   if (stay && (port != NULL))
   {
      /* Empty the port before we trash it */
      while (msg = (struct OURMSG *)GetMsg(port))   /* assign */
         FreeMem( (char *)msg, msg->msgpart.mn_Length );

      MyDeletePort(&global, port);
      BUG(("Port gone..."));
   }

   if (global.stdout)
      Close(global.stdout);
   if (SyncSignal != -1)
      Signal((struct Task *)SyncProcess, 1<<SyncSignal);

   if (stay)
      {
      /* NOTE: "prev" is really used as a "next" here */
      for (key = 0; key < 256; key++)
          {
          if (cb = global.keytab[key])
	     {
             BUG(("Deleting key <%d> at 0x%08lx\n", key, cb));

             FreeMem( (char *)cb, sizeof(CommandBlock) );
	     }
          }
      }

   if (nullfh)                     Close(nullfh);

   if (stay)
      BUG(("..bye bye"));

#ifdef BUGMACROS
   if (stay)
   {
     int paws = 100000;
     while(--paws>0);
     Close(__BugWindow);
   }
#endif

   /* Free all our copyproc() stuff, if we were the server */
   if (stay)
      termproc( &global, (struct Process *)FindTask(NULL) );

   if (DOSBase)
      CloseLibrary( (struct Library *)DOSBase );
}

#ifdef BUGMACROS
#undef DOSBase

void __stdargs myprintf(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char	*fmt;
long	a1, a2, a3, a4, a5, a6, a7, a8, a9;
{
	int			bytes, fmtlen;
	char			buff[512];
	struct DosLibrary	*DOSBase;

	if (__BugWindow <= 0) return(0);
#if 0
	fmtlen = strlen(fmt);
	if (fmtlen > 70)
	{
		bytes = sprintf(buff, "BUGMSG: 0x%x, length %d\n", fmt, fmtlen);
		Write(__BugWindow, buff, bytes);
		return(0);
	}
	else
	{
		bytes = sprintf(buff, "BUG(%s)\n", fmt);
		Write(__BugWindow, buff, bytes);
	}
#endif
	
	bytes = sprintf(buff, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);

	DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",0);

	if (bytes < sizeof(buff))
		Write(__BugWindow, buff, bytes);
	else
		Write(__BugWindow, "!!! msg too big - prepare to die\n",
			    strlen("!!! msg too big - prepare to die\n"));

	CloseLibrary( (struct Library *)DOSBase );
	return(0);
}
#endif
