/*
 *      amicron.c
 *
 *      Public Domain (p) No Rights Reserved
 *
 *      This program operates as a daemon, waking up every minute
 *      to execute the CRONTAB table.  Logging errors to CRONERR.
 *
 *      Put in startup-sequence as:
 *              run newcli con:0/140/160/50/CronTask s:startcron
 *  The startcron file contains one line:
 *              amicron
 *
 *      Some notes are included below concerning the cron table
 *      file format.  In this version the cron table file is left
 *      on disk and not moved to core, and  the command character
 *      to signify a newline '%' is not used.
 *
 *      Some cron table entry examples:
 *
 *      Print the date in the crontask window every minute:
 *              * * * * * date
 *
 *      Print the date in the crontask window on the hour, every hour:
 *              0 * * * * date
 *
 *      Run uupc at 4:30 am every day except Sat and Sun:
 *              30 4 * * 1-5 uupc -siscuva
 *
 *      Backup the files every other day at 7:30 pm:
 *              30 19 * * 1,3,5 sdbackup -s LAST dh0: incbkup_1:
 *
 */

/*
 * Public Domain (p) by S. R. Sampson
 * Version 2.3, October 1987
 * Amiga port by Rick Schaeffer October 1987
 *
 * Rick Schaeffer          UUCP:  seismo!uunet!iscuva!ricks!ricks
 * E. 13611 26th Ave.      Phone: (509)928-3533
 * Spokane, WA  99216
 */

#include <stdio.h>
#include <time.h>
#include <errno.h>

#define TRUE 1
#define FALSE 0

#define CRONTAB "UUCP:lib/crontab"
#define CRONERR "UUCP:spool/cronerr"

#define MAXLINE 132
#define SIZE    64

extern  int     errno;

int     eof;
char    min[SIZE], hour[SIZE], day[SIZE],
        month[SIZE], wday[SIZE], command[SIZE];
char    *tokv[] = { min, hour, day, month, wday };
FILE    *fd, *err;


/*
 *      This is the basics, ready for bells and whistles
 *
 */

void main()
{
        void wakeup();

        /*
         *      fopen() will create the file if it does not exist.
         *      Any other errors will be output to stderr at startup.
         */

        if ((err = fopen(CRONERR, "a")) == (FILE *)NULL)  {
                perror("cron");
                exit(1);
                }

        for (;;)  {
                wakeup();
                Delay(50*59);   /* sleep for one minute */
                }
}

void wakeup()
{
        register struct tm *tm;
        struct tm *localtime();
        char cmd[128];
        char *ctime();
        long cur_time;

        time(&cur_time);                /* get the current time */
        tm = localtime(&cur_time);      /* break it down */

        fd = fopen(CRONTAB, "r");

        eof = FALSE;

        while (!eof)  {
                if (getline() && match(min,tm->tm_min) &&
                   match(hour,tm->tm_hour) && match(day,tm->tm_mday) &&
                   match(month,tm->tm_mon) && match(wday,tm->tm_wday))  {

                        sprintf(cmd,"%s %s","Run",command);
                        system(cmd);

                        }
                }
        fclose(fd);
}


/*
 *      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.
 */

int getline()
{
        register char *p;
        register int   i;
        char    buffer[MAXLINE], *scanner(), *fgets();

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

        for (p = buffer, i = 0; i < 5; i++)  {
                if ((p = scanner(tokv[i], p)) == (char *)NULL)
                        return(FALSE);
                }

        strcpy(command, p);     /* scoop the command */
        return(TRUE);
}


char *scanner(token, offset)
register char   *token;         /* target buffer to receive scanned token */
register char   *offset;        /* place holder into source buffer */
{
        while ((*offset != ' ') && (*offset != '\t') && *offset)
                *token++ = *offset++;

        /*
         *      Check for possible error condition
         */

        if (!*offset)
                return ((char *)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.
 */

int match(left, right)
register char   *left;
register int    right;
{
        register int    n;
        register char   c;

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

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

        switch (c)  {
                case '\0':
                        return (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(right <= n);
                }
}
