#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>

#include <exec/types.h>
#include <devices/timer.h>

#include <libraries/dos.h>
#include <libraries/dosextens.h>

#include <rexx/rxslib.h>
#include <rexx/storage.h>
#include <rexx/errors.h>

#include <dlg/cron.h>
#include <dlg/dlg.h>
#include <dlg/user.h>
#include <dlg/misc.h>

#include <link/io.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/rexxsyslib.h>
#include <proto/dlg.h>

#include <pragmas/dlg.h>

#include <private/Version.h>
#define  ObjRev "1"
const UBYTE version[]="\0$VER: TPTCron " BUILDVER "." ObjRev " ©" COPYRIGHT " by Digerati Dreams "__AMIGADATE__;

#define MAXLINE   132
#define SIZE    256

void          wakeup(void);
BOOL          ReadCronTab(void);
BOOL          getline(void);
char         *scanner(char *, char *);
BOOL          match(char *, int );
unsigned long ClosestEvent(char *);
long          GetTime(struct CronEntry *, long , UBYTE );
int           Closest(char *, UBYTE );
void          HandleEvent(void);
void          reply_rexx_command(struct RexxMsg *, long , char *);
BOOL          OpenTimer(void);
void          CloseTimer(void);
void          FreeEntries(void);
void         _CXBRK(void);
void          CleanUp(char *);
void          Usage(char *);


struct Process     *myproc;

struct timerequest *Timer      = NULL;
struct MsgPort     *Timer_Port = NULL;
ULONG               TimerSig;

struct MsgPort     *cronctl = NULL;
ULONG               CronSig;

struct Library *RexxSysBase = NULL;
struct Library *DLGBase     = NULL;

int    eof;
int    numentries = 0;

struct CronEntry *cronentries[256];
struct EventNode *headnode = NULL;

char   min    [SIZE];
char   hour   [SIZE];
char   day    [SIZE];
char   month  [SIZE];
char   wday   [SIZE];
char   command[256];

char  *tokv[] = {min, hour, day, month, wday};
char   crontabs[256] = "";
char   logfile [256] = "";
char   bkgrnd        = FALSE;

FILE  *fd;
BPTR   ifh      = NULL;
APTR   oldct    = NULL;
ULONG  ctdate   =  0;
UBYTE  checktab = FALSE;
UBYTE  rxenable = TRUE;
BPTR   sout;


void main(int argc, char **argv)

{char  *s;
 char  *iodevice = NULL;
 struct ATime tm;
 long   signals;

 sout = Output();

 if (!(DLGBase = OpenLibrary(DLGNAME, DLGVERSION)))
 {
   FPuts(sout,"TPTCron: Can't open dlg.library!!\n\n");
   exit(5);
 }

 if (!(RexxSysBase = OpenLibrary(RXSNAME, 0)))       rxenable = FALSE;

 while(--argc > 0)
      {s = *++argv;
       if (*s++=='-')
          {while(*s)
                {switch(toupper(*s))
                       {case 'T': if (!--argc) break;
                                  strncpy(crontabs, *++argv, 255);
                                  crontabs[255] = '\0';
                                  break;

                        case 'L': if (!--argc) break;
                                  strncpy(logfile, *++argv, 255);
                                  logfile[255]='\0';
                             break;

                        case 'B': if (!--argc)  Usage("I/O Device required");
                                  iodevice = *++argv;
                                  bkgrnd   =    TRUE;
                                  break;

                        case 'R': checktab=TRUE;
                                  break;

                         default: Usage("Bad Switch");
                  }

                 s++;
                }
          }
        else
          Usage("Bad Syntax");
      }

 Forbid();
 cronctl = FindPort(CRONCONTROL);
 Permit();
 if (cronctl)  CleanUp("TPTCron is already active");

 if (!(cronctl = (struct MsgPort *)CreateMsgPort()))
       CleanUp("Unable to open control port");
 cronctl->mp_Node.ln_Name = CRONCONTROL;
 AddPort(cronctl);
 CronSig = (1 << cronctl->mp_SigBit);

 if (!OpenTimer()) CleanUp("Unable to open timer device");

 if (crontabs[0])
    {if (!ReadCronTab()) CleanUp("Can't open crontab file");
     AFPrintf(NULL, sout, "\n CronTab file is [%s]\n", crontabs);
    }
  else
    {numentries = 0;
     checktab   = FALSE;
     AFPrintf(NULL, sout, "\n No CronTab file in use\n");
    }

 if (logfile[0])
    {AppendFile(logfile, "TPTCron started");
     AFPrintf(NULL, sout, " Logfile is [%s]\n",logfile);
    }
  else
    AFPrintf(NULL, sout, " No Logfile in use\n");

 if (bkgrnd)
    {if (!DeviceProc(iodevice))  CleanUp("Unable to locate I/O device");
     myproc                 = (struct Process *)FindTask(0);
     oldct                  =  myproc->pr_ConsoleTask;
     myproc->pr_ConsoleTask = (APTR)DeviceProc(iodevice);
     fclose(stderr);

     if (!(ifh = Open(iodevice, MODE_NEWFILE)))
           CleanUp("Unable to open redirection file");
    }

 AFPrintf(NULL, sout, "\n TPTCron installed successfully...\n\n");
 UnpackTime(AmigaTime(), &tm);
 if (tm.sec < 30) wakeup();


 for(;;)
    {/* sleep 'till next minute */
     Timer->tr_time.tv_secs  = (60 - tm.sec);
     Timer->tr_time.tv_micro =   0;
     SendIO((struct IORequest *)Timer);

     do {signals = Wait(TimerSig | CronSig);
         if (signals & CronSig)  HandleEvent();
        } while(!(signals & TimerSig));

     WaitIO((struct IORequest *)Timer);
     wakeup();
     UnpackTime(AmigaTime(), &tm);
    }
}


void wakeup(void)

{struct EventNode *tempnode;
 struct ATime      tm;
 ULONG             cur_time;

 char doit[256];
 long entry;

 chkabort();

 if (checktab)
    {GetFileDate(crontabs, (long *)&cur_time);

     if (cur_time > ctdate)
        {AFPrintf(NULL, sout, "\n\n Rereading [%s]\n\n",crontabs);
         FreeEntries();
         ReadCronTab();
        }
    }

 cur_time = AmigaTime();
 UnpackTime(cur_time, &tm);

 while(headnode)
       if (headnode->time <= cur_time-60)
          {tempnode = headnode;
           headnode = headnode->nextevent;
           free(tempnode);
          }
        else
           break;

 tempnode = headnode;
 while(tempnode)
       if (tempnode->time <= cur_time)
          {if (logfile[0])  AppendFile(logfile, headnode->command);
           strcpy(doit, "RUN ");
           strncpy(&doit[4], tempnode->command, 251);
           doit[255] = 0;
           Execute(doit,ifh,ifh);
           tempnode = tempnode->nextevent;
          }
        else
           break;

 for(entry=0; entry<numentries; entry++)
    {if (match(cronentries[entry]->min,   tm.min)    &&
         match(cronentries[entry]->hour,  tm.hour)   &&
         match(cronentries[entry]->day,   tm.mday)   &&
         match(cronentries[entry]->month, tm.mon+1)  &&
         match(cronentries[entry]->wday,  tm.wday))
        {if (logfile[0])  AppendFile(logfile, cronentries[entry]->command);
         strcpy (doit, "RUN ");
         strncpy(&doit[4],  cronentries[entry]->command, 251);
         doit[255] = 0;
         Execute(doit,ifh,ifh);
        }
    }
}


BOOL ReadCronTab(void)

{long szmin;
 long szhour;
 long szday;
 long szmonth;
 long szwday;

 if (!(fd = fopen(crontabs,"r")))  return(FALSE);

 numentries =   0;
 eof        = FALSE;

 while(!eof  &&  numentries<256)
      {if (getline())
          {szmin   = strlen(min);
           szhour  = strlen(hour);
           szday   = strlen(day);
           szmonth = strlen(month);
           szwday  = strlen(wday);

           cronentries[numentries] = malloc(sizeof(struct CronEntry) + szmin + szhour + szday + szmonth + szwday + strlen(command) + 6);

           cronentries[numentries]->min     = (char *)cronentries[numentries] + sizeof(struct CronEntry);
           cronentries[numentries]->hour    =  cronentries[numentries]->min   + szmin   + 1;
           cronentries[numentries]->day     =  cronentries[numentries]->hour  + szhour  + 1;
           cronentries[numentries]->month   =  cronentries[numentries]->day   + szday   + 1;
           cronentries[numentries]->wday    =  cronentries[numentries]->month + szmonth + 1;
           cronentries[numentries]->command =  cronentries[numentries]->wday  + szwday  + 1;

           strcpy(cronentries[numentries]->min,     min);
           strcpy(cronentries[numentries]->hour,    hour);
           strcpy(cronentries[numentries]->day,     day);
           strcpy(cronentries[numentries]->month,   month);
           strcpy(cronentries[numentries]->wday,    wday);
           strcpy(cronentries[numentries]->command, command);

           numentries++;
          }
      }

 fclose(fd);

 GetFileDate(crontabs, (long *)&ctdate);
}


/*
 * A line consists of six fields.  The first five are:
 *
 *    minute:         0-59
 *    hour:           0-23
 *    day:            1-31
 *    month:          1-12
 *    weekday:        0-6 (Sunday = 0)
 *
 * The fields are seperated by spaces or tabs, with the
 * first field left justified (no leading spaces or tabs).
 * See below for optional field syntax.
 *
 * The last field is the command field.  This command will
 * be executed by the CLI just as if typed from a console.
 */
BOOL getline(void)

{char *p;
 int   i;
 char  buffer[MAXLINE];

 if (!fgets(buffer, sizeof(buffer), fd))
    {eof = TRUE;
     return(FALSE);
    }

 if (buffer[0]=='#')  return(FALSE);

 i = strlen(buffer) - 1;
 if (buffer[i] == 13  ||  buffer[i] == 10)
     buffer[i] = 0;

 for(p = buffer, i = 0; i < 5; i++)
     if (!(p = scanner(tokv[i], p)))  return(FALSE);

 strncpy(command, p, 255); /* scoop the command */
 command[255] = 0;

 return(TRUE);
}


char *scanner(char *token, char *offset)

{long count = 0;

 while(*offset != ' '  &&  *offset != '\t'  &&  *offset)
        if (count++ < SIZE-1)  *token++ = *offset++;

 /*
  *      Check for possible error condition
  */
 if (!*offset)  return(NULL);

 *token = 0;

 while(*offset == ' '  ||  *offset == '\t')
        offset++;

 return(offset);
}


/*
 * This routine will match the left string with the right number.
 *
 * The string can contain the following syntax:
 *
 * *     This will return TRUE for any number
 * x,y [,z, ...]  This will return TRUE for any number given.
 * x-y      This will return TRUE for any number within
 *       the range of x thru y.
 */

BOOL match(char *left, int right)

{int   n;
 char  c;

 if (!strcmp(left, "*"))  return(TRUE);

 n = 0;
 while((c = *left++) && (c >= '0') && (c <= '9'))
        n  =  (n * 10) + c - '0';

 switch(c)
       {case 0:     return((BOOL)(right==n));

        case ',':   if (right == n)
                        return(TRUE);

                    do{n = 0;
                       while ((c = *left++) && (c >= '0') && (c <= '9'))
                               n = (n * 10) + c - '0';
                       if (right == n)  return(TRUE);
                      } while (c == ',');

                    return(FALSE);

        case '-':   if (right < n) return(FALSE);

                    n = 0;
                    while((c = *left++) && (c >= '0') && (c <= '9'))
                           n = (n * 10) + c - '0';

                    return((BOOL)(right<=n));
       }
}


unsigned long ClosestEvent(char *command)

{unsigned long cur_time;
 unsigned long new_time;
 unsigned long best_time;
 int           entry;

 best_time = -1;

 for(entry=0;entry<numentries;entry++)
    {if (DLGPatternMatch(command,cronentries[entry]->command))
        {cur_time = AmigaTime();
         new_time = GetTime(cronentries[entry],cur_time,0);

         if (new_time != -1)
            {if (new_time < best_time) best_time = new_time;
            }
          else
            {cur_time += 86400;
             new_time  = GetTime(cronentries[entry],cur_time,1);
             if (new_time < best_time)  best_time = new_time;
            }
        }
    }


 return(best_time);
}


long GetTime(struct CronEntry *entry,long cur_time,UBYTE flag)

{struct ATime tm;
 long         minutes;
 long         hours;

 UnpackTime(cur_time, &tm);

 if (match(entry->day,   tm.mday)   &&
     match(entry->month, tm.mon+1)  &&
     match(entry->wday,  tm.wday))
    {if (flag)
        {hours   = Closest(entry->hour,0);
         minutes = Closest(entry->min,0);

         return(cur_time + (((long)(minutes-tm.min) * 60)) + (((long)(hours-tm.hour) * 3600)));
        }

     hours = Closest(entry->hour,tm.hour);
     if (hours < tm.hour)  return(-1);

     if (hours > tm.hour)
        {minutes = Closest(entry->min,0);
         return(cur_time + (((long)(minutes-tm.min) * 60)) + (((long)(hours-tm.hour) * 3600)));
        }

     minutes = Closest(entry->min,tm.min);
     if (minutes > tm.min)
         return(cur_time + (((long)(minutes-tm.min) * 60)));

     hours = Closest(entry->hour,tm.hour+1);
     if (hours < (tm.hour+1))  return(-1);

     minutes=Closest(entry->min,0);
     return(cur_time + (((long)(minutes-tm.min) * 60)) + (((long)(hours-tm.hour) * 3600)));
    }

 return(-1);
}


int Closest(char *str, UBYTE num)

{int  n, closest;
 char c;

 if (*str=='*')  return(num);

  n = 0;
  while((c = *str++) && (c >= '0') && (c <= '9'))
         n = (n * 10) + c - '0';


 switch(c)
       {case '\0':  return(n);

        case ',':  if (num == n)  return(n);
                   closest = n;

                   do{n = 0;
                      while((c = *str++) && (c >= '0') && (c <= '9'))
                             n = (n * 10) + c - '0';

                      if (num == n)  return(n);
                      if (((n < num) && (n < closest)) || ((n > num) && ((closest < num) || (n < closest))))
                            closest = n;
                     } while (c == ',');

                   return(closest);

        case '-':  if (num < n) return(n);
                   closest = n;

                   n = 0;
                   while ((c = *str++) && (c >= '0') && (c <= '9'))
                           n = (n * 10) + c - '0';

                   if (n < num)
                       return(closest);
                   return(num);

         default:  return(0);
       }

 return(0);
}


void HandleEvent(void)

{struct EventNode   *newnode;
 struct EventNode  **nodeptr;
 struct Message     *msg;
 struct CronMessage *newmessage;
 struct RexxMsg     *rxmsg     = NULL;
 char               *resultstr = NULL;
 long                primary   = 0;

 long   curr_time;
 long   etime;
 char   type;

 char   doit   [256];
 char   action [20];
 char   buf    [80];
 char   command[256];

 while(msg = (struct Message *)GetMsg(cronctl))
      {if (msg->mn_Length == sizeof(struct CronMessage))
          {newmessage = (struct CronMessage *)msg;
           type       =  newmessage->type;
           etime      =  newmessage->minutes;

           command[0] = 0;
           if (newmessage->command)
              {strncpy(command, newmessage->command, 255);
               command[255] = 0;
              }
          }
        else
          {long  numargs;

           rxmsg   = (struct RexxMsg *)msg;
           numargs =  sscanf(rxmsg->rm_Args[0],"%10s %ld %256[^\000]",action,&etime,command);

           if (!numargs)
              {reply_rexx_command(rxmsg, BADSYNTAX, "No command specified");
               continue;
              }

           if (numargs < 3)
               numargs = sscanf(rxmsg->rm_Args[0],"%10s %256[^\000]",action,command);

           if (!Strnicmp(action, "ADDEVENT",  8))  type = ADDEVENT;
      else if (!Strnicmp(action, "CHANGEDIR", 9))  type = CHANGEDIR;
      else if (!Strnicmp(action, "DELEVENT",  8))  type = DELEVENT;
      else if (!Strnicmp(action, "WHENEVENT", 9))  type = WHENEVENT;
      else if (!Strnicmp(action, "READFILE",  8))  type = READFILE;
      else if (!Strnicmp(action, "CRONEXIT",  8))  type = CRONEXIT;
      else reply_rexx_command(rxmsg, BADSYNTAX, "Invalid command");

           if (((type == ADDEVENT) && (numargs<3))  ||
              (((type == DELEVENT)                  ||
                (type == WHENEVENT)                 ||
                (type == READFILE)                  ||
                (type==CHANGEDIR)) && numargs<2))
                 reply_rexx_command(rxmsg, BADSYNTAX, "Not enough arguments");
          }

       switch(type)
             {case ADDEVENT: if (etime < 1)
                                {primary = CNOERR;
                                 if (logfile[0])  AppendFile(logfile, command);
                                 strcpy (doit, "RUN ");
                                 strncpy(&doit[4], command, 251);
                                 doit[255] = 0;
                                 Execute(doit, ifh, ifh);
                                }
                              else
                                {newnode = malloc(sizeof(struct EventNode)+strlen(command)+1);
                                 if (newnode==NULL)
                                    primary = OUTOFMEM;
                                   else
                                    {primary          = CNOERR;
                                     newnode->time    = ((AmigaTime()/60)+etime)*60;
                                     newnode->command = (char *)newnode + sizeof(struct EventNode);
                                     strcpy(newnode->command, command);

                                     nodeptr = &headnode;
                                     while(*nodeptr && ((*nodeptr)->time<newnode->time))
                                            nodeptr=&((*nodeptr)->nextevent);

                                     newnode->nextevent = *nodeptr;
                                    *nodeptr            =  newnode;
                                    }
                                }
                             break;

              case DELEVENT: curr_time =  AmigaTime();
                             primary   =  0;

                             nodeptr = &headnode;
                             while(*nodeptr)
                                  {if (DLGPatternMatch(command, (*nodeptr)->command)&&((*nodeptr)->time>curr_time))
                                      {newnode =  *nodeptr;
                                      *nodeptr = (*nodeptr)->nextevent;
                                       free(newnode);
                                       primary++;
                                      }
                                    else
                                       nodeptr = &((*nodeptr)->nextevent);
                                  }

                             if (rxmsg)
                                {ASPrintf(NULL, buf, "%ld events deleted", primary);
                                 resultstr = buf;
                                }
                             break;

             case WHENEVENT: primary   =        0;
                             resultstr = "Event not found";
                             curr_time =  AmigaTime();

                             nodeptr   = &headnode;
                             while(*nodeptr)
                                  {if (DLGPatternMatch(command, (*nodeptr)->command)  &&  (*nodeptr)->time > curr_time)
                                      {primary = (*nodeptr)->time;
                                       break;
                                      }

                                   nodeptr = &((*nodeptr)->nextevent);
                                  }

                             {unsigned long closest_perm;

                              closest_perm = ClosestEvent(command);
                              if ((primary == 0 && closest_perm < -1)  ||  closest_perm < primary)
                                    primary = closest_perm;
                             }

                             if (rxmsg)
                                {resultstr = "Event not found";
                                 if (primary)
                                    {SMDate(primary, buf);
                                     resultstr = buf;
                                    }
                                }
                             break;

            case LISTEVENTS: curr_time = AmigaTime();
                             newnode   = headnode;

                             if (newnode)
                                {primary = CNOERR;
                                 AFPrintf(NULL, newmessage->ofh, "\n TPTCron: List of pending events:");
                                 AFPrintf(NULL, newmessage->ofh, "\n ================================\n\n");

                                 while(newnode)
                                      {if (newnode->time <= curr_time)
                                           AFPrintf(NULL, newmessage->ofh,"*");
                                         else
                                           AFPrintf(NULL, newmessage->ofh," ");

                                       SMDate(newnode->time, buf);
                                       AFPrintf(NULL, newmessage->ofh, "Time: %.22s - ",  buf);
                                       AFPrintf(NULL, newmessage->ofh, "Command: <%s>\n", newnode->command);
                                       newnode = newnode->nextevent;
                                      }

                                 Write(newmessage->ofh, "\n\n", 2);
                                }
                              else
                                 primary = NOEVENTS;
                             break;

               case TABLIST: if (numentries)
                                {long    entry;

                                 primary = CNOERR;
                                 AFPrintf(NULL, newmessage->ofh, "\n TPTCron: List of events read from [%s]:\n\n", crontabs);
                                 Write(newmessage->ofh, " Minute    Hour      Day       Month     WeekDay   Command\n", 59);
                                 Write(newmessage->ofh, " ============================================================================\n", 78);

                                 for(entry = 0; entry < numentries; entry++)
                                    {AFPrintf(NULL, newmessage->ofh, " %-8.8s  %-8.8s  %-8.8s  %-8.8s  %-8.8s  %-.26s\n", cronentries[entry]->min,
                                                                                                                          cronentries[entry]->hour,
                                                                                                                          cronentries[entry]->day,
                                                                                                                          cronentries[entry]->month,
                                                                                                                          cronentries[entry]->wday,
                                                                                                                          cronentries[entry]->command);
                                    }

                                 Write(newmessage->ofh,"\n\n",2);
                                }
                              else
                                 primary = NOEVENTS;
                             break;

              case READFILE: if (command[0])
                                {strncpy(crontabs, command, 255);
                                 crontabs[255] = 0;
                                }

                             FreeEntries();
                             if (ReadCronTab() == -1)
                                 primary = TABNOTFOUND;
                               else
                                 primary = CNOERR;
                             break;

             case CHANGEDIR: if (CD(command))
                                 primary   =  CNOERR;
                               else
                                {primary   =  DIRNOTFOUND;
                                 resultstr = "Unable to change to specified directory";
                                }
                             break;

              case CRONEXIT: if (rxmsg)
                                 reply_rexx_command(rxmsg, 0, "TPTCron shut down");
                               else
                                 ReplyMsg((struct Message *)newmessage);

                             AbortIO((struct IORequest *)Timer);
                             WaitIO((struct IORequest *)Timer);
                             Wait(1 << Timer_Port->mp_SigBit);

                             AFPrintf(NULL, sout, "\n TPTCron exiting...\n\n");
                             if (logfile[0])  AppendFile(logfile, "Got exit message - shutting down");
                             CleanUp(NULL);
                             break;

                    default: primary   =  BADSYNTAX;
                             resultstr = "Invalid command";
                             break;
             }

       if (rxmsg)
          {if (!resultstr && (type!=WHENEVENT) && (type!=DELEVENT))
              {if (primary == CNOERR)       resultstr="Command successful";
          else if (primary == OUTOFMEM)     resultstr="Unable to allocate memory";
          else if (primary == TABNOTFOUND)  resultstr="Unable to find crontab file";
          else resultstr = "Error";
              }

           if ((type!=WHENEVENT) && (type!=DELEVENT))
                reply_rexx_command(rxmsg,(primary)?RC_WARN:RC_OK,resultstr);
              else
                reply_rexx_command(rxmsg,primary,resultstr);
          }
        else
          {if (type==WHENEVENT)
              {newmessage->minutes = primary;
               newmessage->type    = CNOERR;
              }
            else
               newmessage->type    = primary;

           ReplyMsg(msg);
          }
      }
}


void reply_rexx_command(struct RexxMsg *rexxmessage,long primary,char *result)

{long secondary=NULL;

 /* set an error code */
 if (rexxmessage->rm_Action & 1<<RXFB_RESULT)
    {secondary = result ? (long)CreateArgstring(result,strlen(result)):NULL;
     primary   =   0;
    }

 rexxmessage->rm_Result1 = primary;
 rexxmessage->rm_Result2 = secondary;

 ReplyMsg((struct Message *)rexxmessage);
}

BOOL OpenTimer(void)

{if (Timer_Port = CreateMsgPort())
    {if ((Timer = (struct timerequest *)CreateIORequest(Timer_Port, sizeof(struct timerequest))))
        {if (!(OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)Timer, 0)))
            {Timer->tr_node.io_Command = TR_ADDREQUEST;
             Timer->tr_node.io_Flags   = NULL;
             Timer->tr_node.io_Error   = NULL;

             TimerSig = (1 << Timer_Port->mp_SigBit);
             return(TRUE);
            }
        }
     }

 Timer->tr_node.io_Device = NULL;
 return(FALSE);
}


void CloseTimer(void)

{if (Timer)
    {if (Timer->tr_node.io_Device)
         CloseDevice((struct IORequest *)Timer);

     DeleteIORequest((struct IORequest *)Timer);
    }

 if (Timer_Port)  DeleteMsgPort(Timer_Port);
}


void FreeEntries(void)

{int entry;

 for(entry = 0; entry < numentries; entry++)
     free(cronentries[entry]);
}


void _CXBRK(void)

{Write(sout, "\nTPTCron Aborted...\n\n", 21);
 if (logfile[0])  AppendFile(logfile, "Caught ^C - shutting down");

 CleanUp(NULL);
}


void CleanUp(char *s)

{struct EventNode *tempnode;

 FreeEntries();

 while(headnode)
      {tempnode = headnode;

       headnode = headnode->nextevent;
       free(tempnode);
      }

 if (ifh)   Close(ifh);
 if (oldct) myproc->pr_ConsoleTask = oldct;

 if (cronctl)
    {RemPort(cronctl);
     DeleteMsgPort(cronctl);
    }
 CloseTimer();

 if (RexxSysBase)  CloseLibrary((struct Library *)RexxSysBase);
 CloseLibrary((struct Library *)DLGBase);

 if (s)
    {Write(sout, "\n TPTCron Error: ", 17);
     Write(sout, s, strlen(s));
     Write(sout, "\n\n", 2);
    }

 exit(s?5:0);
}


void Usage(char *str)

{Write(sout, "\n ", 2);
 Write(sout, str, strlen(str));
 Write(sout, "\n\n", 2);
 Write(sout, " Usage:  TPTCron [-t <crontab> -l <logfile> -b <IOdevice> -r]\n\n", 63);

 CleanUp(NULL);
}
