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