[alt.sources] repost: cron

donlash@uncle.uucp (Donald Lashomb) (01/16/91)

It seems that part 4 of 4 of my cron program somehow was lost in space.
I have recieved email from 3 widely separated sites who are missing it,
so here is a repost.  Sorry about the bandwith to those who got it the
first time.
----
Don

---- Cut Here and unpack ----
#!/bin/sh
# This is part 04 of a multipart archive
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= parsesched.c ==============
if test X"$1" != X"-c" -a -f 'parsesched.c'; then
	echo "File already exists: skipping 'parsesched.c'"
else
echo "x - extracting parsesched.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > parsesched.c &&
X/*   parse: cronjob <schedule>
X *
X *	<schedule> = <mm> <hh> <DD> <MM> <ww>
X *
X *		<mm> = <intlist>
X *
X *		<hh> = <intlist>
X *
X *		<DD> = <intlist>
X *
X *		<MM> = <intlist>
X *		      |<monthlist>
X *
X *		<ww> = <intlist>
X *		      |<wdaylist>
X *
X *		<intlist> = <number>
X *			   |<number>,<intlist>
X *
X *		<number> = <digits>
X *			  |<digits>-<digits>
X *
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <memory.h>
X#include <setjmp.h>
X#include "cron.h"
X#include "job.h"
X
X
X/* =============== hooks to the caller of parsesched() ================ */
X
X/*   parsesched() returns pointer to static SCHED struct or NULL if bad */
X/*          sets caller's char *str --> rest of the line in static area */
X
Xextern int		optind;
Xstatic SCHED		sched;
X
X/* ==================================================================== */
X
X
Xextern void		longjmp();
Xstatic jmp_buf		parsebad;
X#define badsched()	longjmp(parsebad,-1)
X
Xstatic char		line[LINESIZ];
Xstatic char		*lin;
X
Xstatic void		markstring();
Xstatic void		list();
Xstatic int		nocvt();
Xstatic int		mocvt();
Xstatic int		wkcvt();
Xstatic int		compare();
Xstatic void		eatword();
X/* ==================================================================== */
X
XSCHED *parsesched(argc,argv,strp)
X	int	argc;
X	char	**argv;
X	char	**strp;
X	{
X	char	*mm,*hh,*DD,*MM,*ww;
X
X	if(setjmp(parsebad)) return((SCHED *)NULL);
X
X	/* put command line back together, always put " " at end */
X	*line = '\0';
X	while(optind < argc) {
X		strncat(line,argv[optind],
X			LINESIZ-1-strlen(line)-strlen(argv[optind]));
X		strncat(line," ",
X			LINESIZ-1-strlen(line)-strlen(argv[optind]));
X		++optind;
X		}
X	lin = line;
X
X	/* now break command line into 5 strings */
X	markstring(&mm);
X	markstring(&hh);
X	markstring(&DD);
X	markstring(&MM);
X	markstring(&ww);
X	while(isspace(*lin)) ++lin;
X	*strp = lin;			/* pass back pointer to rest of line */
X
X	list(mm,sched.min,0,60,0,nocvt);
X	list(hh,sched.hour,0,24,0,nocvt);
X	list(DD,sched.mday,1,32,0,nocvt);
X	list(MM,sched.mon,0,12,1,mocvt);
X	list(ww,sched.wday,0,7,0,wkcvt);
X
X/* if specify '*' for only one "days" field
X * then only the other one counts
X */
X	if((*DD == '*') && (*ww != '*'))
X		memset(sched.mday,'\0',32);
X	if((*ww == '*') && (*DD != '*'))
X		memset(sched.wday,'\0',7);
X
X/* note: things like Feb 31st are not checked for
X * the resched() routine takes care of these
X */
X	return(&sched);
X	}
X
X
X/* mark separate strings -------------------------------------- */
X
Xstatic void markstring(str)
X	char	**str;
X	{
X	while(isspace(*lin)) ++lin;
X	if(*lin == '\0') badsched();
X	*str = lin;
X	while(!isspace(*lin)) ++lin;
X	*lin++ = '\0';
X	}
X
X/* set arrays from ascii "lists" ------------------------------ */
X
Xstatic void list(asc,ary,beg,end,off,cvt)
X	char	*asc;
X	char	ary[];
X	int	beg;
X	int	end;
X	int	off;
X	int	(*cvt)();
X	{
X	register int	i,b,e;
X
X	if(strcmp(asc,"*") == 0) {
X		memset(ary,'\001',end);
X		return;
X		}
X	memset(ary,'\0',end);
X	while(1) {
X		if(isdigit(*asc)) {
X			b = atoi(asc)-off;
X			while(isdigit(*asc)) ++asc;
X			}
X		else
X			b = (*cvt)(&asc);
X
X		if(*asc == '-') {
X			++asc;
X			if(isdigit(*asc)) {
X				e = atoi(asc)-off;
X				while(isdigit(*asc)) ++asc;
X				}
X			else
X				e = (*cvt)(&asc);
X			}
X		else
X			e = b;
X
X		for(i=b;i<=e;++i) {
X			if((i < beg) || (i > end))
X				badsched();
X			ary[i] = '\001';
X			}
X		switch(*asc) {
X		case '\0':
X			return;
X		case ',':
X			++asc;
X			continue;
X			}
X		badsched();
X		}
X	}
X
Xstatic int nocvt(ascp)
X	char	**ascp;
X	{
X	badsched();
X	/*NOTREACHED*/
X	}
X
X
Xstatic char		*months[] = {
X				"January","February","March","April",
X				"May","June","July","August",
X				"September","October","November","December"
X				};
Xstatic int mocvt(ascp)
X	register char	**ascp;
X	{
X	register int	i;
X	register char	*p;
X
X	/* check month names */
X	for(i=0;i<12;++i) {
X		p = months[i];
X		if(compare(*ascp,p,3) == 0) {
X			eatword(ascp,p);
X			break;
X			}
X		}
X	if(i > 11) badsched();
X	return(i);
X	}
X
X
Xstatic char		*wdays[] = {
X				"Sunday","Monday","Tuesday","Wednesday",
X				"Thursday","Friday","Saturday"
X				};
Xstatic int wkcvt(ascp)
X	register char	**ascp;
X	{
X	register int	i;
X	register char	*p;
X	
X	/* check weekday names */
X	for(i=0;i<7;++i) {
X		p = wdays[i];
X		if(compare(*ascp,p,3) == 0) {
X			eatword(ascp,p);
X			break;
X			}
X		}
X	if(i > 6) badsched();
X	return(i);
X	}
X
Xstatic int compare(p,q,n)
X	register char *p,*q;
X	register int n;
X	{
X	while(n--)
X		if(tolower(*p++) != tolower(*q++)) return(-1);
X	return(0);
X	}
X
Xstatic void eatword(ascp,p)
X	register char	**ascp;
X	register char	*p;
X	{
X	while(tolower(**ascp) == tolower(*p)) { ++(*ascp); ++p; }
X	}
SHAR_EOF
$TOUCH -am 1007171790 parsesched.c &&
chmod 0644 parsesched.c ||
echo "restore of parsesched.c failed"
set `wc -c parsesched.c`;Wc_c=$1
if test "$Wc_c" != "4523"; then
	echo original size 4523, current size $Wc_c
fi
fi
# ============= parsetime.c ==============
if test X"$1" != X"-c" -a -f 'parsetime.c'; then
	echo "File already exists: skipping 'parsetime.c'"
else
echo "x - extracting parsetime.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > parsetime.c &&
X/*   parse:  at <time>[ <date>][ <increment>]
X *
X *	<sched> = <time>
X *		 |<time> <date>
X *		 |<time> +<increment>
X *		 |<time> + <increment>
X *		 |<time> <date> +<increment>
X *		 |<time> <date> + <increment>
X *
X *	<time> = <clock>|noon|midnight|now|next|nxt|this	this=next=now
X *
X *		<clock> = <clk>|<clk><csufx>|<clk> <csufx>
X *		<clk>   = <hour>|<hour><min>|<hour>:<min>
X *		<csufx> = am|pm|zulu
X *		<hour>  = <digit>|<digit><digit>
X *		<min>   = <digit><digit>
X *
X *	<date> = <month> <day>
X *		|<month> <day> <year>
X *		|<month> <day>,<year>
X *		|<month> <day>, <year>
X *		|<week>
X *		|next <month> <day>
X *		|next <week>
X *		|nxt <month> <day>
X *		|nxt <week>
X *		|this <month> <day>
X *		|this <week>
X *		|today|tomorrow
X *
X *		<month> = January|Feburary ....
X *		<day>   = <digit>|<digit><digit>		1-31
X *		<year>  = <digit><digit><digit><digit>		1970-2037
X *		<week>  = Monday|Tuesday ....
X *
X *	<increment> = +<number><incr>
X *		     |+<number> <incr>
X *		     |+ <number><incr>
X *		     |+ <number> <incr>
X *
X *		<number> = <digit>|<digit><number>
X *		<incr>   = minutes|hours|days|weeks|months|years
X *			  |hrs|wks|mos|yrs
X *
X *	notes:	words only have to match first three characters
X *		words can be upper or lower case
X *		next|nxt|this must have <date>
X *		can't use <year>|today|tomorrow with next|nxt|this
X */
X
X/* ------------------------- NOTICE -----------------------------
X
X	parsetime
X	(c) copyright March 29, 1989 by Donald Lashomb
X	(c) copyright October 16, 1990 by Donald Lashomb
X
X   This program is free.  Use it, modify it, copy it, give a copy
X   to a friend; I simply request the following provisions be observed:
X
X   1. My name as original author and this notice remain intact.
X   2. This program (or any modification of it) is not to be sold
X      for profit.
X   3. If this program is included in commercial products, there be
X      no charge for it.
X   4. This program must be distributed with source code.  Compiled-
X      only or binary-only distribution of this program is not allowed.
X      The administrator of any system that uses this program must have
X      full access to the source code.
X   5. If you enhance this program, discover a bug, have any comments
X      about it (or flames) please let me know.
X   
X   		Donald Lashomb
X   		Main Street
X   		Cranberry Lake, NY 12927
X
X   -------------------------------------------------------------- */
X
X
X/* =============== hooks to the caller of parsetime() ================= */
X
X/*    parsetime() returns sec.s since Jan 1, 1970 gmt or -1L if bad     */
X
Xextern int		optind;
X
X/* ==================================================================== */
X
X#include <stdio.h>
X#include <time.h>
X#include <memory.h>
X#include <string.h>
X#include <ctype.h>
X#include <setjmp.h>
X#include "cron.h"
X
X
Xextern long		time();
Xextern struct tm	*localtime();
Xextern struct tm	*gmtime();
Xextern void 		tzset();
Xextern long		timezone;
Xextern int		daylight;
Xextern void		longjmp();
X
Xstatic jmp_buf		parsebad;
X#define badsched()	longjmp(parsebad,-1)
X
Xstatic char		line[LINESIZ];
Xstatic char		*lin;
Xstatic long		now;
Xstatic struct tm	nowtm;
X
X					/* timcvt()  line[]      struct tm */
Xstatic int		YY;		/* 70-137    1970-2037   70-137    */
Xstatic int		MM;		/* 0-11      Jan-Dec     0-11      */
Xstatic int		DD;		/* 1-31      1-31        1-31      */
X			/* weekday	   ---       Sun-Sat     0-6       */
Xstatic int		hh;		/* 0-23      1-12ampm    0-23      */
Xstatic int		mm;		/* 0-59      0-59        0-59      */
Xstatic int		ss;		/* 0-59      ---         0-59      */
Xstatic int		gmtflag = 0;
Xstatic int		nxtflag = 0;
Xstatic int		thiflag = 0;
X
X#define chr	(*lin)
X#define gchr	(*lin++)
X
Xstatic char copyright[] = "parsetime - (c)1989,1990 D.Lashomb";
X
Xstatic void		hour();
Xstatic void		year();
Xstatic int		increment();
Xstatic void		eatword();
Xstatic int		compare();
Xstatic long		timcvt();
X/* ==================================================================== */
X
Xlong parsetime(argc,argv)
X	int argc;
X	char **argv;
X	{
X	register char *p;
X	int dateflag;
X
X	/* put command line back together, always put " " at end */
X	*line = '\0';
X	while(optind < argc) {
X		strncat(line,argv[optind],
X			LINESIZ-1-strlen(line)-strlen(argv[optind]));
X		strncat(line," ",
X			LINESIZ-1-strlen(line)-strlen(argv[optind]));
X		++optind;
X		}
X	lin = line;
X	now = time((long *)0);
X	memcpy(&nowtm,localtime(&now),sizeof(struct tm));
X	tzset();
X	YY = nowtm.tm_year;
X	MM = nowtm.tm_mon;
X	DD = nowtm.tm_mday;
X	hh = nowtm.tm_hour;
X	mm = nowtm.tm_min;
X#ifdef ZEROSECS
X	ss = 0;
X#else
X	ss = nowtm.tm_sec;
X#endif
X
X	if(setjmp(parsebad)) return(-1L);
X
X	if(isdigit(chr)) hour();
X
X	else if(compare(lin,(p="noon"),3) == 0) {
X			eatword(p);
X			hh = 12;
X			mm = 0;
X			}
X	else if(compare(lin,(p="midnight"),3) == 0) {
X			eatword(p);
X			hh = 0;
X			mm = 0;
X			}
X	else if(compare(lin,(p="now"),3) == 0) {
X			eatword(p);
X			ss = nowtm.tm_sec;
X			/* default now */
X			}
X	else if((compare(lin,(p="next"),3) == 0) ||
X		(compare(lin,(p="nxt"),3) == 0)) {
X			eatword(p);
X			nxtflag = 1;
X			}
X	else if(compare(lin,(p="this"),3) == 0) {
X			eatword(p);
X			thiflag = 1;
X			}
X	else badsched();
X
X	dateflag = date();
X	if(!dateflag) {
X		if((hh < nowtm.tm_hour) ||
X		   ((hh == nowtm.tm_hour) && (mm < nowtm.tm_min))) {
X			/* tommorrow */
X			++DD;
X			}
X		}
X	increment();
X	if(chr != '\0') badsched();
X	if((nxtflag || thiflag) && (!dateflag)) badsched();
X	return(timcvt());
X	}
X
X/* ==================================================================== */
X
Xstatic void hour()
X	{
X	char digits[5];
X	register int i;
X	register char *p;
X
X	for(i=0;i<5; ) {
X		if(isdigit(chr)) digits[i++] = gchr;
X		else if(chr == ':') ++lin;
X		else  break;
X		}
X	digits[i] = '\0';
X	if(i > 2) {
X		if((mm = atoi(&digits[i-2])) > 59) badsched();
X		digits[i-2] = '\0';
X		}
X	else
X		mm = 0;
X
X	hh = atoi(digits);
X
X	if(chr == ' ') ++lin;
X	if(compare(lin,(p="am"),2) == 0) {
X		eatword(p);
X		if(hh > 12) badsched();
X		if(hh == 12) hh = 0;
X		}
X	else if(compare(lin,(p="pm"),2) == 0) {
X		eatword(p);
X		if(hh != 12) hh += 12;
X		}
X	else if(compare(lin,(p="zulu"),3) == 0) {
X		eatword(p);
X		gmtflag = 1;
X		memcpy(&nowtm,gmtime(&now),sizeof(struct tm));
X		YY = nowtm.tm_year;
X		MM = nowtm.tm_mon;
X		DD = nowtm.tm_mday;
X		}
X	else if(*(lin-1) != ' ') badsched();
X
X	if(hh > 23) badsched();
X	}
X
X/* ==================================================================== */
X
Xstatic char		*months[] = {
X				"January","February","March","April",
X				"May","June","July","August",
X				"September","October","November","December"
X				};
X
Xstatic char		*wdays[] = {
X				"Sunday","Monday","Tuesday","Wednesday",
X				"Thursday","Friday","Saturday"
X				};
X
Xstatic int date()		/* strips trailing space */
X	{
X	register int i;
X	register char *p;
X
X	/* check for "next" and "this" */
X	if((compare(lin,(p="next"),3) == 0) ||
X	   (compare(lin,(p="nxt"),3) == 0)) {
X		eatword(p);
X		nxtflag = 1;
X		}
X	else if(compare(lin,(p="this"),3) == 0) {
X		eatword(p);
X		thiflag = 1;
X		}
X
X	/* check month names */
X	for(i=0;i<12;++i) {
X		p = months[i];
X		if(compare(lin,p,3) == 0) {
X			eatword(p);
X			break;
X			}
X		}
X	if(i <= 11) {
X		MM = i;
X		if(!isdigit(chr)) badsched();
X		DD = atoi(lin);
X		if(DD > 31) badsched();
X		while(isdigit(chr)) ++lin;
X		if(chr == ',') {
X			++lin;
X			if(chr == ' ') ++lin;
X			if(!isdigit(chr)) badsched();
X			year();
X			return(1);
X			}
X		if(gchr != ' ') badsched();
X		if(isdigit(chr)) year();
X		if((nxtflag) ||
X		   ((!thiflag) && (MM < nowtm.tm_mon)))
X			++YY;
X		return(1);
X		}
X	
X	/* check weekday names */
X	for(i=0;i<7;++i) {
X		p = wdays[i];
X		if(compare(lin,p,3) == 0) {
X			eatword(p);
X			break;
X			}
X		}
X	if(i <= 6) {
X		if(nxtflag) DD += (i - nowtm.tm_wday) + 7;
X		else if(thiflag) DD += (i - nowtm.tm_wday);
X		else DD += (7 + (i - nowtm.tm_wday)) % 7;
X		return(1);
X		}
X	
X	/* check "today" or "tomorrow" */
X	if(nxtflag || thiflag) badsched();
X	p = "today";
X	if(compare(lin,p,3) == 0) {
X		eatword(p);
X		return(1);
X		}
X	p = "tomorrow";
X	if(compare(lin,p,3) == 0) {
X		eatword(p);
X		++DD;
X		return(1);
X		}
X
X	return(0);
X	}
X
X/* ==================================================================== */
X
Xstatic void year()
X	{
X	if(nxtflag || thiflag) badsched();
X	if(((YY = atoi(lin)) < 1970) || (YY > 2037)) badsched();
X	lin += 4;
X	if(gchr != ' ') badsched();
X	YY -= 1900;
X	}
X			
X/* ==================================================================== */
X
Xstatic int increment()
X	{
X	register int i;
X	register char *p;
X
X	if(chr != '+') return(0);
X	++lin;				/* gobble up '+' */
X	if(chr == ' ') ++lin;
X	if(!isdigit(chr)) badsched();
X	i = atoi(lin);
X	while(isdigit(chr)) ++lin;
X	if(chr == ' ') ++lin;
X
X	if(compare(lin,(p="minutes"),3) == 0)     { eatword(p); mm += i; }
X	else if(compare(lin,(p="hours"),3) == 0)  { eatword(p); hh += i; }
X	else if(compare(lin,(p="hrs"),3) == 0)    { eatword(p); hh += i; }
X	else if(compare(lin,(p="days"),3) == 0)   { eatword(p); DD += i; }
X	else if(compare(lin,(p="weeks"),3) == 0)  { eatword(p); DD += 7*i; }
X	else if(compare(lin,(p="wks"),3) == 0)    { eatword(p); DD += 7*i; }
X	else if(compare(lin,(p="months"),3) == 0) { eatword(p); MM += i; }
X	else if(compare(lin,(p="mos"),3) == 0)    { eatword(p); MM += i; }
X	else if(compare(lin,(p="years"),3) == 0)  { eatword(p); YY += i; }
X	else if(compare(lin,(p="yrs"),3) == 0)    { eatword(p); YY += i; }
X	else badsched();
X	return(1);
X	}
X
X/* ==================================================================== */
X
Xstatic void eatword(p)
X	register char *p;
X	{
X	while(tolower(chr) == tolower(*p)) { ++lin; ++p; }
X	if(gchr != ' ') badsched();
X	}
X
X/* ==================================================================== */
X
Xstatic int compare(p,q,n)
X	register char *p,*q;
X	register int n;
X	{
X	while(n--)
X		if(tolower(*p++) != tolower(*q++)) return(-1);
X	return(0);
X	}
X
X/* ==================================================================== */
X
Xstatic int thirty[2][12] = {31,28,31,30,31,30,31,31,30,31,30,31,
X                           31,29,31,30,31,30,31,31,30,31,30,31};
X
Xstatic int modays[2][12] = {0,31,59,90,120,151,181,212,243,273,304,334,
X			    0,31,60,91,121,152,182,213,244,274,305,335};
X
Xstatic long timcvt()
X	{
X	long secs,days;
X	int isleap;
X	struct tm *when;
X
X	/* roll forward the hands of time */
X	hh += mm/60; mm %= 60;
X	DD += hh/24; hh %= 24;
X	while(1) {
X		YY += MM/12; MM %= 12;
X		/* leapyear = (div4 except(100 except(400))); 2000 is! */
X		isleap = ((YY%4)==0)? 1: 0;
X		if(DD <= thirty[isleap][MM])
X			break;
X		DD -= thirty[isleap][MM];
X		++MM;
X		}
X
X	days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X	secs = (((days+DD-1)*24L*3600L) + (hh*3600L) + (mm*60) + ss);
X
X	if(!gmtflag) {
X		secs += timezone;
X		when = localtime(&secs);
X		if(when->tm_isdst) secs -= 3600;
X		}
X	return(secs);
X	}
SHAR_EOF
$TOUCH -am 1016221090 parsetime.c &&
chmod 0644 parsetime.c ||
echo "restore of parsetime.c failed"
set `wc -c parsetime.c`;Wc_c=$1
if test "$Wc_c" != "10619"; then
	echo original size 10619, current size $Wc_c
fi
fi
# ============= resched.c ==============
if test X"$1" != X"-c" -a -f 'resched.c'; then
	echo "File already exists: skipping 'resched.c'"
else
echo "x - extracting resched.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > resched.c &&
X/* ------------------------------------------------------------
X   reschedule a cron job:
X
X   Resched scans the scheduling information in the global JOB
X   structure, jjj, comparing it to jtime, and determines the
X   next time that a job is supposed to run.  It returns the
X   answer or -1L if the scheduling info is invalid.
X
X   This is a weird algorithm.  You'd think there would be a more
X   elegant way, maybe some matrix calculations.  But, anyway, it
X   is fairly fast.  It's an O-sum rather than O-product method
X   because, eg., minutes are reset if hours need searching.
X   ------------------------------------------------------------ */
X
X#include <time.h>
X#include <setjmp.h>
X#include "job.h"
X
Xextern struct tm	*localtime();
Xextern void		tzset();
Xextern long		timezone;
Xextern void		longjmp();
X
Xextern JOB	jjj;			/* global JOB struct */
X
X
Xstatic int thirty[2][12] = {31,28,31,30,31,30,31,31,30,31,30,31,
X                           31,29,31,30,31,30,31,31,30,31,30,31};
X
Xstatic int modays[2][12] = {0,31,59,90,120,151,181,212,243,273,304,334,
X			    0,31,60,91,121,152,182,213,244,274,305,335};
X
Xstatic int		ss;
Xstatic int		mm;
Xstatic int		hh;
Xstatic int		DD;
Xstatic int		MM;
Xstatic int		YY;
Xstatic int		ww;
Xstatic int		chk;
Xstatic struct tm	*sched;
Xstatic int		isleap;
Xstatic long		days;
Xstatic long		jtime;
Xstatic jmp_buf		bad;
X#define badsched()	longjmp(bad,-1)
X
Xstatic void incmon()
X	{
X	MM = (++MM)%12;
X	if(MM == 0) {
X		++YY;
X		isleap = ((YY%4)==0)? 1: 0;
X		}
X	}
X
Xstatic void chkmon()
X	{
X	if(jjj.mon[MM] == '\0') {
X		ss = mm = hh = 0;
X		DD = 1;
X		do {
X			if(--chk < 0) badsched();
X			incmon();
X			} while(jjj.mon[MM] == '\0');
X
X		/* find day of the week: 00:00 Jan 1 1970 GMT was Thursday */
X		days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X		ww = (int)((days+4)%7);
X		}
X	}
X
Xstatic void incday()
X	{
X	ww = (++ww)%7;
X	++DD;
X	if(DD > thirty[isleap][MM]) {
X		DD = 1;
X		incmon();
X		chkmon();
X		}
X	}
X
Xstatic void chkday()
X	{
X	if((jjj.wday[ww] == '\0') && (jjj.mday[DD] == '\0')) {
X		ss = mm = hh = 0;
X		do {
X			if(--chk < 0) badsched();
X			incday();
X			} while((jjj.wday[ww]=='\0') && (jjj.mday[DD]=='\0'));
X		}
X	}
X
Xstatic void inchour()
X	{
X	hh = (++hh)%24;
X	if(hh == 0) {
X		incday();
X		chkday();
X		}
X	}
X
Xstatic void chkhour()
X	{
X	if(jjj.hour[hh] == '\0') {
X		ss = mm = 0;
X		do {
X			if(--chk < 0) badsched();
X			inchour();
X			} while(jjj.hour[hh] == '\0');
X		}
X	}
X
Xstatic void incmin()
X	{
X	mm = (++mm)%60;
X	if(mm == 0) {
X		inchour();
X		chkhour();
X		}
X	}
X
Xstatic void chkmin()
X	{
X	if(jjj.min[mm] == '\0') {
X		ss = 0;
X		do {
X			if(--chk < 0) badsched();
X			incmin();
X			} while(jjj.min[mm] == '\0');
X		}
X	}
X
X/* ------------------------------------------------------------ */
X
Xlong resched(tim)
X	long		tim;
X	{
X	jtime = tim + 60;	/* kick ahead 1 min */
X
X/* check that sched arrays are valid
X * if takes more than 60+24+31+12 loops
X * then schedule is invalid
X */
X	chk = (60+24+31+12)*2;
X	if(setjmp(bad)) return(-1L);
X
X	tzset();
X	sched = localtime(&jtime);
X	ss = sched->tm_sec;
X	mm = sched->tm_min;
X	hh = sched->tm_hour;
X	DD = sched->tm_mday;
X	MM = sched->tm_mon;
X	YY = sched->tm_year;
X	ww = sched->tm_wday;
X	isleap = ((YY%4)==0)? 1: 0;	/* works for year 2000 */
X
X	chkmon();
X	chkday();
X	chkhour();
X	chkmin();
X
X	days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X	jtime = (((days+DD-1)*24L*3600L)+(hh*3600L)+(mm*60)+ss+timezone);
X	sched = localtime(&jtime);
X	if(sched->tm_isdst) jtime -= 3600;
X	if(jtime <= tim) jtime += 3600;		/* kludge around 2:00am dst */
X	return(jtime);
X	}
X
SHAR_EOF
$TOUCH -am 1009070890 resched.c &&
chmod 0644 resched.c ||
echo "restore of resched.c failed"
set `wc -c resched.c`;Wc_c=$1
if test "$Wc_c" != "3496"; then
	echo original size 3496, current size $Wc_c
fi
fi
# ============= version ==============
if test X"$1" != X"-c" -a -f 'version'; then
	echo "File already exists: skipping 'version'"
else
echo "x - extracting version (Text)"
sed 's/^X//' << 'SHAR_EOF' > version &&
X****************************************************
X************ cron facility by D.Lashomb ************
X****************************************************
X
XThis is an informal revision history for the cron facility:
X
X1.0	March 89	minimal daemon. at(1) and batch(1) working
X
X2.0	April 89	source code is broken into multiple files for
X			easier dev and maint. added cronjob(1).
X
X2.5	April 89	added crontab(1).  incr number of jobs facility
X			can handle by decr bytes passed thru fifo and
X			stored in mem.  multiple forms of JOB struct.
X			added MAXKIDS param.  daemon's sleep/wake cycle
X			now adjusts for time used while awake.  many
X			other tweeks - first *real* version
X
X2.6	April 89	fixed minor bug in cleanlog with uid=cron.
X			released to jbm@uncle
X
X2.7	May 17, 89	faster day of week calc in resched.c and kludge
X			around resched problem at 2:00am d.s.t. changes
X
X2.8	June 19, 89	fixed align() in daemon.c
X
X2.8.1	July 3, 89	crontab must run as SUID=root because of bug in
X			setuid() with uid=0.  changed README and Install
X
X2.9	July 16, 89	fixed problem in at.c concerning getpwnam() - I
X			forgot that it uses static area of memory.  This
X			bug was introduced in ver 2.6 when I put in code
X			for getpwnam("cron").  Cosmetic changes to cron.h
X			and README file.
X
X3.0	July 26, 89	Runtime speedup: user's shell is now put in job file
X			by at(1) ... crontab(1) instead of daemon having to
X			search passwd file to get this info.  At(1) ... have
X			to check the passwd file in order validate LOGNAME
X			anyway, so this is much more effecient.
X
X			Added SET_LOGNAME code to putenv("LOGNAME=root")
X			for uid=0 when normal LOGNAME check fails for at(1)
X			... crontab(1) commands.  Thanks to John Milton for
X			this suggestion.  Pulled allow/deny checking out of
X			at.c and crontab.c and put it in a new file allow.c
X
X			Cleanup and reorganized use of some globals.  Minor
X			additions to makefile.  Fixed man pages re: LOGNAME
X
X3.0.1	Dec 21, 89	Added caveats to README file, released to usenet.
X
X4.0	Sep-Oct, 90	New getwd() code fixes read() bug not reading until
X			newline from /bin/pwd.
X			
X			Added "all" arg to -l option for symmetry with -r
X			
X			Pulled getwd and dir code out of at.c into new files
X			getwd.c and dir.c
X
X			Sprinkled the code with register variables.
X
X			Tried to cleanup the use of global/static variables.
X
X			Streamlined allow mechanism for at.c and crontab.c
X			Handles degenerate case of LOGNAME=root better and
X			is faster.  One login() routine does it all.  Code
X			is in file login.c, formerly called allow.c
X
X			Fixed execlp() in crontab.c - was missing NULL last
X			arg.  Why this didn't cause any problem before, I
X			don't know; just lucky I guess.
X
X			Used dir.c routines in initlist() memlist.c instead
X			of using popen to ls(1) command.
X
X			rdfifo() and wrfifo() always used global jjj JOB
X			struct, so no need of passing &jjj to them at every
X			call.  Changed to use global jjj.
X
X			insert() in memlist.c always called with &jjj, too.
X			Changed to use global jjj.
X
X			Added -x option to execute jobs at will.  This req'd
X			changes to at.c, daemon.c, job.h and job.c to support
X			the new EXECUT msg type.
X
X			Better checking of numeric jobnumber/filename in at.c
X
X			A new scheme for marking completed job files is used.
X			Previously AT_JOBs, BATCHJobs and CR_JOBs were marked
X			with an extra newline and a "# job done\n" as the
X			last act of writing a new job file.  This was done to
X			insure that only complete jobs would be executed.
X			The new scheme uses the first 4 bytes in the file as
X			a magic number.  The magic number is initialized to
X			NOMAGIC when working on the job file.  As the last
X			act of writing a new job file, the file is rewound
X			and the magic number, MAGIC, is written in the file.
X			The envc and envz fields of the header also play a
X			role similar to the magic in the new scheme.
X
X			Added throwaway program, convertjob, to ease the
X			upgrading old style jobs to the new scheme.
X
X			The new scheme fixes the problem of at's -u option
X			displaying the extra newline.
X
X			Removed the requirement for having to have a crontab
X			file in place in order for CR_TAB jobs to survive a
X			reboot.
X
X			Better checking of job files by openjob() in daemon.c
X
X			Made the daemon a little smarter about handling
X			changes to the system clock.  If someone changes the
X			system clock more than just a little bit, the daemon
X			resets its idea of the time.
X
X			Fixed man pages to reflect the above changes.
X			Split off Design_Notes from README file.
SHAR_EOF
$TOUCH -am 1016214790 version &&
chmod 0644 version ||
echo "restore of version failed"
set `wc -c version`;Wc_c=$1
if test "$Wc_c" != "4537"; then
	echo original size 4537, current size $Wc_c
fi
fi
exit 0