[comp.sys.amiga] Cron task for the Amiga

ricks@iscuva.ISCS.COM (Rick Schaeffer) (11/07/87)

The following posting contains the source and documentation for a cron
task that runs on an Amiga.  It can be compiled only with Aztec C 3.4
although mods to make it work with other compilers shouldn't be too 
tough.  I hope no one minds my posting it here rather than
comp.sources.amiga (it's pretty small).

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Amicron.doc
#	Makefile
#	Amicron.c
# This archive created: Sat Nov  7 12:38:57 1987
cat << \SHAR_EOF > Amicron.doc
                    AmiCron - Cron task for the Amiga

This is a rather crude but effective version of the Unix (tm) 
program known as "cron" modified to run on an Amiga.  For those 
who aren't Unix knowledgeable, cron is a background task which 
uses a disk-resident table to automatically run certain tasks on a 
regular basis.  The cron table's format is very simple...it 
contains entries in the form of lines where each line has 6 fields 
and each field is separated by "white space" (either tabs or 
spaces) from it's neighbor. The fields are as follows:

   1.  Minute       (0-59)
   2.  Hour         (0-23)
   3.  Day          (1-31)
   4.  Month        (1-12)
   5.  Day of week  (0-6 where 0=Sunday and 6=Saturday)
   6.  Command      This is the command to be run at the appointed 
                    time.  It will be run just as if typed into 
                    the CLI.

Each of the first 5 fields are numbers.  However, it is 
permissable to use an asterisk (*) in a field to mean ALL possible 
numbers for that field,  and you can specify a series of numbers 
separated by commas (,) and ranges separated by dashes (-).  For 
instance, in the Day field you could specify "1,5,10" to mean the 
first, fifth, and tenth days of the month; or you could specify 
"1-15" to mean the first through the fifteenth of the month.  Here 
are some examples of Amicron table entries:

  	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
  
  	Incrementally backup the files every other day at 7:30 pm:
  		30 19 * * 1,3,5 sdbackup -l -s LAST dh0: incbkup_1:

    Backup the files on the 1st and 15th of each month at 1:00 am:
        00 01 1,15 * * sdbackup -l dh0: SemiBkup_1:

The Amicron table file is located using the following path:
    sys:usr/spool/cron/crontab
and errors, if any are logged into the following path:
    sys:usr/spool/cron/cronerr

Crontab entries *MUST* be left justified starting in column 1 and 
each entry must contain 6 fields, each separated by spaces or 
tabs.

To run Amicron, you must first set up the directory 
"sys:usr/spool/cron" and then use your favorite editor to create 
the "crontab" file.  Amicron needs a CLI window for displaying the 
output of the commands it runs...but it can be made as small or 
large as you wish.  Assuming that you have placed Amicron in your 
command directory (c:), you can start it by typing the following 
line into a "newcli" window:
    Amicron
Then you can size and move the window anywhere you want, just be 
sure not to type anything else into the window because that would 
prevent cron tasks from running.

I have an entry in my startup-sequence file that looks like this:
   run newcli con:0/140/160/50/CronTask s:startcron
The "s:startcron" file contains one line:
   Amicron
This automatically sets up a tiny CronTask window and runs Amicron 
in it.

This program was originally written by Steve R.  Sampson (UUCP:
sys1!killer!sampson), who very kindly responded to my request for
a PD cron that I could port to Minix.  I haven't done that yet
(My new Amiga 2000 came in right after I received the cron
program <grin>) but will do so ASAP.  The modifications to make
it run on the Amiga were very minor!

Rick Schaeffer          UUCP:  seismo!uunet!iscuva!ricks
E. 13611 26th Ave.      Phone: (509)928-3533
Spokane, WA  99216
SHAR_EOF
cat << \SHAR_EOF > Makefile
Amicron: Amicron.o
	ln -o Amicron Amicron.o -lc32

Amicron.o: Amicron.c
	cc +l Amicron.c
SHAR_EOF
cat << \SHAR_EOF > Amicron.c
/*
 *	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 "Sys:usr/spool/cron/crontab"
#define CRONERR "Sys:usr/spool/cron/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 *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))  {
 
			fexecl("Run","Run",command,NULL);
			}
		}
	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);
		}
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Rick Schaeffer          UUCP:  uunet!iscuva!ricks
ISC Systems Corp.              ricks@iscuva.ISCS.COM
Box TAF-C8              Phone: (509)927-5114
Spokane, WA  99220