#include "Includes.h"
#include "Defines.h"
#include "Protos.h"
#include "Vars.h"
#include "top.h" // Get rid of this!

#include <link/io.h>

#include <devices/timer.h>

#define FORCE1_3 1   /* uses old CreateProc stuff */

struct Library *TimerBase;

struct List *queues[CLASS_TOTALS];

BOOL RECONFIGFLAG=FALSE;   /*do not put this in the globals.h file*/

BOOL DLGMAILSTOP=FALSE;

int CPxBusy[5];

int PROCESSINGSUSPENDED=0;

/// GetCurWindow
struct Window *GetCurWindow(void)
{
   struct Window *win;
   ULONG lock;

   lock=LockIBase(0);

   win=IntuitionBase->ActiveWindow;
   
   UnlockIBase(lock);

   return win;
}
//-

/// CreateQEntry
/*******************
 *
 * PRIVATE
 * CreateQEntry
 *
 *******************/

/* the following is a "private" function, used by the other
   list manipulation routines to add queue entries */

struct ZQueue * CreateQEntry( char *command,
            char *args,
            ULONG token,
            ULONG flags,
            char pri)
{
   struct ZQueue *q;

   q=(struct ZQueue *)AllocMem(sizeof(struct ZQueue),MEMF_PUBLIC|MEMF_CLEAR);
   if(q)
   {
      q->zq_node.ln_Type=LISTTYPE;
      q->zq_node.ln_Pri=pri;
      q->zq_node.ln_Name=(char *)(q+14);

      strncpy(q->zq_command,command,ZQ_CMDLENGTH);
      strncpy(q->zq_args,args,ZQ_ARGLENGTH);
      q->zq_commandtoken=token;
      q->zq_flags=flags;

      return(q);
   }
   return(NULL);
}
//-

/// KillQEntry
/*******************
 *
 * PUBLIC
 * KillQEntry
 *
 *******************/

/* public, frees the memory used by a queue entry */

int KillQEntry(struct ZQueue *q)
{
   if(q)
   {
      FreeMem(q,sizeof(struct ZQueue));
      return 1;
   }
   return(0);
}
//-

/// DeleteDupeQEntries
/*******************
 *
 * PUBLIC
 * DeleteDupeQEntries
 *
 *******************/

/* public, used to remove the head entry from the queue */
/* OBSERVE TYPE RETURNED */

int DeleteDupeQEntries(int class,struct ZQueue *rem)
{
   int count=0;
   struct ZQueue *q;
   struct List *lh;

   lh=queues[class];

   Forbid();
   while(1)
   {
      for(q=(struct ZQueue *)lh->lh_Head;q->zq_node.ln_Succ;q=(struct ZQueue *)q->zq_node.ln_Succ)
      {
         if((q->zq_commandtoken==rem->zq_commandtoken) && (0==Stricmp(q->zq_args,rem->zq_args)))
         {
            /*match*/
            Remove((struct Node *)q);
            count++;
            break;
         }
      }
      if(q->zq_node.ln_Succ==NULL)break;
   }
   Permit();
   return(count);
}
//-

/// CreateZQList
/*******************
 *
 * PRIVATE
 * CreateZQList
 *
 *******************/

/* private, initializes a queue */

struct List * CreateZQList(int class)
{
   struct List *qlist;

   if(queues[class]) return NULL;   /* list already inited */

   qlist=(struct List *)AllocMem(sizeof(struct List),MEMF_CLEAR|MEMF_PUBLIC);
   if(!qlist)
   {
      return(NULL);
   }
   qlist->lh_Head=(void *)&qlist->lh_Tail;
   qlist->lh_TailPred=(void *)&qlist->lh_Head;
   qlist->lh_Tail=NULL;
   qlist->lh_Type=LISTTYPE;

   queues[class]=qlist; /* magic! */

   return(qlist);
}
//-

/// AddToQueue
/*******************
 *
 * PUBLIC
 * AddToQueue
 *
 *******************/

/* public, adds an entry to one of the queues - class determines which queue */

int AddToQueue(   char *command,
      char *args,
      int token,
      int class,
      ULONG flags,
      int pri)
{
   struct ZQueue *q;

   q=CreateQEntry(command,args,token,flags,pri);
   if(q)
   {
      Forbid();
      Enqueue(queues[class],(struct Node *)q);
      Permit();
      return 1;
   }
   return 0;
}
//-

/// GetFrom Queue
/*******************
 *
 * PUBLIC
 * GetFromQueue
 *
 *******************/

/* public, used to remove the head entry from the queue */
/* OBSERVE TYPE RETURNED */

struct ZQueue * GetFromQueue( int class,
            char *command)
{
   struct ZQueue *q;
   
   Forbid();
   q=(struct ZQueue *)RemHead(queues[class]);
   Permit();
   if(q)
   {
      if(command)
      {
         strcpy(command,"");
         if(*q->zq_command) strcpy(command,q->zq_command);
      }
      return(q);
   }
   return(NULL);
}
//-

/// ShowZQ
/*******************
 *
 * PUBLIC
 * ShowZQ
 *
 *******************/

/* public, used to clear the queue and display what was there */

int ShowZQ(int class)
{
   struct ZQueue *q;
   char command[30];

   while(1)
   {
      q=GetFromQueue(class,command);
      if(q)
      {
         KillQEntry(q);
      }
      else
         break;
   }
   return 1;
}
//-

/// InitQueues
/*******************
 *
 * PUBLIC
 * InitQueues
 *
 *******************/

/* public, used once at program startup */

int InitQueues(void)
{
   int i;
   struct List *l;

   for(i=0;i<CLASS_TOTALS;i++)
   {
      l=CreateZQList(i);
      if(!l)
         return 0;
   }
   return 1;
}
//-

/// DeleteZQList
/*******************
 *
 * PRIVATE
 * DeleteZQList
 *
 *******************/

/* private, removes a queue */

void DeleteZQList(int class)
{
   struct ZQueue *q;

   while(1)
   {
      q=GetFromQueue(class,NULL);
      if(q)
         KillQEntry(q);
      else
         break;
   }

   FreeMem(queues[class],sizeof(struct List));
   return;
}
//-

/// KillQueues
/*******************
 *
 * PUBLIC
 * KillQueues
 *
 *******************/

/* public, used once at program close */

int KillQueues(void)
{
   int i;
   
   for(i=0;i<CLASS_TOTALS;i++)
   {
      DeleteZQList(i);
   }
   return 1;
}
//-

/// ParseArgs
/*******************
 *
 * PRIVATE
 * ParseArgs
 *
 *******************/

int ParseArgs(char **argv,char *argstring)
{
   char token[100];
   char *s;
   int i=0;

   s=argstring;

   argv[i]=NULL;

   while(*s != NULL && i<MAX_ARGS)
   {
      s=stpblk(s);
      if(*s)
      {
         argv[i]=s;
         i++;
         argv[i]=NULL;
         s=stptok(s,token,sizeof(token)," \t");
         if(*s) *s++=NULL;
      }
   }
   return(i);
}
//-

/// WinOpen
BPTR WinOpen(int x, int y, int wid, int height, char *name, char *scnname)
{
   BPTR fh=0;
   char buf[128];
   
   ASPrintf(NULL,buf,"RAW:%d/%d/%d/%d/%s ",x,y,wid,height,name);

   if(OS_VERSION>36)
   {
      strcat(buf,"/INACTIVE");

      if(*scnname)
      {
         strcat(buf,"/SCREEN ");
         strcat(buf,scnname);
      }
   }

   CP_CommandLine(9,buf);

   fh=Open(buf,MODE_NEWFILE);

   return fh;
}
//-

/// CoProcImmed
/*******************************************************************************
 *
 * PRIVATE
 * CoProcImmed
 *
 *******************************************************************************/

__saveds void CoProcImmed(void)
{
   struct ProcMsg *startupmsg;
   BPTR cdlock;
   BPTR oldlock;
   struct ZQueue *zq;
   int ARGC;
   char *ARGV[MAX_ARGS];
   char command[100];
   char argbuf[300];

   int MYCLASS=0;
   struct Process *me;
   int i;

   me=(struct Process *)FindTask(NULL);
   WaitPort(&me->pr_MsgPort);
   startupmsg=(void *)GetMsg(&me->pr_MsgPort);

   cdlock=Lock("INBOUND:",ACCESS_READ);
   if(cdlock) oldlock=CurrentDir(cdlock);

   /*pdmain(me);*/

   while(1)
   {
      zq=GetFromQueue(MYCLASS,command);
      if(!zq) break;
      strcpy(argbuf,zq->zq_args);

      CP_CommandLine(MYCLASS,"%.40s %.80s",zq->zq_command,argbuf);

      ARGC=ParseArgs(ARGV,argbuf);

      switch(zq->zq_commandtoken)
      {
         case 11:
         __REPORT(ARGC,ARGV);
         break;

         case 12:
         __ZMH(ARGC,ARGV);
         break;

         case 13:
         __MSGADDED(ARGC,ARGV);
         break;

         case 14:
         Delay(300);
         break;

      }


      i=DeleteDupeQEntries(MYCLASS,zq);

      KillQEntry(zq);
   }

   CP_CommandLine(MYCLASS,"");

   if(cdlock)
   {
      CurrentDir(oldlock);
      UnLock(cdlock);
   }

   Forbid();
   ReplyMsg((struct Message *)startupmsg);
}
//-

/// CoProcBkgnd
/*******************
 *
 * PRIVATE
 * CoProcBkgnd
 *
 *******************/

__saveds void CoProcBkgnd(void)
{
/**/  struct ProcMsg *startupmsg;
   BPTR cdlock;
   BPTR oldlock;
   int myerror;
   char logbuf[250];
   struct ZQueue *zq;
   int ARGC;
   char *ARGV[MAX_ARGS];
   char command[100];
   char argbuf[300];

   int MYCLASS=1;
   struct Process *me;
   int i;

   me=(struct Process *)FindTask(NULL);
   WaitPort(&me->pr_MsgPort);
   startupmsg=(void *)GetMsg(&me->pr_MsgPort);

   cdlock=Lock("INBOUND:",ACCESS_READ);
   if(cdlock) oldlock=CurrentDir(cdlock);

   myerror=getcd(0,logbuf);

   while(1)
   {
      zq=GetFromQueue(MYCLASS,command);
      if(!zq) break;
      strcpy(argbuf,zq->zq_args);
            
      CP_CommandLine(MYCLASS,"%.40s %.80s",zq->zq_command,argbuf);

      ARGC=ParseArgs(ARGV,argbuf);

      switch(zq->zq_commandtoken)
      {
         case 21:
         __QUICKATTACH(ARGC,ARGV); 
         break;

         case 22:
         __QUICKREQUEST(ARGC,ARGV);
         break;
      }

      i=DeleteDupeQEntries(MYCLASS,zq);
      KillQEntry(zq);
   }
   CP_CommandLine(MYCLASS,"");

   if(cdlock)
   {
      CurrentDir(oldlock);
      UnLock(cdlock);
   }

   Forbid();
   ReplyMsg((struct Message *)startupmsg);
}
//-

/// CoProcCall
/*******************
 *
 * PRIVATE
 * CoProcCall
 *
 *******************/

__saveds void CoProcCall(void)
{
/**/  struct ProcMsg *startupmsg;
   BPTR cdlock;
   BPTR oldlock;

   struct ZQueue *zq;
   int ARGC;
   char *ARGV[MAX_ARGS];
   char command[100];

   char argbuf[300];

   int MYCLASS=2;
   struct Process *me;
   int i;

   me=(struct Process *)FindTask(NULL);
   WaitPort(&me->pr_MsgPort);
   startupmsg=(void *)GetMsg(&me->pr_MsgPort);

   cdlock=Lock("INBOUND:",ACCESS_READ);
   if(cdlock) oldlock=CurrentDir(cdlock);

   while(1)
   {
      zq=GetFromQueue(MYCLASS,command);

      if(!zq) break;
      strcpy(argbuf,zq->zq_args);

      CP_CommandLine(MYCLASS,"%.40s %.80s",zq->zq_command,argbuf);

      ARGC=ParseArgs(ARGV,argbuf);

      switch(zq->zq_commandtoken)
      {
         case 31: 
         __NODELIST(ARGC,ARGV);
         break;

         case 32:
         __CALL(ARGC,ARGV);
         break;

         case 33:
         __DELAYCALL(ARGC,ARGV);
         break;

         case 34:
         __CRASH(ARGC,ARGV); 
         break;

         case 35:
         __DIRECT(ARGC,ARGV);
         break;

         case 36:
         __DIRECTONLY(ARGC,ARGV);
         break;

         case 37:
         __NORMAL(ARGC,ARGV);
         break;

         case 38:
         __REQUEST(ARGC,ARGV);
         break;

         case 39:
         __NO_PEND_TRAPDOOR(ARGC,ARGV);
         break;

         case 391:
         __ACCOUNTING(ARGC,ARGV);
         break;
      }


      i=DeleteDupeQEntries(MYCLASS,zq);

      KillQEntry(zq);
   }
   CP_CommandLine(MYCLASS,"");

   if(cdlock)
   {
      CurrentDir(oldlock);
      UnLock(cdlock);
   }

   Forbid();
   ReplyMsg((struct Message *)startupmsg);
}
//-

/// CoProcProc
/*******************
 *
 * PRIVATE
 * CoProcProc
 *
 *******************/

__saveds void CoProcProc(void)
{
   struct Window *lastwindow=NULL;
   struct ProcMsg *startupmsg;
   BPTR cdlock;
   BPTR oldlock;

   struct ZQueue *zq;
   int ARGC;
   char *ARGV[MAX_ARGS];
   char command[100];
   char argbuf[300];

   int w_x,w_y,w_wid,w_height;

   int MYCLASS=3;
   struct Process *me;
   BPTR window;
   BPTR mynull;
   int i;
   unsigned long crc;

   me=(struct Process *)FindTask(NULL);
   WaitPort(&me->pr_MsgPort);
   startupmsg=(void *)GetMsg(&me->pr_MsgPort);

// new march 15 1995

   if(PROCESSINGSUSPENDED)
      goto cp3here;

   cdlock=Lock("INBOUND:",ACCESS_READ);
   if(cdlock) oldlock=CurrentDir(cdlock);

   i_am_processing=1;

   w_x=0;
   w_y=120;
   w_wid=ScreenPixelWidth;
   w_height=80;

   if(_PROCESS_X)       w_x=_PROCESS_X;
   if(_PROCESS_Y)       w_y=_PROCESS_Y;
   if(_PROCESS_WIDTH)   w_wid=_PROCESS_WIDTH;
   if(_PROCESS_HEIGHT)  w_height=_PROCESS_HEIGHT;

   window=NULL;

   if(_PROCESSWINDOW)
   {
      if(OS_VERSION<37)
      {
         lastwindow=GetCurWindow();
         window=WinOpen(w_x,w_y,w_wid,w_height,"DLGMail Processing Window",_SCREENNAME);

         if(lastwindow)
            ActivateWindow(lastwindow);
      }
      else
      {
         window=WinOpen(w_x,w_y,w_wid,w_height,"DLGMail Processing Window",_SCREENNAME);
      }
   }
   else
   {
      window=Open("NULL:",MODE_NEWFILE);
   }

   sout = window;

   mynull=Open("NULL:",MODE_NEWFILE);

   if(window)
   {
      AFPrintf(NULL,window,"[?7l[0 p[0m[H[J");

      while(1)
      {
         if(_ROSSAREAS)
         {
            crc=CalcAreasCRC(_ROSSAREAS);

            if(_AREACRC==0)
            {
               _AREACRC=crc;
               CP_CommandLine(MYCLASS,"Areas integrity revalidated");
            }
            else
            {
               if(crc != _AREACRC)
               {
                  CP_CommandLine(MYCLASS,"Echo areas structures integrity violated!");
                  Delay(250);
                  DisplayErrReq(NULL,"Echo areas structures integrity violated.","To process mail, unload and relaunch DLGMail.");
                  break;
               }
            }
         }
         else
         {
            CP_CommandLine(MYCLASS,"Loading ARE file");

            if(!__LoadARE())
               break;
         }

// new march 15 1995
         zq=0;
         if(!PROCESSINGSUSPENDED)
// end new
            zq=GetFromQueue(MYCLASS,command);
         if(!zq) break;
         strcpy(argbuf,zq->zq_args);
            
         CP_CommandLine(MYCLASS,"%.40s %.80s",zq->zq_command,argbuf);

         ARGC=ParseArgs(ARGV,argbuf);

         switch(zq->zq_commandtoken)
         {
            case 40:
            __NETSCAN(ARGC,ARGV,window,mynull); 
            break;

            case 41:
            __PROCESS(ARGC,ARGV,window,mynull);
            break;

            case 42:
            __EXPORT(ARGC,ARGV,window,mynull);
            break;

            case 43:
            __CHANGEFLOW(ARGC,ARGV,window,mynull);
            break;

            case 44:
            __CHANGEPKT(ARGC,ARGV,window,mynull);
            break;

            case 45:
            __USEREXPORT(ARGC,ARGV,window,mynull);
            break;

            case 46:
            __TRIMLOGS(ARGC,ARGV,window,mynull);
            break;

            case 47:
            __CHANGEBOTH(ARGC,ARGV,window,mynull);
            break;
            
            case 48:
            CP_CommandLine(MYCLASS,"UNLoading ARE file - will reload when required");
            Delay(200);
            __UnLoadARE();
            break;
         }
         i=DeleteDupeQEntries(MYCLASS,zq);
         KillQEntry(zq);
      }

      if(window)
      {
         sout = Output();
         Close(window);
      }

      CP_CommandLine(MYCLASS,"");
   }
   else
   {
      CP_CommandLine(4,"Error: No window or NULL: for CP3");
      Delay(100);
   }

   if(mynull)
      Close(mynull);

   if(cdlock)
   {
      CurrentDir(oldlock);
      UnLock(cdlock);
   }

   i_am_processing=0;

cp3here:

   Forbid();
   ReplyMsg((struct Message *)startupmsg);
}
//-

/// ARexxHeart
/*******************
 *                 ***********************************************************
 * PUBLIC          *********** THIS IS THE MAIN ENTRY POINT FOR ALL THE  *****
 * ARexxHeart      *********** PROCESS/COPROCESS CODE AND THEIR HANDLING *****
 *                 ***********************************************************
 *******************/

int ARexxHeart(void)
{
   struct   ProcMsg                 *StartMsg[CLASS_TOTALS];
   struct   MsgPort                 *CoProcReplyPort[CLASS_TOTALS];
   struct   MsgPort                 *ZPort;
   struct   MsgPort              *timerport;
   struct   PhoneySegList        *FakeList[CLASS_TOTALS];
   struct   timerequest          *tr;
   struct   Process                 *ParentTask;
   struct   RexxMsg                 *RM;
   struct  MsgPort      *CoProc[CLASS_TOTALS];
   /*struct  Process    *CoProc204[CLASS_TOTALS];*/

   char     buf[20];

   int      INTERNAL_FIRE=0;
   int      i,j,k;
   BOOL     FAIL=TRUE;
   BOOL     INHIBIT=FALSE;

   int      PortBit;
   int      RxSigBit;
   int      TimeSigBit;
   BYTE     CoProcSigBit[CLASS_TOTALS];


   /*** Code Begins *******************/

   for(i=0;i<CLASS_TOTALS;i++)
   {
      CoProcSigBit[i]=-1;
      CoProcReplyPort[i]=NULL;
      FakeList[i]=NULL;
      CoProc[i]=NULL;
   }

   ParentTask=(struct Process *)FindTask(0);

   if(1)
   {
      timerport=(struct MsgPort *)CreatePort(0,0);
      if(timerport)
      {
         TimeSigBit=timerport->mp_SigBit;

         tr=(struct timerequest *)CreateExtIO(timerport,sizeof(struct timerequest));
         if(tr)
         {
           if(!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)tr,0))
           {

         /* phoney just to get it running */

         tr->tr_node.io_Command=TR_ADDREQUEST;
         tr->tr_time.tv_secs=600;
         tr->tr_time.tv_micro=0;
         SendIO((struct IORequest *)tr);


         ZPort=(struct MsgPort *)CreatePort(DLGMAILREXXPORT,NULL);
         if(ZPort)
         {
            RxSigBit=ZPort->mp_SigBit;
            ZPort->mp_SigTask=(struct Task *)ParentTask;

            StatCommandLine("ARexx port initialized");

            if(InitQueues())
            {
               StatCommandLine("Queues Initialized");

               FAIL=FALSE;

               for(i=0;i<CLASS_TOTALS;i++)
               {
                  StartMsg[i]=NULL;
                  StartMsg[i]=(void *)AllocMem(sizeof(struct ProcMsg),MEMF_PUBLIC | MEMF_CLEAR);
                  if(!StartMsg[i])
                  {
                     Log("! No mem for StartMsg[%d]",i);
                     FAIL=TRUE;
                     break;
                  }

                  CoProcReplyPort[i]=NULL;
                  CoProcReplyPort[i]=(struct MsgPort *)CreatePort(NULL,NULL);

                  if(!CoProcReplyPort[i])
                  {
                     StatCommandLine("Reply port %d failed to create",i);
                     Log("! Reply port %d failed to create",i);
                     FAIL=TRUE;
                     break;
                  }

                  CoProcSigBit[i]=CoProcReplyPort[i]->mp_SigBit;

                  FakeList[i]=NULL;                
                  FakeList[i]=(void *)AllocMem(sizeof(struct PhoneySegList),MEMF_PUBLIC | MEMF_CLEAR);

                  if(!FakeList[i])
                  {
                     StatCommandLine("Fakelist %d failure to allocate memory",i);
                     Log("! Fakelist %d failure to allocate memory",i);
                     FAIL=TRUE;
                     break;
                  }
                  
               }

               if(FAIL==FALSE)
               {
                  StatCommandLine("Completely Initialized");
                  
                  for(;;)
                  {
                     PortBit=Wait(1<<RxSigBit | 1<<TimeSigBit | 1<<CoProcSigBit[CLASS_IMMED] | 1<<CoProcSigBit[CLASS_BKGND] | 1<<CoProcSigBit[CLASS_CALL] | 1<<CoProcSigBit[CLASS_PROC]);

                     if(PortBit & 1<<RxSigBit)
                     {
                        while(1)
                        {
                           RM=(void *)GetMsg(ZPort);
                           if(RM)
                           {
                              if(RM->rm_Action & RXFF_STRING)
                              {
                                 PrintConsoleCoProc(RM->rm_Args[0]);
                                 ReplyMsg((struct Message *)RM);
                                 RM=NULL;
                              }
                           }

                           if(RM)
                           {
                              i=ParseARexxCommand(RM,INHIBIT); /* puts things into the queue */
                              ReplyMsg((struct Message *)RM);
                              if(!i)
                                 break;
                              if(i==-1)
                              {
                                 INHIBIT=TRUE;
                                 StatCommandLine("DLGMail inhibited, preparing to exit");
                              }
                           }

                           if( (RM && i>0) || (!RM && INTERNAL_FIRE))
                           {
                              if(!INHIBIT)
                              {
                                 /* ATTEMPT TO START NEW COPROCESSES HERE */
                                 /* Check if the coprocess is free */

                                 if(!RM && INTERNAL_FIRE)
                                 {
                                    i=INTERNAL_FIRE;
                                    INTERNAL_FIRE=0;
                                 }

                                 for(j=0;j<CLASS_TOTALS;j++)
                                 {
                                    if(CoProc[j]==NULL && (i-1)==j)
                                    {
                                       ASPrintf(NULL,buf,"CoProcess_<<%d>>",j);

                                       FakeList[j]->psl_size=0;
                                       FakeList[j]->psl_length=sizeof(*FakeList[j])+3 & ~3;
                                       FakeList[j]->psl_NextSeg=NULL;
                                       FakeList[j]->psl_JMP=0x4EF9;

                                       if(j==CLASS_IMMED) FakeList[j]->func=CoProcImmed;
                                       if(j==CLASS_BKGND) FakeList[j]->func=CoProcBkgnd;
                                       if(j==CLASS_CALL)  FakeList[j]->func=CoProcCall;
                                       if(j==CLASS_PROC)  FakeList[j]->func=CoProcProc;

                                       StartMsg[j]->msg.mn_Node.ln_Type = NT_MESSAGE;
                                       StartMsg[j]->msg.mn_Node.ln_Pri  = 0;
                                       StartMsg[j]->msg.mn_ReplyPort = CoProcReplyPort[j];
                                       StartMsg[j]->msg.mn_Length =sizeof(struct ProcMsg)-sizeof(struct Message);
                                       StartMsg[j]->global_data      =(void *)getreg(REG_A4);
                                       StartMsg[j]->return_code      =0;
                                       StartMsg[j]->seg           =FakeList[j];

/*  NOTE THAT PRIORITY WAS ADJUSTED HERE -> */

                                       CoProc[j]=(void *)CreateProc(buf,_TASKPRI-1,(BPTR)(LONG)&FakeList[j]->psl_NextSeg>>2,13004);

                                       if(CoProc[j])
                                       {
                                          PutMsg((struct MsgPort *)CoProc[j],(struct Message *)StartMsg[j]);
                                          CPxBusy[j]=1;
                                       }
                                       else
                                       {
                                          StatCommandLine("Error in launching new process");
                                       }
                                    }
                                 }
                              }
                           }

                           if(RM==0) break;
                        }
                     }

                     if(PortBit & 1<<TimeSigBit)
                     {
                        if(_AUTOCALL && !INHIBIT)
                        {
                           WaitIO((struct IORequest *)tr);

                           tr->tr_node.io_Command=TR_ADDREQUEST;
                           tr->tr_time.tv_secs=_AUTOCALL;
                           tr->tr_time.tv_micro=0;
                           SendIO((struct IORequest *)tr);
                           AddToQueue("CALL","",32,2,0,0);
                           INTERNAL_FIRE=3; /* coprocess # for CALL + 1 */
                           Signal((void *)ParentTask,1<<RxSigBit);
                        }
                     }

                     if(PortBit & 1<<CoProcSigBit[CLASS_IMMED])
                     {
                        GetMsg(CoProcReplyPort[CLASS_IMMED]);
                        CoProc[CLASS_IMMED]=NULL;
                        Signal((void *)ParentTask,1<<RxSigBit);
                        IF_ANNOYING StatCommandLine("CoProcess 0 returned");
                        CPxBusy[0]=0;
                     }

                     if(PortBit & 1<<CoProcSigBit[CLASS_BKGND])
                     {
                        GetMsg(CoProcReplyPort[CLASS_BKGND]);
                        CoProc[CLASS_BKGND]=NULL;
                        Signal((void *)ParentTask,1<<RxSigBit);
                        IF_ANNOYING StatCommandLine("CoProcess 1 returned");
                        CPxBusy[1]=0;
                     }

                     if(PortBit & 1<<CoProcSigBit[CLASS_CALL])
                     {
                        GetMsg(CoProcReplyPort[CLASS_CALL]);
                        CoProc[CLASS_CALL]=NULL;
                        Signal((void *)ParentTask,1<<RxSigBit);
                        IF_ANNOYING StatCommandLine("CoProcess 2 returned");
                        CPxBusy[2]=0;
                     }

                     if(PortBit & 1<<CoProcSigBit[CLASS_PROC])
                     {
                        GetMsg(CoProcReplyPort[CLASS_PROC]);
                        CoProc[CLASS_PROC]=NULL;
                        Signal((void *)ParentTask,1<<RxSigBit);
                        IF_ANNOYING StatCommandLine("CoProcess 3 returned");
                        CPxBusy[3]=0;
                     }

                     k=0;

                     for(j=0;j<CLASS_TOTALS;j++)
                        k |= (int)CoProc[j];

                     if(INHIBIT==TRUE && k==0)
                        break;

                     if(RECONFIGFLAG && k==0 && INHIBIT==FALSE)
                     {
                        StatCommandLine("Re-reading DLGMail.CFG file");

                        RECONFIGFLAG=FALSE;

                        if(__RECONFIG() != LS_NO_ERROR)
                        {
                           DisplayErrReq(NULL,"DLGMail.CFG configuration error.","See LOGS:DLGMailCfgErr.RPT for details.");
                           StatCommandLine("Error in DLGMail.CFG file found during reconfig - exiting");
                           Log("! Error in DLGMail.CFG file found during reconfig - exiting");
                           break;
                        }
                     }
                  }

                  IF_OBNOXIOUS StatCommandLine("Out of endless port checking loop");
               }

               StatCommandLine("Deallocating CoProcess Startup Resources");

               for(i=0;i<CLASS_TOTALS;i++)
               {
                  if(StartMsg[i])
                     FreeMem(StartMsg[i],sizeof(struct ProcMsg));
                  if(FakeList[i])
                     FreeMem(FakeList[i],sizeof(struct PhoneySegList));
                  if(CoProcReplyPort[i])
                     DeletePort(CoProcReplyPort[i]);
               }

               KillQueues();
            }

            /* throw away recent ARexx messages */

            while((RM=(void *)GetMsg(ZPort))!=0)
               ReplyMsg((struct Message *)RM);

            DeletePort(ZPort);
         
         }
         /* close timer device */
         if(!(CheckIO((struct IORequest *)tr)))
            AbortIO((struct IORequest *)tr);

         WaitIO((struct IORequest *)tr);
         CloseDevice((struct IORequest *)tr);
       }
          

       DeleteExtIO((struct IORequest *)tr);

    }

    DeletePort(timerport);
 }
      FreeSignal(RxSigBit);
   }
   DeleteFile("ENV:DLGMAILSTOP");
   StatCommandLine("All ARexx/Co-Process resources freed");
   Delay(50);
   if(FAIL==TRUE) return 0;
   return(0);
}
//-

/// MacroSubstitution
/*******************
 *
 * PRIVATE
 * MacroSubstitution
 *
 *******************/

/* this function returns -1 to indicated a shutdown
 * returns 0 to indicate a bad command
 * returns CLASS+1 to indicate what class the command was
 *
 * this function needs to 
 */

int MacroSubstitution(char *tok, char *args, char **macromemory)
{
   int rc=0;
   BPTR fp;
   char buffer[300];
   char token1[100];
   char token2[100];
   char *s;
   int i=0;
   char macbuf[30];
   char *p;

   fp=Open(MACROFILENAME,MODE_OLDFILE);
   if(fp)
   {
      while(1)
      {
         s=FGets(fp,buffer,sizeof(buffer));
         if(s)
         {
            s=buffer;
            if(*s) s[strlen(s)-1]=NULL;
            if(*s==';' || *s==NULL) continue;
            s=stpblk(s);
            s=stptok(s,token1,sizeof(token1)," \t");

            if(*macromemory)
            {
               p=*macromemory;
               i++;
               ASPrintf(NULL,macbuf,"%-23.23s ",token1);
               Upper(macbuf);
               if(i==3) strcat(macbuf,"\n");
               *macromemory=(void *)calloc(strlen(p)+strlen(macbuf)+4,1);
               if(*macromemory)
               {
                  strcpy(*macromemory,p);
                  strcat(*macromemory,macbuf);
                  free(p);
               }
               else
                  *macromemory=p;

               if(i==3) i=0;
            }

            s=stpblk(s);
            s=stptok(s,token2,sizeof(token2)," \t");
            s=stpblk(s);

            if(Stricmp(tok,token1)) continue;

            /* match! */

            strcpy(tok,token2);
            strncpy(args,s,250);
            rc=1;
            break;
         }
         else break;
      }
      Close(fp);
   }
   return(rc);
}
//-

/// CreateARexxTextReply
/*******************
 *
 * PRIVATE
 * CreateARexxTextReply
 *
 *******************/

/* this searches the FIDO:DLGMail.MAC file looking for token */
/* returns 0 if no macro found */

ULONG CreateARexxTextReply(int whichreply,char *macmem, char *spectext)
{
   ULONG val;
   struct RexxArg *RA;  /* if we pass something meaningful back to the Rexx caller */
   char *p;
   char *usemem=NULL;
   int i,j=0;
   char usebuf[30];

   char *rs=NULL;
   int size;

   usemem=(void *)calloc(25,1);
   if(usemem)
      ASPrintf(NULL,usemem,"DLGMail commands:\n\n");

   if(spectext)
   {
      rs=spectext;
   }
   else
   {
      switch(whichreply)
      {
         case 1:
         rs="OK";
         break;
   
         case 2:
         if(usemem)
         {
            for(i=0;*tokens[i].tk_command != NULL;i++)
            {
               p=usemem;
               j++;
               ASPrintf(NULL,usebuf,"%-23.23s ",tokens[i].tk_command);
               if(j==3) strcat(usebuf,"\n");
               usemem=(void *)calloc(strlen(p)+strlen(usebuf)+4,1);
               if(usemem)
               {
                  strcpy(usemem,p);
                  strcat(usemem,usebuf);
                  free(p);
               }
               else usemem=p;

               if(j==3) j=0;
            }
            if(macmem)
            {
               strcat(usemem,"\n");
               p=usemem;
               usemem=(void *)calloc(strlen(p)+strlen(macmem)+10,1);
               if(usemem)
               {
                  strcpy(usemem,p);
                  strcat(usemem,macmem);
                  free(p);
               }
               else usemem=p;
            }
            rs=usemem;
         }
         else
         {
            rs="No usage available from DLGMail";
         }
         break; /* breaks the default switch */
      }
   }

   size=strlen(rs);
   size=((size/4)*4)+4+8;

   RA=(void *)AllocMem(size,MEMF_PUBLIC|MEMF_CLEAR);

   if(RA)
   {
      RA->ra_Size  =size;
      RA->ra_Length=strlen(rs)+1;
      RA->ra_Flags =NSF_STRING;
      if(*rs)
      {
         strcpy(RA->ra_Buff,rs);
      }
      if(usemem) free(usemem);
      val=(int)RA;
      val += 8;
      return val;
   }

   if(usemem) free(usemem);
   return NULL;
}
//-

/// ParseARexxCommand
/*******************
 *
 * PRIVATE
 * ParseARexxCommand
 *
 *******************/

int ParseARexxCommand(struct RexxMsg *rm,BOOL INHIBIT)
{
   char logbuf[250];

   char token[100];
   char *s;
   char args[255];
   int i;
   char *macromemory=NULL;
   char usestring[200];

   if(INHIBIT)
   {
      rm->rm_Result1=0;
      rm->rm_Result2=CreateARexxTextReply(0,NULL,"DLGMail: Inhibited, QUIT was received");
      return(0);
   }

   macromemory=(void *)calloc(40,1);
   if(macromemory)
      ASPrintf(NULL,macromemory,"\nMacros commands available:\n\n");

   *args=NULL;

   s=rm->rm_Args[0];
   s=stpblk(s);
   ConCommandLine(s);
   s=stptok(s,token,sizeof(token)," \t");
   if(*token)
   {
      if(s && *s) s=stpblk(s);   /*ready to store arguments to token*/

      i=MacroSubstitution(token,args,&macromemory);
      if(!i)
         strcpy(args,s);
      s=args;
      while(*s != NULL)
      {
         if(*s==';')
         {
            *s=NULL;
            break;
         }
         s++;
      }
      
      /* compare token with our list of known keywords */

      for(i=0;*tokens[i].tk_command != NULL;i++)
      {
         if(0==Stricmp(token,tokens[i].tk_command))
         {
            if(tokens[i].tk_class <0)  /* HANDLE THIS SHIT IMMEDIATELY */
            {
               ASPrintf(NULL,logbuf,"%s %s",tokens[i].tk_command,args);
               InstCommandLine(logbuf);

               /* handle this shit immediately */

               switch(tokens[i].tk_toknum)
               {
                  case 1:  /* TD */
                  if(0==Strnicmp(args,"ON",2))
                  {
                     __TD(1,1);
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"TrapDoor Enabled");
                     StatCommandLine("TrapDoor enabled");
                     break;
                  }
                  if(0==Strnicmp(args,"OFF",3))
                  {
                     __TD(0,1);
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"TrapDoor Disabled");
                     StatCommandLine("TrapDoor disabled");
                     break;
                  }
                  StatCommandLine("Unknown TRAPDUMP argument [%s]",args);
                  Usage(usestring,1);
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,usestring);
                  break;

                  case 2: /* RECONFIG */
                  RECONFIGFLAG=TRUE;
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,"Reconfig Request Queued");
                  StatCommandLine("DLGMail RECONFIG queued");
                  break;

                  case 3: /* WHEREIS */
                  StatCommandLine("Global configuration address returned");
                  rm->rm_Result1= ~(ULONG)GCFG;
                  ASPrintf(NULL,logbuf,"%u",~(ULONG)GCFG);
                  /*rm->rm_Result2=CreateARexxTextReply(0,NULL,logbuf);*/
                  break;

                  case 4: /* PUBSHELL */
                  if(OS_VERSION<37 || !_SCNPTR)
                  {
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"No public screen available");
                     StatCommandLine("Request for public shell failed");
                  }
                  else
                  {
                     Spawn(Input(),sout,"NEWSHELL \"CON:300/15/300/75/NewShell/INACTIVE/CLOSE/SCREEN %s\"",PUBSCNNAME);
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"New shell on public window opened");
                     StatCommandLine("Request for public shell satisfied");
                  }
                  break;



                  case 5: /* STATUS */
                  if(0==Strnicmp(args,"TRAPDOOR",8))
                  {
                     if(IsTrapdoorThere(_BBSPORT))
                     {
                        rm->rm_Result1=0;
                        rm->rm_Result2=CreateARexxTextReply(0,NULL,"TRAPDOOR LOADED");
                     }
                     else
                     {
                        rm->rm_Result1=5;
                        rm->rm_Result2=CreateARexxTextReply(0,NULL,"TRAPDOOR NOT PRESENT");
                     }
                     break;
                  }
                  if(0==Strnicmp(args,"CP",2))
                  {
                     char *x;
                     int cc;
                     x=args;
                     x++;
                     x++;
                     cc=*x;
                     cc-=0x30;  /* make cc range from 0 to 3 */


                     if(cc>3 || cc<0)
                     {
                        StatCommandLine("Unknown STATUS CPx process identifier [%d]",cc);
                        Usage(usestring,0);
                        rm->rm_Result1=0;
                        rm->rm_Result2=CreateARexxTextReply(0,NULL,usestring);
                        break;
                     }
                     if(CPxBusy[cc]==0)
                     {
                        rm->rm_Result1=0;
                        rm->rm_Result2=CreateARexxTextReply(0,NULL,"IDLE");
                     }
                     else
                     {
                        rm->rm_Result1=5;
                        rm->rm_Result2=CreateARexxTextReply(0,NULL,"BUSY");
                     }
                     break;
                  }
                  StatCommandLine("Unknown STATUS argument [%s]",args);
                  Usage(usestring,5);
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,usestring);
                  break;


                  case 6:  /* HELP */
                  rm->rm_Result2=__HELP(args);
                  rm->rm_Result1=0;
                  StatCommandLine("HELP sent");
                  break;

                  case 7:  /* PROCESSING */
                  if(0==Strnicmp(args,"SUSPEND",7))
                  {
                     PROCESSINGSUSPENDED=1;
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"CP3 Processing Suspended");
                     StatCommandLine("CP3 Processing Suspended");
                     break;
                  }
                  if(0==Strnicmp(args,"RESUME",6))
                  {
                     PROCESSINGSUSPENDED=0;
                     rm->rm_Result1=0;
                     rm->rm_Result2=CreateARexxTextReply(0,NULL,"CP3 Processing Resumed");
                     StatCommandLine("CP3 Processing Resumed");
                     break;
                  }
                  StatCommandLine("Unknown PROCESSING argument [%s]",args);
                  Usage(usestring,1);
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,usestring);
                  break;

                  case 255: /* QUIT */
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,"DLGMail Quit Received - Will Exit When Able");
                  StatCommandLine("DLGMail QUIT queued");
                  break;


                  case 254: /* STOP */
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,"DLGMail Stop Received - Will Exit ASAP");
                  StatCommandLine("DLGMail STOP queued");
                  DLGMAILSTOP=TRUE;
                  SetEnv("DLGMAILSTOP","STOP PROCESSING");
                  break;
               }

               InstCommandLine("");
            }
            else  /* QUEUE THIS SHIT UP */
            {
               if(AddToQueue( tokens[i].tk_command,args,tokens[i].tk_toknum,tokens[i].tk_class,tokens[i].tk_flags,tokens[i].tk_pri  ))
               {
                  StatCommandLine("Command queued");
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(1,NULL,NULL);
               }
               else
               {
                  StatCommandLine("Command lost");
                  rm->rm_Result1=0;
                  rm->rm_Result2=CreateARexxTextReply(0,NULL,"DLGMail: Couldn't queue command");
               }
            }
            if(macromemory)
               free(macromemory);
            return(tokens[i].tk_class+1);
         }
      }
      StatCommandLine("Unknown ARexx command [%s]",strupr(token));
      rm->rm_Result1=0;
      rm->rm_Result2=CreateARexxTextReply(2,macromemory,NULL);
      if(macromemory)
         free(macromemory);
   }
   return(0);
}
//-

/// __HELP
ULONG __HELP(char *args)
{
   char helpbuf[500];
   char *s;
   char token[50];
   int i;

   if(*args==NULL)
   {
      ASPrintf(NULL,helpbuf,"Syntax to receive help: DMC HELP <command>");
      return(CreateARexxTextReply(0,NULL,helpbuf));
   }
   s=args;
   s=stptok(s,token,sizeof(token)," \t");

   Upper(token);

   for(i=0;*tokens[i].tk_command != NULL;i++)
   {
      if(0==Stricmp(tokens[i].tk_command,token))
      {
         ASPrintf(NULL,helpbuf,"DLGMail help for command [%s]:\n\nTemplate: %s ",token,token);
         strcat(helpbuf,tokens[i].tk_template);
         strcat(helpbuf,"\n    Info: ");
         strcat(helpbuf,tokens[i].tk_help);
         return(CreateARexxTextReply(0,NULL,helpbuf));
      }
   }
   ASPrintf(NULL,helpbuf,"Unknown built-in command [%s] - can't show help",token);
   return(CreateARexxTextReply(0,NULL,helpbuf));
}
//-

/// Usage
int Usage(char *outstring, int toknum)
{
   int i;

   for(i=0;*tokens[i].tk_command != NULL;i++)
   {
      if(tokens[i].tk_toknum==toknum)
      {
         ASPrintf(NULL,outstring,"DLGMail Command Usage: [7m %s ",tokens[i].tk_command);
         if(tokens[i].tk_template)
            strcat(outstring,tokens[i].tk_template);
         else
            strcat(outstring,"  (No argument accepted)");
         strcat(outstring," [27m\n");
         break;
      }
   }
   return 1;
}
//-

/// CP_CommandLine
/*******************
 *
 * For printing to status window
 * CP_CommandLine
 *
 *******************/

int __stdargs CP_CommandLine(int class, char *string, ...)
{
   struct RexxMsg *RexxMsg;
   struct RexxArg *ra;

   struct MsgPort *RexxPort;
   struct MsgPort *ReplyPort;
   int retval = 10;
   char buf[256];
   char duf[256];

   XASPrintf(NULL, duf, string, (char *)(&string+1));
    ASPrintf(NULL, buf,"%d %s",class, duf);

   if (ReplyPort=CreatePort(0,0))
   {
      if(RexxMsg=AllocMem(sizeof(struct RexxMsg), MEMF_CLEAR|MEMF_PUBLIC))
      {
         RexxMsg->rm_Node.mn_ReplyPort=ReplyPort;
         RexxMsg->rm_Node.mn_Length=sizeof(struct RexxMsg);
         RexxMsg->rm_Action=RXCOMM | RXFF_RESULT | RXFF_STRING ;
         RexxMsg->rm_Args[0]=buf;

         Forbid();
         RexxPort=FindPort("DLGM_ARexx");
         if (RexxPort)
            PutMsg(RexxPort, (struct Message *) RexxMsg);
         Permit();

         if(RexxPort)
         {
            WaitPort(ReplyPort);
            GetMsg(ReplyPort);
            retval=RexxMsg->rm_Result1;

            if(RexxMsg->rm_Result2)
            {
               ra=(struct RexxArg *)((char *)RexxMsg->rm_Result2 - &( (struct RexxArg *)NULL)->ra_Buff[0]);
               FreeMem(ra, ra->ra_Size);
            }
         }
         FreeMem(RexxMsg,sizeof(struct RexxMsg));
      }
      DeletePort(ReplyPort);
   }
   return retval;
}
//-

/// __RECONFIG
/* PUBLIC
 * 
 * 
 * 
 * This function reloads the configuration
 * 
 * 
 * NEEDS WAY TO ENABLE ZMH IF ZMH IS ACTIVE
 */

int __RECONFIG(void)
{
   return(LoadConfig());
}
//-

/// __TD
/* PUBLIC
 * 
 * 
 * 
 * This funtion enables/disables TrapDoor.
 * 
 * 
 * NEEDS WAY TO ENABLE ZMH IF ZMH IS ACTIVE
 */

int __TD(int c, int log)
{
   char returnstring[150];

   if(c==0 && TRAPDOOR_LOADED)  /*disable trapdoor*/
   {
      SendRexx2(_TDPORT,"STATUS D",returnstring);
         /* dummy way to make sure TrapDoor is idle */

      LockPort(_BBSPORT,"DLGM_Wanted_TrapDoor","DLGMail needed the BBS port",1,"");

      DoAScript("TrapDoor_OFF.DMB",NULLFH);

      if(log) Log(". TrapDoor disabled");
      TRAPDOOR_LOADED=0;
   }
   if(c==1 && TRAPDOOR_LOADED==0) /*enable trapdoor*/
   {
      FreePort(_BBSPORT,"DLGM_Wanted_TrapDoor");

      DoAScript("TrapDoor_ON.DMB",NULLFH);

      if(log) Log(". TrapDoor enabled");
      TRAPDOOR_LOADED=1;
   }
   
   return 1;
}
//-

