v07i063: Nag reminder service, Part02/02
sources-request at mirror.TMC.COM
sources-request at mirror.TMC.COM
Tue Nov 11 03:48:01 AEST 1986
Submitted by: rtech!daveb at rtech.uucp (Dave Brower)
Mod.sources: Volume 7, Issue 63
Archive-name: nag/Part02
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# gdate.c
# nag.c
export PATH; PATH=/bin:$PATH
echo shar: extracting "'gdate.c'" '(13521 characters)'
if test -f 'gdate.c'
then
echo shar: over-writing existing file "'gdate.c'"
fi
sed 's/^X//' << \SHAR_EOF > 'gdate.c'
X/*
X * routines to turn a date from various formats to other formats
X *
X * Primary interesting routine is gdate() which eats a date
X * string of several common formats (see comment) and
X * fills in a standard UNIX tm structure.
X *
X * Barry Shein, Boston University
X *
X * if you compile it -DDEBUG (with DEBUG defined) it will
X * pull in a main() routine to run standalone for testing.
X *
X * NOTE:
X *
X * Barry's gdate was broken by a 1-off error; tm_mon is kept
X * in the range 0..11 instead of 1..12. Also, his totime was
X * broken, so I've deleted it and use the tm_to_time() function
X * from mod.sources.
X *
X *
X * Defines the functions:
X *
X * lcase() -- convert a char to lower case
X * dstring() -- get digit string from sp into bp (buffer) returning new sp
X * skipw() -- skip white space returning updated ptr
X * prefix() -- return how many chars of s1 prefix s2
X * find() -- look up str in list for non-ambiguous (prefix) match
X * lookup() -- like find but demands exact match
X * extract() -- extract a token
X * fill() -- fill up an area with a value (eg. zeros)
X *
X * gdate() -- convert a date/time string to a tm structure.
X * gtime() -- convert time string to a tm structure.
X *
X * days() -- how many days were in a year.
X * jan1() -- return day of the week of jan 1 of given year
X * dowk() -- insert day of week given year and day of year into tm struct.
X * doyr() -- insert partial day of year given yr, mon and mday into tm struct.
X *
X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
X * ndays() -- number of days between UNIX epoch and time in a tm struct.
X * tm_to_time() -- Convert a tm struct to a time_t.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X
X#ifdef SYS5
X
X# define time_t long /* SV is inconsistent, so go with lcd */
X
X# include <time.h>
X# include <sys/times.h>
X# include <string.h>
X
X# else /* BSD */
X
X# include <sys/time.h>
X# include <sys/timeb.h>
X# include <strings.h>
X
X#endif
X
X/*----------------------------------------------------------------
X *
X * Manifest constants
X *
X */
X
X#define MAXTOK 20
X#define AMBIG -1 /* ambiguous month */
X#define FALSE -2 /* bad syntax */
X
X/*----------------------------------------------------------------
X *
X * static and global Data
X *
X */
X
Xchar *months[] = {
X "january", "february", "march", "april", "may", "june", "july",
X "august", "september", "october", "november", "december", 0
X} ;
X
Xchar *dow[] = {
X "sunday", "monday", "tuesday", "wednesday", "thursday",
X "friday", "saturday", 0
X} ;
X
X /*
X * known time-zone name list
X */
Xchar *tz[] =
X{
X "adt", "ast", "edt", "est", "cdt", "cst", "mst", "mdt",
X "pdt", "pst", "gmt",
X 0
X} ;
X
Xchar mdays[] = {
X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X} ;
X
X/*----------------------------------------------------------------
X *
X * Utility functions
X *
X */
X
X/*
X * lcase() -- convert a char to lower case
X */
Xlcase(c) register char c ;
X{
X return(isupper(c) ? tolower(c) : c) ;
X}
X
X/*
X * dstring() -- get digit string from sp into bp (buffer) returning new sp
X */
Xchar *
Xdstring(bp,sp)
X register char *sp, *bp ;
X{
X register int i = 0 ;
X while(isdigit(*sp))
X if(i++ == MAXTOK) break ;
X else *bp++ = *sp++ ;
X *bp = '\0' ;
X return(sp) ;
X}
X
X/*
X * skipw() -- skip white space returning updated ptr
X */
Xchar *
Xskipw(sp) register char *sp ;
X{
X while(isspace(*sp) || *sp == '-') ++sp ;
X return(sp) ;
X}
X
X/*
X * prefix() -- return how many chars of s1 prefix s2
X */
Xprefix(s1,s2) register char *s1, *s2 ;
X{
X register int i = 0 ;
X
X for(; *s1 == *s2 ; s1++,s2++,i++)
X if(*s2 == '\0') break ;
X return(*s1 == '\0' ? i : 0) ;
X}
X
X/*
X * find() -- look up str in list for non-ambiguous (prefix) match
X */
Xfind(sp,lp) register char *sp, **lp ;
X{
X int i,j,ambig = 0 ;
X register int k ;
X int ret = FALSE ;
X
X for(i = 0,k = 0 ; *lp ; lp++,k++)
X if((j = prefix(sp,*lp)) > i)
X {
X ambig = 0 ;
X i = j ;
X ret = k + 1 ;
X }
X else if(j && (i == j)) ++ambig ;
X return(ambig ? AMBIG : ret) ;
X}
X
X/*
X * lookup() -- like find but demands exact match
X */
Xlookup(sp,lp) register char *sp, **lp ;
X{
X int i = 0 ;
X
X for(i=0 ; *lp ; lp++,i++)
X if(strcmp(sp,*lp) == 0) return(i+1) ;
X return(0) ;
X}
X
X/*
X * extract() -- extract a token
X */
Xchar *
Xextract(bp,sp) register char *bp, *sp ;
X{
X register int i = 0 ;
X
X sp = skipw(sp) ;
X for(; isalnum(*sp); sp++)
X if(i++ == MAXTOK) break ;
X else *bp++ = lcase(*sp) ;
X *bp = '\0' ;
X return(sp) ;
X}
X
X/*
X * fill() -- fill up an area with a value (eg. zeros)
X */
Xfill(cp,c,n) register char *cp, c ; register int n ;
X{
X while(n--) *cp++ = c ;
X}
X
X/*----------------------------------------------------------------
X *
X * gdate, gtime related.
X *
X */
X
Xchar *gdate() ;
Xchar *gtime() ;
X
X/*
X * gdate(date_str_ptr,time_buf_ptr)
X * (see CTIME(3) in UPM for second arg format)
X * takes many reasonable date strings and translates to
X * the time-buf structure (integers)
X *
X * formats (eg.):
X * oct 19, 1983
X * OcT 19, 1983 12:43
X * oCt 19, 1983 12:43:33
X * oct 19 1983 ...
X * 19 oct 83 ....
X * 10/19/83 12:43:33
X * 19-OCT-83 12:43:00 (DEC style)
X * Wed Oct 19 12:43 EST 83 (UNIX style)
X * oct. 19, 83 1:44 pm est
X * 831019124333 (see date(1))
X * some variations on those themes also.
X *
X * BUGS: probably a few (maybe some in the eye of the beholder)
X * does not set dst flag (unless 'EDT' is in string)
X */
Xchar *
Xgdate(sp,tp) register char *sp ; register struct tm *tp ;
X{
X char buf[MAXTOK] ;
X
X fill(tp,'\0',sizeof *tp) ;
X sp = skipw(sp) ;
X if(isdigit(*sp)) /* either '7/12/83' or '12 jul 83' */
X {
X if(isdigit(sp[1]) && isdigit(sp[2])) /* UNIX yymmddhhmmss */
X {
X buf[2] = '\0' ;
X (void)strncpy(buf,sp,2) ;
X tp->tm_year = atoi(buf) ;
X (void)strncpy(buf,sp += 2,2) ;
X tp->tm_mon = atoi(buf) - 1 ;
X sp += 2 ;
X if(!isdigit(*sp)) goto badday ;
X (void)strncpy(buf,sp,2) ;
X tp->tm_mday = atoi(buf) ;
X sp += 2 ;
X if(!isdigit(*sp)) goto check ;
X (void)strncpy(buf,sp,2) ;
X
X /* ??? formerly null effect "tp->tm_hour ;" */
X
X tp->tm_hour = atoi(buf) ;
X sp += 2 ;
X if(!isdigit(*sp)) goto check ;
X (void)strncpy(buf,sp,2) ;
X tp->tm_min = atoi(buf) ;
X sp += 2 ;
X if(!isdigit(*sp)) goto check ;
X (void)strncpy(buf,sp,2) ;
X tp->tm_min = atoi(buf) ;
X goto check ;
X }
X sp = dstring(buf,sp) ;
X sp = skipw(sp) ;
X if(*sp == '/') /* must be '7/12/83' */
X {
X if((tp->tm_mon = atoi(buf) - 1) < 0 || (tp->tm_mon > 11))
X {
X tp->tm_mon = FALSE ;
X goto badmon ;
X }
X sp = skipw(++sp) ;
X if(!isdigit(*sp)) goto badday ;
X sp = dstring(buf,sp) ;
X tp->tm_mday = atoi(buf) ;
X
X sp = skipw(sp) ;
X if(*sp != '/') goto badyr ;
X sp = skipw(++sp) ;
X if(!isdigit(*sp)) goto badyr ;
X sp = dstring(buf,sp) ;
X tp->tm_year = atoi(buf) ;
X
X sp = gtime(sp,tp) ;
X }
X else
X {
X /*
X * must be '12 jul 83'
X */
X tp->tm_mday = atoi(buf) ;
X
X sp = extract(buf,sp) ;
X if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
X
X if(*sp == '.') ++sp ;
X sp = skipw(sp) ;
X
X if(!isdigit(*sp)) goto badyr ;
X sp = dstring(buf,sp) ;
X tp->tm_year = atoi(buf) ;
X
X sp = gtime(sp,tp) ;
X }
X }
X else
X {
X int flag = 0 ; /* used to indicate looking for UNIX style */
X
X /*
X * either 'jul 12 83' or (UNIX) Wed jul 12 18:33 EST 1983
X */
X sp = extract(buf,sp) ;
X if(find(buf,dow) > 0)
X {
X sp = extract(buf,sp) ;
X ++flag ;
X }
X
X if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
X
X if(*sp == '.') ++sp ;
X sp = skipw(sp) ;
X
X if(!isdigit(*sp)) goto badday ;
X sp = dstring(buf,sp) ;
X tp->tm_mday = atoi(buf) ;
X
X sp = skipw(sp) ;
X if(*sp == ',') sp++ ;
X sp = skipw(sp) ;
X
X if(flag) sp = skipw(gtime(sp,tp)) ;
X
X if(!isdigit(*sp)) goto badyr ;
X sp = dstring(buf,sp) ;
X tp->tm_year = atoi(buf) ;
X
X if(!flag) sp = gtime(sp,tp) ;
X }
X check:
X /*
X * check for ridiculous numbers
X */
X if(tp->tm_mday < 1) goto badday ;
X if(tp->tm_mday > mdays[tp->tm_mon])
X if(!((tp->tm_mon == 1) && /* check for Feb 29 */
X (tp->tm_mday == 29) && (days(tp->tm_year) == 365) ))
X goto badday ;
X if(tp->tm_year >= 1900) tp->tm_year -= 1900 ;
X if(tp->tm_hour > 23)
X {
X tp->tm_hour = FALSE ;
X return(sp) ;
X }
X if(tp->tm_min > 59)
X {
X tp->tm_hour = FALSE ;
X return(sp) ;
X }
X if(tp->tm_sec > 59)
X {
X tp->tm_sec = FALSE ;
X return(sp) ;
X }
X /*
X * fill in day of year, day of week (these calls must be
X * in this order as dowk() needs doyr()
X */
X
X doyr(tp) ;
X dowk(tp) ;
X /*
X * all done !
X */
X return(NULL) ;
X badday :
X tp->tm_mday = FALSE ;
X return(sp) ;
X badmon :
X return(sp) ;
X badyr :
X tp->tm_year = FALSE ;
X return(sp) ;
X}
X
X/*
X * gtime() -- get hh:mm:ss or equivalent into a tm struct.
X */
Xchar *
Xgtime(sp,tp) register char *sp ; register struct tm *tp ;
X{
X char buf[MAXTOK],*cp ;
X
X sp = skipw(sp) ;
X if(isdigit(*sp))
X {
X sp = dstring(buf,sp) ;
X tp->tm_hour = atoi(buf) ;
X sp = skipw(sp) ;
X if(*sp == ':') sp = skipw(++sp) ;
X else goto out ;
X if(isdigit(*sp))
X {
X sp = dstring(buf,sp) ;
X tp->tm_min = atoi(buf) ;
X sp = skipw(sp) ;
X if(*sp == ':') sp = skipw(++sp) ;
X else goto out ;
X if(isdigit(*sp))
X {
X sp = dstring(buf,sp) ;
X tp->tm_sec = atoi(buf) ;
X }
X }
X }
X out :
X sp = skipw(sp) ;
X if(isalpha(*sp)) /* PM:AM or time zone or both */
X {
X cp = extract(buf,sp) ;
X if(strcmp(buf,"am") == 0 || strcmp(buf,"pm") == 0)
X {
X if(buf[0] == 'p' && tp->tm_hour < 12)
X tp->tm_hour += 12 ;
X sp = cp = skipw(cp) ;
X cp = extract(buf,cp) ;
X }
X if(lookup(buf,tz))
X {
X if(buf[1] == 'd') tp->tm_isdst++ ;
X sp = skipw(cp) ;
X }
X }
X return (sp);
X}
X
X/*
X * days() -- how many days were in a year.
X *
X * Ok, you were all wondering so here it is folks...
X * THE LEAP YEAR CALCULATION
X * note: does not take into account 1752.
X */
Xdays(y) register int y ;
X{
X if(y < 1970) y += 1900 ;
X if(((y % 4) == 0) && ( (y % 100) || ((y % 400)==0) )) y = 366 ;
X else y = 365 ;
X return(y) ;
X}
X
X
X/*
X * jan1() -- return day of the week of jan 1 of given year
X */
Xjan1(yr)
X{
X register y, d;
X
X /*
X * normal gregorian calendar
X * one extra day per four years
X */
X
X y = yr;
X d = 4+y+(y+3)/4;
X
X /*
X * julian calendar
X * regular gregorian
X * less three days per 400
X */
X
X if(y > 1800) {
X d -= (y-1701)/100;
X d += (y-1601)/400;
X }
X
X /*
X * take care of weirdness at 1752.
X */
X
X if(y > 1752)
X d += 3;
X
X return(d%7);
X}
X
X/*
X * dowk() -- insert day of week given year and day of year into tm struct.
X */
Xdowk(tp) register struct tm *tp ;
X{
X tp->tm_wday = (jan1(tp->tm_year+1900) + tp->tm_yday) % 7 ;
X}
X
X/*
X * doyr() -- insert partial day of year given yr and mon into tm struct.
X */
Xdoyr(tp) register struct tm *tp ;
X{
X register int i,j ;
X
X j = ((tp->tm_mon > 1) && (days(tp->tm_year) == 366)) ? 1 : 0 ;
X for(i=1 ; i < tp->tm_mon ; i++)
X j += mdays[i-1] ;
X tp->tm_yday = j + tp->tm_mday - 1 ;
X}
X
X/*----------------------------------------------------------------
X *
X * tm_to_time related
X *
X */
X
X/*
X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
X */
Xstatic int
Xleap (y)
X int y;
X{
X y += 1900;
X if (y % 400 == 0)
X return (1);
X if (y % 100 == 0)
X return (0);
X return (y % 4 == 0);
X}
X
X/*
X * ndays() -- number of days since UNIX epoch and time in a tm struct.
X */
Xstatic int
Xndays (p)
X struct tm *p;
X{
X register n = p->tm_mday;
X register m, y;
X register char *md = "\37\34\37\36\37\36\37\37\36\37\36\37";
X
X for (y = 70; y < p->tm_year; ++y) {
X n += 365;
X if (leap (y)) ++n;
X }
X for (m = 0; m < p->tm_mon; ++m)
X n += md[m] + (m == 1 && leap (y));
X return (n);
X}
X
X/*
X * tm_to_time() -- Convert a tm struct to a time_t.
X *
X * returns 0 if the time is before the UNIX epoch, 1/1/70 00:00:00
X */
Xtime_t
Xtm_to_time (tp)
X struct tm *tp;
X{
X register int m1, m2;
X time_t t;
X struct tm otm;
X
X /* special case date before epoch */
X if( tp->tm_year < 70) return(0);
X
X t = (ndays (tp) - 1) * 86400L + tp->tm_hour * 3600L
X + tp->tm_min * 60 + tp->tm_sec;
X /*
X * Now the hard part -- correct for the time zone:
X */
X otm = *tp;
X tp = localtime (&t);
X m1 = tp->tm_hour * 60 + tp->tm_min;
X m2 = otm.tm_hour * 60 + otm.tm_min;
X t -= ((m1 - m2 + 720 + 1440) % 1440 - 720) * 60L;
X return (t);
X}
X
X/*----------------------------------------------------------------
X *
X * Test program related
X *
X */
X
X#if DEBUG
X/*
X * test driver
X * translates first arg from command line (argv[1])
X * and dumps out structure built.
X */
Xusage(sp) char *sp ;
X{
X fprintf(stderr,"Usage: %s date\n",sp) ;
X exit(1) ;
X}
X
X/*
X * main() -- test the gdate and tm_to_time routines
X */
Xmain(argc,argv) int argc ; char **argv ;
X{
X char *cp ;
X struct tm tm ;
X time_t t,t2 ;
X char *asctime();
X char *ctime();
X
X if(argc != 2) usage(*argv) ;
X
X if((cp = gdate(*++argv,&tm)) != NULL)
X printf("error: %s (%s)\n",*argv,cp) ;
X
X printf("year : %d month: %d day: %d\n",
X tm.tm_year,tm.tm_mon,tm.tm_mday);
X printf("day of month: %d hour: %d minute: %d second: %d\n",
X tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec) ;
X printf("day of year: %d day of week: %d dst: %d\n",
X tm.tm_yday, tm.tm_wday, tm.tm_isdst) ;
X
X t = time(NULL) ;
X t2 = tm_to_time(&tm) ;
X
X printf("time_t of now %d, of arg %d\n",t, t2 ) ;
X printf("Now: %s", ctime(&t) );
X printf("Arg: %s", ctime(&t2) );
X exit(0) ;
X}
X
X#endif /* DEBUG */
X
SHAR_EOF
if test 13521 -ne "`wc -c 'gdate.c'`"
then
echo shar: error transmitting "'gdate.c'" '(should have been 13521 characters)'
fi
echo shar: extracting "'nag.c'" '(29846 characters)'
if test -f 'nag.c'
then
echo shar: over-writing existing file "'nag.c'"
fi
sed 's/^X//' << \SHAR_EOF > 'nag.c'
X/*----------------------------------------------------------------
X *
X * Nag.c -- annoying reminder service daemon
X *
X * Sun Aug 24 14:18:08 PDT 1986
X *
X * by Dave Brower, {amdahl, cbosgd, mtxinu, sun}!rtech!gonzo!daveb
X *
X * Copyright 1986, David C Brower. All rights reserved.
X *
X * This is a preliminary version. The final release will be offered
X * with fewer restrictions.
X *
X * Nag should be launched out of your .login or .profile. It
X * periodically reads your ~/.nag file and executes commands
X * that can be used as reminders of upcoming events. The environment
X * variable NAGFILE can be used to get input from something other than
X * the ~/.nag file.
X *
X * NAGFILE FORMAT:
X * ---------------
X *
X * The ~/.nag file should contain lines of the form:
X *
X * status day time interval command
X *
X * where:
X *
X * status is one of
X * 1. '#' indicating a commented out reminder
X * 2. ':' indicating a silenced reminder
X * 3. ' ' for an activate reminder.
X * Other values produce unpredicatable results.
X *
X * day is one of:
X * 1. A date, "8/8/88", "8-Aug-88", etc., but no blanks.
X * 2. '*' for any day.
X * 3. A day, "Sun", "Mon", ...
X * The last is presently unimplemented (sorry).
X *
X * time is a time spec, "8AM", "23:00", etc., but no blanks
X *
X * interval is a colon separated list of minutes after time at which
X * to execute the command, e.g.,
X *
X * -30:-15:0:5:10
X *
X * produces execution 30 and 15 minutes before the event,
X * at the time, and 5 and 10 minutes later.
X *
X * command is a command to execute with /bin/sh. Some shell variables
X * are set for use in messages:
X *
X * $pretime -interval
X * $posttime interval
X * $now hh:mm of the current time
X * $then hh:mm of the parent event
X *
X * Blank lines are ignored.
X *
X * Example:
X *
X * # don't forget to eat.
X * * 12:30PM 0 writebig "Lunch Time"
X *
X * # Weekly warning that has been silenced.
X * :Mon 3:00PM -30:-20:-10:-5:0 echo "^GStatus report in $time"
X *
X * # Active Weekly warning.
X * Fri 1:30PM -20:-10:-5:0:5:10 echo "^GCommittee meeting in $time"
X *
X * # One shot warning to call the east coast.
X * 8/25/86 1:30PM -180:-120:-60:0:10:20 echo "^GCall DEC Marlblerow"
X *
X * NAG
X * ---
X *
X * Nag puts itself in the background, and exits when you logout.
X * Standard output and standard error go to your terminal.
X *
X * Each time it wakes up, it sees if the ~/.nag file has changed.
X * If so, it builds an event queue for lines without '#' comment symbols.
X *
X * Events that were not silenced with 'X' and were due before "now"
X * are executed. If the event was the last for an entry in ~/.nag,
X * the file is edited to re-enable it from a gagged state.
X *
X * The program then sleeps for at most MINSLEEP minutes.
X *
X * OKOK
X * ----
X *
X * The "okok" program just edits ~/.nag and prepends an 'X' to lines
X * that need to be shut up.
X *
X * BUILD INSTRUCTIONS:
X * -------------------
X *
X * cc -o nag [ -DSYS5 ] nag.c gdate.c
X * ln nag okok
X *
X * The code compiles for a BSD system by default.
X *
X * CAVEATS:
X * --------
X *
X * Sorry Christopher, it probably won't work if stty nostop is set.
X *
X */
X
X# include <stdio.h>
X# include <sys/types.h>
X# include <sys/stat.h>
X# include <signal.h>
X# include <pwd.h>
X# include <ctype.h>
X
X# ifdef SYS5
X# include <string.h>
X# include <time.h>
X# define index strchr
X# define rindex strrchr
X# else
X# include <strings.h>
X# include <sys/time.h>
X# endif
X
X/*----------------
X *
X * defines
X *
X */
X
X# define DPRINTF if(Debug) (void)fprintf
X
X# define COMCHAR '#'
X# define SILCHAR ':'
X
X# define HRSECS 3600L
X
X# define CTIMELEN 32 /* length of a date/time string */
X
X# define MINSLEEP (5*60)
X# define MAXARGS 5120 /* max arg/env size on System V */
X
X# define TRUE (1)
X# define FALSE (0)
X
X# define min(a,b) ((a) < (b) ? (a) : (b))
X
X/*----------------
X *
X * typedefs and structure definitions
X *
X */
X
X/*
X * A NAGLINE is a parsed entry from the .nag file. We keep
X * a list of them representing the current file, so we can
X * write it back out easily.
X */
X
Xtypedef struct nagline NAGLINE;
X
Xstruct nagline
X{
X NAGLINE * next; /* Next in the chain */
X int type; /* COMMENT, SILENT, PENDING, BAD */
X# define UNKNOWN 0
X# define COMMENT 1
X# define SILENT 2
X# define PENDING 3
X# define BAD 4
X
X int errtype; /* if type is BAD, cause of error */
X# define NOERR 0
X# define EMPTY 1
X# define DATEBAD 2
X# define NOTIME 3
X# define TIMEBAD 4
X# define NOINTERVALS 5
X# define NOCMD 6
X
X time_t atime; /* absolute time of event */
X char *err; /* string that caused the error */
X char *line; /* the raw line, allocated */
X char *datestr; /* the date string, allocated */
X char *timestr; /* the time string, allocated */
X char *intstr; /* extracted string of intervals, allocated */
X char *cmd; /* extracted command to execute, allocated */
X};
X
Xstatic
Xchar *linetypes[] =
X{
X "Unknown",
X "Comment",
X "Silent",
X "Pending",
X "Bad"
X};
X
Xstatic
Xchar *parserrs[] =
X{
X "No error",
X "Empty line",
X "Bad date",
X "No time",
X "Bad time",
X "No intervals",
X "No command"
X};
X
X
X/*
X * An EVENT is an entry in the event queue.
X */
X
Xtypedef struct event EVENT;
X
Xstruct event
X{
X EVENT * next; /* next event in chain */
X NAGLINE *lp; /* the parent nagline */
X time_t etime; /* absolute time of the event */
X int offset; /* minutes difference with parent time */
X};
X
X
X/*----------------
X *
X * File local variables
X *
X */
X
Xstatic char *Myname=""; /* name from argv[0] */
Xstatic time_t Now = 0; /* absolute time of "now" */
Xstatic time_t Last = 0; /* time last time we were awake */
Xstatic NAGLINE *Flist = NULL; /* lines from the file */
Xstatic NAGLINE *Flast = NULL; /* last line from the file */
Xstatic EVENT *Evq = NULL; /* the global event queue */
Xstatic char Origlogin[20] = ""; /* login name when program started */
Xstatic char Nagfile[ 256 ] = ""; /* full path of the nag file */
Xstatic int Debug = FALSE; /* debugging? */
X
Xstatic char Laststr[ CTIMELEN ]; /* ctime output for last time through */
Xstatic char Nowstr[ CTIMELEN ]; /* ctime output for this time through */
X
X/*----------------
X *
X * Forward and external function definitions
X *
X */
X
X/* library defined */
X
Xextern char *getlogin(); /* login name in /etc/utmp */
Xextern char *getenv(); /* get an environment variable */
Xextern struct passwd *getpwuid(); /* passwd entry for this user */
Xextern time_t time();
Xextern struct tm *localtime();
Xextern char *fgets();
Xextern char *index();
Xextern char *rindex();
Xextern int sprintf();
Xextern void perror(); /* int on BSD? */
Xextern void qsort(); /* int on BSD? */
Xextern unsigned sleep();
Xextern void free();
Xextern void exit();
Xextern char *ctime();
X
X/* gdate.c defined */
X
Xextern char *gdate(); /* date string to time buf struct */
Xextern char *gtime(); /* time string to time buf struct */
Xextern time_t tm_to_time(); /* time buf to secs past epoch */
Xextern char *dow[]; /* days of the week names */
Xextern int find(); /* unambiguous search of string tables */
X
X/* forward function references */
X
X# define forward extern
X
Xforward void nagfile();
Xforward void setup();
X
Xforward int readf();
Xforward int editf();
Xforward int writef();
X
Xforward int parseline();
Xforward void zaplines();
X
Xforward void buildq();
Xforward void zapq();
Xforward void insq();
Xforward void addevents();
Xforward int timecmp();
Xforward void sortq();
Xforward void runq();
X
Xforward void showlines();
Xforward void dumpline();
X
Xforward void showevents();
Xforward void dumpevent();
X
Xforward char *emalloc();
Xforward char *ecalloc();
Xforward FILE *efopen();
X
Xforward char *nctime();
Xforward char *nhour();
Xforward void delay();
Xforward void lowcase();
X
X/*----------------
X *
X * main() -- Main program.
X *
X * Do one time setup, then go into a loop rebuilding the event queue,
X * executing events in order. Sleep is done after running the queue.
X *
X */
X/*ARGSUSED*/
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X char *cp;
X
X if(argc > 1)
X Debug = TRUE;
X
X Myname = (cp = rindex(argv[0], '/')) ? cp + 1 : argv[0] ;
X nagfile();
X
X if( !strcmp(Myname, "nag") )
X {
X setup();
X
X# ifndef FOREGROUND
X DPRINTF(stderr, "forking to self-backgrounnd");
X if(fork())
X exit(0);
X# endif
X /* pretend we started at the epoch */
X Now = 0;
X (void) strcpy( Nowstr, nctime( &Now ));
X
X /*
X * This loop never exits.
X *
X * The program terminates in delay() when the user logs
X * off this terminal.
X */
X for(;;)
X {
X (void) strcpy( Laststr, Nowstr );
X Last = Now;
X
X Now = time(NULL);
X (void) strcpy( Nowstr, nctime( &Now ) );
X
X DPRINTF(stderr, "\nLoop:\tLast %s\tNow %s\n", Laststr, Nowstr);
X
X if ( readf() )
X buildq();
X
X runq();
X }
X }
X else if ( !strcmp(Myname, "okok"))
X {
X Now = time( NULL );
X (void) strcpy( Nowstr, nctime( &Now ));
X
X if ( readf() )
X {
X buildq();
X if ( editf( PENDING ) )
X exit( writef() );
X }
X else
X {
X (void) fprintf(stderr, "%s: Can't read %s\n", Myname, Nagfile );
X exit(1);
X }
X }
X else
X {
X (void) fprintf(stderr, "Identity crisis: \"%s\" bad program name\n",
X argv[0]);
X exit(1);
X }
X exit(0);
X /*NOTREACHED*/
X}
X
X/*----------------
X *
X * nagfile -- get the full .nag file path
X *
X */
Xvoid
Xnagfile()
X{
X register char *home;
X register char *cp;
X
X /* remember who you are to check for logout later */
X
X (void) strcpy(Origlogin, getlogin());
X
X /* expand the Nagfile name */
X
X if( cp = getenv("NAGFILE") )
X (void)strcpy( Nagfile, cp );
X else if( home = getenv("HOME") )
X (void) sprintf( Nagfile, "%s/.nag", home );
X else
X {
X (void) fprintf(stderr, "%s: HOME is not set\n", Myname );
X exit(1);
X }
X
X DPRINTF(stderr, "Origlogin %s, Nagfile %s\n", Origlogin, Nagfile);
X}
X
X/*----------------
X *
X * setup() -- one time initialization.
X *
X * Setup signals so we don't go away.
X * accidentally.
X *
X */
Xvoid
Xsetup()
X{
X if(!Debug)
X {
X (void) signal( SIGQUIT, SIG_IGN );
X (void) signal( SIGTERM, SIG_IGN );
X# ifdef SIGTTOU
X (void) signal( SIGTTOU, SIG_IGN );
X# endif
X }
X}
X
X
X/*----------------
X *
X * readf() -- read the nagfile and build in memory copy.
X *
X * Returns TRUE if the file was read.
X */
Xint
Xreadf()
X{
X register NAGLINE *lp;
X register FILE *fp;
X char line[ MAXARGS ];
X struct stat newstat;
X static struct stat laststat = { 0 };
X static time_t readtime = 0;
X
X /* check to see if Nagfile has changed, and reread file. */
X
X if(stat(Nagfile, &newstat))
X {
X /* set it the epoch, but don't complain */
X newstat.st_mtime = 0;
X }
X
X /* if file changed, or we read it more than 12 hours ago */
X
X if ( newstat.st_mtime <= laststat.st_mtime
X || (readtime && Now > 0 && readtime < (Now - (HRSECS * 12))))
X {
X DPRINTF(stderr, "already read %s\n", Nagfile );
X return FALSE;
X }
X
X /* rebuild the internal copy of the file */
X
X DPRINTF(stderr, "reading Nagfile\n");
X
X laststat = newstat;
X readtime = Now;
X
X zaplines();
X
X /* warn, but don't fatal if file can't be opened this time through */
X
X if ( NULL==(fp = efopen(Nagfile, "r")))
X return FALSE;
X
X /* build the new incore copy */
X
X while( NULL != fgets( line, sizeof(line), fp ) )
X {
X /* Lose trailing newline */
X line[ strlen(line) - 1 ] = '\0';
X
X /*ALIGNOK*/
X lp = (NAGLINE *) ecalloc( sizeof(*lp), 1 );
X
X if( parseline( line, lp ) )
X {
X if( lp->type == BAD )
X DPRINTF(stderr, "Parsed OK: %s\n", lp->line );
X else
X DPRINTF(stderr, "Parsed OK: %s %s %s %s\n",
X lp->datestr,
X lp->timestr,
X lp->intstr,
X lp->cmd );
X }
X else
X {
X (void) fprintf(stderr, "%s: Can't parse line:\n%s\n%s %s\n",
X Myname,
X lp->line,
X parserrs[ lp->errtype ],
X lp->err );
X }
X
X if( !Flist )
X Flist = lp;
X if( Flast )
X Flast->next = lp;
X Flast = lp;
X }
X (void) fclose(fp);
X
X if(Debug)
X {
X (void) fprintf(stderr, "Read file OK\n");
X showlines( "\nLines after file read in:\n" );
X }
X
X return TRUE;
X}
X
X
X/*----------------
X *
X * editf() -- interactively edit the nag file in memory, then write it out.
X *
X * Used by 'okok' to make PENDING events SILENT; can also be used to
X * make SILENT events PENDING.
X *
X * Goes WAY out of it's way to force i/o to be on the terminal.
X *
X * Returns TRUE if lines were changed.
X */
Xint
Xeditf( what )
Xregister int what;
X{
X register FILE *ifp;
X register FILE *ofp;
X register NAGLINE *lp;
X register EVENT *ep;
X register int changed = FALSE;
X
X char buf[ 80 ];
X
X if( ( ifp = efopen( "/dev/tty", "r" ) ) == NULL )
X return( changed );
X
X if( ( ofp = efopen( "/dev/tty", "w" ) ) == NULL )
X return( changed );
X
X setbuf( ofp, NULL ); /* force output to be unbuffered */
X
X for( lp = Flist; lp ; lp = lp->next )
X {
X if( lp->type == what )
X {
X /* only display events on the queue within 12 hours */
X
X for( ep = Evq; ep && ep->lp != lp; ep = ep->next )
X continue;
X
X if( !ep || ep->etime > Now + (HRSECS * 12) )
X continue;
X
X (void) fprintf( ofp, "Silence %s: %s (y/n/q)? ",
X lp->timestr, lp->cmd ) ;
X
X if( fgets( buf, sizeof(buf), ifp ) == NULL )
X break;
X
X if( buf[ 0 ] == 'y' || buf[ 0 ] == 'Y' )
X {
X lp->type = ( what == PENDING ) ? SILENT : PENDING;
X changed = TRUE;
X }
X
X /* stop querying if a 'q' is entered */
X
X if( buf[ 0 ] == 'q' || buf[ 0 ] == 'Q' )
X break;
X }
X }
X (void) fclose( ifp );
X (void) fclose( ofp );
X return ( changed );
X}
X
X
X/*----------------
X *
X * writef() -- Write the file back out after a change.
X *
X * Returns TRUE if file wrote OK.
X */
Xint
Xwritef()
X{
X char buf[ 80 ];
X
X register int err;
X register FILE *fp;
X register NAGLINE *lp;
X
X DPRINTF(stderr, "Writing %s\n", Nagfile );
X
X if( ( fp = efopen( Nagfile, "w" ) ) == NULL )
X return (FALSE);
X
X err = 0;
X for( lp = Flist; lp && err >= 0 ; lp = lp->next )
X {
X switch( lp->type )
X {
X case BAD:
X case COMMENT:
X err = fprintf( fp, "%s\n", lp->line );
X break;
X default:
X err = fprintf( fp, "%c%s %s %s %s\n",
X lp->type == SILENT ? SILCHAR : ' ',
X lp->datestr,
X lp->timestr,
X lp->intstr,
X lp->cmd );
X break;
X }
X }
X
X if( err < 0 )
X {
X DPRINTF( stderr, "err %d\n", err );
X (void) sprintf( buf, "%s: error writing %s", Myname, Nagfile );
X perror( buf );
X }
X else if( (err = fclose( fp ) ) < 0 )
X {
X (void) sprintf( buf, "%s: error closing %s", Myname, Nagfile );
X perror( buf );
X return( FALSE );
X }
X return ( err >= 0 );
X}
X
X
X/*----------------
X *
X * parseline() -- Split text into a NAGLINE more amenable to processing.
X *
X * Returns TRUE with the NAGLINE all set up if parsed OK.
X * Returns FALSE with the line->type set to BAD,
X * and line->errtype set if undecipherable.
X *
X *
X * in the code, buf points to the first character not processed,
X * cp points to the last character examined.
X *
X * cp places nulls in likely places.
X *
X * This is a very ugly function and should be rewritten.
X */
Xint
Xparseline( buf, lp )
Xregister char *buf;
Xregister NAGLINE *lp;
X{
X register char *cp;
X register int today;
X register int i;
X time_t d;
X time_t t;
X int anyday;
X struct tm ntm; /* now tm struct */
X struct tm dtm; /* date tm struct */
X struct tm ttm; /* time tm struct */
X
X anyday = FALSE;
X lp->line = strcpy( emalloc( strlen( buf ) + 1 ), buf );
X
X /*
X * determine line type, and advance buf to first non-blank after
X * the status field
X */
X
X switch (*buf)
X {
X case COMCHAR:
X lp->type = COMMENT;
X return TRUE;
X /*NOTREACHED*/
X
X case SILCHAR:
X lp->type = SILENT;
X buf++;
X break;
X
X default:
X lp->type = PENDING;
X break;
X }
X
X /* skip to non-whitespace */
X
X while( *buf && isspace(*buf))
X buf++;
X
X /* empty line isn't fatal (it's a comment) */
X
X if (!*buf) {
X lp->type = BAD;
X lp->errtype = EMPTY;
X lp->err = buf;
X return TRUE;
X }
X
X /* bracket the day/date, and null terminate it */
X
X for( cp = buf; *cp && !isspace( *cp ); cp++ )
X continue;
X if( *cp ) *cp++ = '\0';
X else *cp = '\0';
X
X /* cp now positioned at char past null, or on null at the end */
X
X /*
X * buf points at the day field; figure out the
X * absolute time of "Midnight" of the right day for the event.
X */
X lp->datestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
X
X /* figure when midnight of today was */
X
X ntm = *localtime( &Now );
X ntm.tm_sec = 0;
X ntm.tm_min = 0;
X ntm.tm_hour = 0;
X
X if (*buf == '*')
X {
X anyday = TRUE;
X dtm = ntm;
X }
X else
X {
X
X /* parse date */
X
X if( NULL != gdate( buf, &dtm ) )
X {
X DPRINTF(stderr, "not a date, maybe a day\n");
X
X /* maybe it's a day name... */
X
X lowcase( buf );
X if( (i = find( buf, dow )) >= 0 )
X {
X i--;
X today = ntm.tm_wday;
X DPRINTF(stderr, "today %s, event %s\n",
X dow[ today ],
X dow[ i ] );
X if( i < today )
X i += 7; /* it's next week */
X d = Now + (( i - today ) * HRSECS * 24 );
X dtm = *localtime( &d );
X dtm.tm_sec = 0;
X dtm.tm_min = 0;
X dtm.tm_hour = 0;
X }
X else
X {
X DPRINTF(stderr, "find of %s in dow returned %d\n", buf, i );
X lp->type = BAD;
X lp->errtype = DATEBAD;
X lp->err = buf;
X return FALSE;
X }
X }
X }
X
X d = tm_to_time( &dtm );
X DPRINTF(stderr, "parseline: date %s\n", nctime(&d) );
X
X /* advance to time */
X
X for( buf = cp ; *buf && isspace(*buf); buf++) /* skip blanks */
X continue;
X
X if (!*buf) {
X lp->type = BAD;
X lp->errtype = NOTIME;
X lp->err = buf;
X return FALSE;
X }
X
X /* bracket the time */
X
X for( cp = buf; *cp && !isspace( *cp ); cp++ )
X continue;
X if( *cp ) *cp++ = '\0';
X else *cp = '\0';
X
X /*
X * buf now at time field, figure offset until event,
X * then fill in absolute time.
X *
X * gtime can't fail -- it will say it's 00:00 if it
X * doesn't understand.
X */
X DPRINTF(stderr, "parseline: time buf %s\n", buf );
X lp->timestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
X (void) gtime( buf, &ttm );
X t = (ttm.tm_hour * HRSECS) + (ttm.tm_min * 60);
X lp->atime = d + t;
X
X /*
X ** If past the event, and it's for any day, do it tomorrow.
X ** BUG: This breaks if there is an interval after the event
X ** This is a rare case, and I haven't yet thought of a clean fix.
X */
X if( anyday && lp->atime < Now )
X lp->atime += HRSECS * 24;
X
X DPRINTF(stderr, "parseline: time offset %s is %d seconds, %02d:%02d\n",
X buf, t, t / HRSECS, t % HRSECS );
X DPRINTF(stderr, "parseline: etime %s\n", nctime(&lp->atime));
X
X /* advance to intervals */
X
X for( buf = cp; *buf && isspace(*buf); buf++)
X continue;
X
X if (!*buf)
X {
X lp->type = BAD;
X lp->errtype = NOINTERVALS;
X lp->err = buf;
X return FALSE;
X }
X
X /* bracket the intervals */
X
X for( cp = buf; *cp && !isspace( *cp ); cp++ )
X continue;
X if( *cp ) *cp++ = '\0';
X else *cp = '\0';
X
X /* save the interval string. */
X
X lp->intstr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
X
X /* take rest of the line as the command */
X
X if (!*cp)
X {
X lp->type = BAD;
X lp->errtype = NOCMD;
X lp->err = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
X return FALSE;
X }
X
X lp->cmd = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
X
X return TRUE;
X}
X
X
X/*----------------
X *
X * zaplines() -- delete all NAGLINEs and free their space
X *
X */
Xvoid
Xzaplines()
X{
X register NAGLINE *lp;
X register NAGLINE *nlp;
X
X for( lp = Flist; lp ; lp = nlp )
X {
X nlp = lp->next;
X
X if( lp->line )
X free(lp->line);
X if( lp->datestr )
X free(lp->datestr);
X if( lp->timestr )
X free(lp->timestr);
X if( lp->intstr )
X free(lp->intstr);
X if( lp->cmd )
X free(lp->cmd);
X
X free( lp );
X }
X Flast = Flist = NULL;
X}
X
X
X/*----------------
X *
X * buildq() -- Rebuild the event queue if the .nag file has changed.
X *
X */
Xvoid
Xbuildq()
X{
X register NAGLINE *lp;
X
X DPRINTF(stderr, "buildq: rebuilding the event queue\n");
X
X zapq();
X
X for( lp = Flist; lp; lp = lp->next )
X {
X /* add events for silenced lines too. */
X if( lp->type != COMMENT )
X addevents( lp );
X }
X
X sortq();
X
X if(Debug)
X showevents( "Event queue after rebuild and sort\n" );
X}
X
X
X/*----------------
X *
X * zapq() -- Destroy an event queue, setting the head back to NULL.
X *
X * Only the actual element is freed.
X */
Xvoid
Xzapq()
X{
X register EVENT *this;
X register EVENT *next;
X
X for ( this = Evq; this ; this = next )
X {
X next = this->next;
X free( this );
X }
X Evq = NULL;
X}
X
X/*----------------
X *
X * insq() -- Add a new EVENT to the head of a queue.
X *
X */
Xvoid
Xinsq( etime, offset, lp )
Xtime_t etime;
Xregister int offset;
XNAGLINE *lp;
X{
X register EVENT *ep;
X
X etime += (offset * 60);
X
X /* add events after last time we ran, but no more than 24 hours
X in the future */
X
X if( ( etime >= Now || ( Last && etime > Last ) )
X && etime < ( Now + ( HRSECS * 24 ) ) )
X {
X DPRINTF(stderr, "insq: Adding %s at %s\n", lp->cmd, nctime(&etime) );
X }
X else /* too late */
X {
X DPRINTF(stderr, "insq: Dropping %s at %s\n", lp->cmd, nctime(&etime) );
X return;
X }
X
X /*ALIGNOK*/
X ep = (EVENT *) emalloc( sizeof(*ep) );
X ep->etime = etime;
X ep->offset = offset;
X ep->lp = lp;
X
X /* splice into the head of the queue */
X ep->next = Evq; /* NULL, if last event */
X Evq = ep;
X}
X
X
X/*----------------
X *
X * addevents() -- Add pending events for the NAGLINE to the queue.
X *
X * Events in the past are not considered.
X * If the command has been silenced, don't do the command.
X *
X */
Xvoid
Xaddevents( lp )
Xregister NAGLINE *lp;
X{
X register char *cp; /* ptr into the interval string */
X int offset; /* offset in minutes */
X
X /* for every numeric value in the interval string... */
X
X for( cp = lp->intstr; cp && *cp ; cp = index( cp, ':' ) )
X {
X if (*cp == ':') /* skip past optional ':' */
X cp++;
X if (!*cp) /* ignore trailing ':' */
X return;
X
X /* read (possibly) signed interval value */
X
X if( 1 != sscanf( cp, "%d", &offset ) )
X {
X (void) fprintf(stderr, "%s: bad intervals '%s'\n", Myname,
X lp->intstr );
X return;
X }
X insq( lp->atime, offset, lp );
X }
X}
X
X
X
X/*----------------
X *
X * timecmp() -- Compare time of two events.
X *
X * Made slightly tricky since it must return an int, not a time_t.
X *
X */
Xint
Xtimecmp( a, b )
Xregister EVENT **a;
Xregister EVENT **b;
X{
X time_t val = (*a)->etime - (*b)->etime;
X
X return( val < 0 ? -1 : val > 0 );
X}
X
X
X/*----------------
X *
X * sortq() -- Sort the event queue into chronological order.
X *
X * 1. Create an array of pointers to the events in the queue.
X * 2. Sort the array by time of the pointed-to events.
X * 3. Rebuild the queue in the order of the array.
X *
X */
Xvoid
Xsortq()
X{
X register unsigned int n; /* number of events in the queue */
X register unsigned int i; /* handy counter */
X register EVENT **events; /* allocated array of EVENT ptrs */
X register EVENT **ap; /* ptr into allocated events */
X register EVENT *ep; /* pointer in event chain */
X
X forward int timecmp();
X
X n = 0;
X for( ep = Evq; ep; ep = ep->next )
X n++;
X
X DPRINTF(stderr, "sortq: %d events\n", n );
X
X if ( n < 2 )
X return;
X
X /* build array of ptrs to events */
X
X /*ALIGNOK*/
X ap = events = (EVENT **) ecalloc( (unsigned)sizeof(**ap), n );
X
X /* build array of ptrs to events */
X for( ep = Evq; ep; ep = ep->next )
X *ap++ = ep;
X
X /* sort by ascending time */
X (void) qsort( events, (unsigned)n, sizeof(*events), timecmp );
X
X /* rechain the event queue from the sorted array */
X Evq = ep = events[0];
X for ( i = 0 ; i < n ; )
X {
X ep->next = events[i++];
X ep = ep->next;
X }
X ep->next = NULL;
X
X free( events );
X}
X
X
X/*----------------
X *
X * runq() -- Execute all events that are due.
X *
X * Sleep until the next scheduled event. If there are none, or
X * next is far away, sleep for MINSLEEP and try again.
X *
X */
Xvoid
Xrunq()
X{
X char cmd[ 5120 ];
X char now[ CTIMELEN ];
X register EVENT *evq; /* standin for global Evq in loop */
X register EVENT *ep; /* next event */
X register NAGLINE *lp;
X int dsecs;
X
X DPRINTF(stderr, "runq start at %s\n", Nowstr );
X
X evq = Evq; /* fast access, be sure to save back */
X
X /*
X * Execute commands that are due.
X *
X * Keeps head of the queue current by cutting out events as
X * they are processed.
X *
X * The loop breaks out when the queue is gobbled up,
X * or we get to an event that is not due now.
X */
X
X while( evq && evq->etime <= Now )
X {
X lp = evq->lp;
X
X DPRINTF(stderr, "due at %s:\n", nctime( &evq->etime ) );
X
X /* Run a PENDING event */
X
X if( lp->type == PENDING && lp->cmd )
X {
X (void)strcpy( now, &Nowstr[ 11 ] );
X now[ 5 ] = '\0';
X
X (void)sprintf( cmd, "pretime=%d;posttime=%d;now=%s;then=%s;%s\n",
X -evq->offset,
X evq->offset,
X now,
X nhour( &lp->atime ),
X lp->cmd );
X
X DPRINTF(stderr, "executing:\n%s\n", cmd );
X if( system( cmd ) )
X (void) fprintf( stderr, "%s: Trouble running\n'%s'\n",
X Myname, cmd );
X }
X
X /* if it's a SILENT event, is it time to make it PENDING? */
X
X if( lp->type == SILENT )
X {
X /* find the queue end or the next event for the line */
X
X for( ep = evq->next ; ep && ep->lp != lp ; ep = ep->next )
X continue;
X
X /* if match, or it was the last in the queue, turn it on */
X
X if ( ep )
X {
X DPRINTF(stderr, "SILENT event\n");
X }
X else
X {
X DPRINTF(stderr, "Last SILENT event, making PENDING again.\n");
X lp->type = PENDING;
X
X /*
X * if the write fails, keep going and hope the user fixes
X * the nag file. If we exit, the daemon would need
X * to be restarted by hand. Since it won't do anything
X * but sleep and exit when the user logs off, no harm
X * is done by sticking around.
X */
X (void) writef();
X }
X }
X ep = evq->next;
X free( evq );
X evq = ep;
X } /* for events on the queue */
X
X dsecs = evq ? min( evq->etime - Now, MINSLEEP) : MINSLEEP;
X
X DPRINTF(stderr, "sleeping for %d seconds, next %s\n",
X dsecs,
X evq ? nctime( &evq->etime ) : "never" );
X
X Evq = evq; /* back to global var */
X
X delay( dsecs );
X}
X
X
X/*----------------
X *
X * emalloc() -- malloc with error msg.
X *
X */
Xchar *
Xemalloc( size )
Xregister int size;
X{
X register char *ptr;
X extern char *malloc();
X
X if ( ( ptr = malloc( (unsigned) size ) ) == NULL )
X {
X (void) fprintf(stderr, "%s: Can't malloc %d bytes\n", Myname, size );
X exit(1);
X }
X return( ptr );
X}
X
X/*----------------
X *
X * ecalloc() -- calloc with error message.
X *
X */
Xchar *
Xecalloc( n, size )
Xregister unsigned int n;
Xregister unsigned int size;
X{
X register char *ptr;
X extern char *calloc();
X
X if ( ( ptr = calloc( (unsigned) size, n ) ) == NULL )
X {
X (void) fprintf(stderr, "%s: Can't calloc %d bytes\n", Myname, size * n);
X exit(1);
X }
X return( ptr );
X}
X
X/*
X * efopen() -- fopen with error message on failure (no fatal error)
X */
XFILE *
Xefopen( file, mode )
Xchar *file;
Xchar *mode;
X{
X char buf [ 80 ];
X register FILE * fp;
X
X if( (fp = fopen( file, mode )) == NULL )
X {
X (void)sprintf( buf, "%s: can't open file %s with mode \"%s\"",
X Myname, file, mode );
X perror( buf );
X }
X return( fp );
X}
X
X
X/*
X * showline() -- Dump the line list.
X */
Xvoid
Xshowlines( msg )
Xchar *msg;
X{
X register NAGLINE *lp;
X
X (void) fprintf(stderr, "%s", msg );
X for( lp = Flist; lp ; lp = lp->next )
X dumpline( lp );
X}
X
X/*
X * dumpline() -- dump a NAGLINE for debugging.
X */
Xvoid
Xdumpline( lp )
Xregister NAGLINE *lp;
X{
X if( lp == NULL )
X {
X (void) fprintf(stderr, "dumpline: NULL lp\n");
X return;
X }
X (void) fprintf(stderr, "\nline (%s):\n%s\n", linetypes[ lp->type ],
X lp->line );
X switch( lp->type )
X {
X case BAD:
X (void) fprintf(stderr, "%s %s\n", parserrs[ lp->errtype ], lp->err );
X break;
X
X case PENDING:
X case SILENT:
X (void) fprintf(stderr, "The event is at %s\n", nctime( &lp->atime ));
X }
X}
X
X/*
X * showevents() -- dump the event list, for debugging.
X */
Xvoid
Xshowevents( msg )
Xchar *msg;
X{
X register EVENT *ep;
X
X (void) fprintf(stderr, "%s", msg );
X for( ep = Evq; ep; ep = ep->next )
X dumpevent( ep );
X}
X
X/*
X * dumpevent() -- print an event, for debugging.
X */
Xvoid
Xdumpevent( ep )
Xregister EVENT *ep;
X{
X if( ep == NULL )
X (void) fprintf(stderr, "dumpevent: NULL ep\n");
X else
X (void) fprintf(stderr, "event 0x%x, next 0x%x offset %d time %s\n",
X ep, ep->next, ep->offset, nctime(&ep->etime) );
X}
X
X/*
X * nctime() -- ctime with trailing '\n' whacked off.
X */
Xchar *
Xnctime( t )
Xtime_t *t;
X{
X register char *cp;
X
X cp = ctime( t );
X cp[ strlen( cp ) - 1 ] = '\0';
X return ( cp );
X}
X
X/*
X * nhour() -- return an hh:mm string given a pointer to a time_t.
X */
Xchar *
Xnhour( t )
Xtime_t *t;
X{
X register char *buf = ctime( t );
X
X /*
X * 012345678901234567890123
X * Wed Dec 31 16:00:00 1969
X */
X
X buf[ 16 ] = '\0';
X return ( &buf[ 11 ] );
X}
X
X
X/*----------------
X *
X * delay() -- like sleep but knows what 0 means.
X *
X * If user logs out, notices and exit with OK status.
X *
X */
Xvoid
Xdelay( secs )
Xint secs;
X{
X char thislogin[20];
X
X if( secs > 0)
X {
X (void) sleep( (unsigned) secs );
X (void) strcpy(thislogin, getlogin());
X if ( strcmp(Origlogin, thislogin) )
X exit(0);
X }
X}
X
X/*
X * lowcase() -- make a string all lower case.
X */
Xvoid
Xlowcase( s )
Xchar *s;
X{
X while ( *s )
X {
X if( isupper( *s ) )
X *s = tolower( *s );
X s++;
X }
X}
X
X# if 0
X
X/*
X * dumptm() -- show contents of a tm structure.
X */
Xdumptm( tm )
Xstruct tm *tm;
X{
X (void) fprintf(stderr, "year : %d month: %d day: %d\n",
X tm->tm_year,tm->tm_mon,tm->tm_mday);
X (void) fprintf(stderr, "day of month: %d hour: %d minute: %d second: %d\n",
X tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec) ;
X (void) fprintf(stderr, "day of year: %d day of week: %d dst: %d\n",
X tm->tm_yday, tm->tm_wday, tm->tm_isdst) ;
X}
X
X# endif
X
X/* end of nag.c */
X
X
SHAR_EOF
if test 29846 -ne "`wc -c 'nag.c'`"
then
echo shar: error transmitting "'nag.c'" '(should have been 29846 characters)'
fi
# End of shell archive
exit 0
More information about the Mod.sources
mailing list