[alt.sources] REMIND 2.2 03/05

dfs@doe.carleton.ca (David F. Skoll) (11/17/90)

#!/bin/sh
# This is part 03 of Remind-2.2
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= main.c ==============
if test X"$1" != X"-c" -a -f 'main.c'; then
	echo "File already exists: skipping 'main.c'"
else
echo "x - extracting main.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > main.c &&
X/***************************************************************/
X/*                                                             */
X/*  REMIND - version 2.2                                       */
X/*                                                             */
X/*  By David Skoll - 13 Novemeber 1990                         */
X/*                                                             */
X/*  (C) 1990 by David Skoll - all rights reserved              */
X/*                                                             */
X/***************************************************************/
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#ifndef UNIX
X#include <stdlib.h>
X#include <dos.h>
X#include <stdarg.h>
X#else
X#include <varargs.h>
X#include <sys/types.h>
X#ifdef SYSV
X#include <time.h>
X#else
X#include <sys/time.h>
X#endif
X#endif
X#include "defines.h"
X#include "protos.h"
X
X
X/* List of months */
Xchar *MonthName[] = {
X   "January", "February", "March", "April", "May", "June",
X   "July", "August", "September", "October", "November", "December"
X};
X
X/* List of weekdays */
Xchar *DayName[] = {
X   "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
X};   
X			   
X/* List of recognized tokens */
X
XToken keywd[] = {
X   { "BANNER",    Banner_t, 0 },
X   { "OMIT",      Omit_t,   0 },
X   { "REM",       Rem_t,    0 },
X   { "January",   Month_t,  0 },
X   { "February",  Month_t,  1 },
X   { "March",     Month_t,  2 },
X   { "April",     Month_t,  3 },
X   { "May",       Month_t,  4 },
X   { "June",      Month_t,  5 },
X   { "July",      Month_t,  6 },
X   { "August",    Month_t,  7 },
X   { "September", Month_t,  8 },
X   { "October",   Month_t,  9 },
X   { "November",  Month_t,  10 },
X   { "December",  Month_t,  11 },
X   { "Monday",    WkDay_t,  0  },
X   { "Tuesday",   WkDay_t,  1  },
X   { "Wednesday", WkDay_t,  2  },
X   { "Thursday",  WkDay_t,  3  },
X   { "Friday",    WkDay_t,  4  },
X   { "Saturday",  WkDay_t,  5  },
X   { "Sunday",    WkDay_t,  6  },
X   { "MSG",       Msg_t,    0 },
X   { "RUN",       Run_t,    0 },
X   { "ONCE",      Once_t,   0 },
X   { "INCLUDE",   Include_t, 0},
X   { "AT",        At_t,      0}
X};
X
X/* List of days in month - Feb MUST be 29 for CheckDate to work. */
Xint MonthDays[] = {
X   31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
X
X/* Index of the first day of each month.  First array is non-leap-year;
X   second is leap-year. */
Xint MonthIndex[2][12] = {
X   { 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};
X
X/* Global ommissions array */
Xint FullOmitArray[FOMITSIZE];
X
X/* Global partial omissions array */
Xint PartOmitArray[POMITSIZE];
X
X/* Define the working buffers */
Xchar Line[512], WorkBuf[512];
Xchar TmpBuf[512];
Xint Fresh;  /* True if the contents of Line are fresh */
X
X/* Global variables */
Xint Purge, Debug, Verbose, IgRun, IgOnce;
Xint LastRun;
Xchar FileName[200];
Xint CurLine;
Xchar Banner[200] = "Reminders for %w, %d%s %m, %y%o:";
Xint NumEmitted, NumRem;
Xint NumFullOmit, NumPartOmit;
Xint JulianToday, RealToday;
Xint CurYear, CurMon, CurDay;
Xint QueueAts, PrintAts, NumAtsQueued;
Xint Calendar, CalTime, CalWidth, SimpleCalendar;
X
Xstatic int JulFirst; /* Julian date of 1 Jan Current_year */
Xstatic int FirstYear;
X#ifndef UNIX
Xint int_comp(int *a, int *b) { return *a - *b; }
X#else
Xint int_comp(a, b) 
X   int *a;
X   int *b;
X{ return *a - *b; }
X#endif
X
X/***************************************************************/
X/*                                                             */
X/* Output                                                      */
X/* Output a string with line separators.                       */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xvoid Output(char *s)
X#else
Xvoid Output(s)
Xchar *s;
X#endif
X{
X   while (*s) {
X      if (*s == '\n') putchar('\\');
X      putchar(*s++);
X   }
X   putchar('\n');
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int MoveBack(...)                                          */
X/*                                                             */
X/*  Move back by specified number of days, skipping holidays   */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint MoveBack (int jul, int back, int d, int m, int y, int omit)
X#else
Xint MoveBack (jul, back, d, m, y, omit)
X     int jul;
X     int back;
X     int d;
X     int m;
X     int y;
X     int omit;
X#endif
X{
X   int temp;
X
X   if (!NumFullOmit && !NumPartOmit && !omit) return jul - back;
X   while (back) {
X      jul--;
X      if (omit & 1 << (jul % 7)) continue; /* Omitted due to weekday */
X
X      /* Omit if in fully-specified omit list */
X      if (NumFullOmit && bsearch(&jul, FullOmitArray, NumFullOmit, sizeof(int), int_comp)) continue;
X
X      if (NumPartOmit) {
X	 d--;
X	 if (d == 0) {
X	    m--;
X	    if (m < 0) {
X	       y--;
X	       m = 11;
X	    }
X	    d = DaysInMonth(m, y);
X	 }
X	 temp = (m << 5) + d;
X	 if (bsearch(&temp, PartOmitArray, NumPartOmit, sizeof(int), int_comp)) continue;
X      }
X      back--;
X   }
X   return jul;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int ProcessLine()                                          */
X/*                                                             */
X/*  Process the line in the "Line" buffer.                     */
X/*                                                             */
X/*  Normally returns 0.  Returns 1 only if we're in Calendar   */
X/*  mode and we hit a reminder which should be placed in the   */
X/*  calendar.                                                  */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint ProcessLine(void)
X#else
Xint ProcessLine()
X#endif
X{
X   char *s = Line;
X   Token tok;
X   int i;
X
X   while (isspace(*s)) s++;
X
X   /* Skip comments and blank lines */
X   if (*s == '#' || *s == 0) {
X      if (Purge && TopLevel()) Output(Line);
X      return 0;
X   }
X
X   tok = ParseToken(&s);
X   switch(tok.type) {
X      case Banner_t: DoBanner(&s);
X		     if (Purge && TopLevel()) Output(Line);
X		     break;
X
X      case Omit_t:   i = DoGlobalOmit(&s);
X		     if (Purge && TopLevel())
X			if (i < 0) Eprint("Purged '%s'\n", Line);
X			else       Output(Line);
X		     break;
X
X      case Rem_t:    i = DoRem(&s);
X	             if (Calendar) return i;
X		     if (Purge && TopLevel())
X			if (i < 0) Eprint("Purged '%s'\n", Line);
X			else       Output(Line);
X		     NumRem++;
X		     break;
X
X      case Include_t: if (Purge && TopLevel()) Output(Line);
X		      DoInclude(&s);
X		      break;
X		       
X
X      default:       if (Purge && TopLevel()) Output(Line);
X		     Eprint("Unknown command '%s'\n", tok.str);
X   }
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X#ifndef UNIX
X/*  void Eprint(const char *f, ...)                            */
X#else
X/*  void Eprint(va_alist)                                      */
X#endif
X/*                                                             */
X/*  Prints an error message.                                   */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xvoid Eprint(const char *f, ...)
X#else
X/*VARARGS0*/
Xvoid Eprint(va_alist)
Xva_dcl
X#endif
X{
X#ifndef UNIX
X   va_list args;
X#else
X  va_list args;
X  char *f;
X#endif
X
X#ifndef UNIX
X   if (Verbose & Fresh) {
X#else
X  if (Verbose & Fresh) {
X#endif
X      fprintf(stderr, "\n--- %s\n", Line);
X      Fresh = 0;
X   }
X   if (Verbose) fprintf(stderr, "--- ");
X   fprintf(stderr, "%s(%d): ", FileName, CurLine);
X#ifndef UNIX
X   va_start(args, f);
X#else
X   va_start(args);
X   f = va_arg(args, char *);
X#endif
X   vfprintf(stderr, f, args);
X#ifdef UNIX
X   va_end(args);
X#endif
X}
X
X
X/***************************************************************/
X/*                                                             */
X/*  int DoBanner(char **s)                                     */
X/*                                                             */
X/*  Sets the "Reminders for..." banner.                        */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint DoBanner(char **s)
X#else
Xint DoBanner(s)
X     char **s;
X#endif
X{
X   if (Purge) return 0;
X   while (isspace(**s)) (*s)++;
X   strcpy(Banner, *s);
X   if (! *Banner)
X   {
X      if (Debug) Eprint("Empty banner.\n");
X      strcpy(Banner, "Reminders for %w, %d%s %m, %y%o:");
X   }
X   if (NumRem && Debug) Eprint("Warning: Banner after reminder.\n");
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/* int CheckDate(int d, int m, int y)                          */
X/*                                                             */
X/* Checks that a date is valid - returns 0 for OK, 1 for BAD.  */
X/*                                                             */
X/* If y=-1, just checks that month & day are valid, giving     */
X/* benefit of the doubt for February 29.                       */
X/*                                                             */
X/* No point in checking if month is valid - months are named   */
X/* and thus a month out of range can never be entered into     */
X/* the system.                                                 */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint CheckDate(int d, int m, int y)
X#else
Xint CheckDate(d, m, y)
X     int d;
X     int m;
X     int y;
X#endif
X{
X   if (y == -1)
X      if (d > 0 && d <= MonthDays[m]) return 0; else return 1;
X   else
X      if (y < BASE || y > BASE + 85) return 1;
X      else if (d > 0 && d <= DaysInMonth(m, y)) return 0; else return 1;
X}
X
X
X/***************************************************************/
X/*                                                             */
X/*  int DoGlobalOmit(char **s)                                 */
X/*                                                             */
X/*  Add an entry to the global ommissions array.  Either       */
X/*  a fully-specified date, or a mm-yy type date.              */
X/*  Return 0 if OK, -1 if date is in past, 1 if problem.       */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint DoGlobalOmit(char **s)
X#else
Xint DoGlobalOmit(s)
X     char **s;
X#endif
X{
X   int d = -1, m = -1, y = -1;
X   int omit;
X   int *ptr;
X   Token tok;
X
X   tok.type = Unknown_t;
X   while(tok.type != Eol_t) {
X      tok = ParseToken(s);
X      switch (tok.type) {
X	 case Year_t:  y = tok.val; break;
X	 case Month_t: m = tok.val; break;
X	 case Day_t:   d = tok.val; break;
X	 case Eol_t:   break;
X	 default:      Eprint("Invalid token '%s' for OMIT command.\n", tok.str);
X		       return 1;
X      }
X   }
X
X   if (d == -1 || m == -1 || CheckDate(d, m, y)) {
X      Eprint("Invalid date specification.\n");
X      return 1;
X   }
X
X   if (y == -1) { /* Only mm-dd specified */
X      if (NumPartOmit == POMITSIZE) {
X	 Eprint("Too many partially-specified OMITs.\n");
X	 return 1;
X      }
X      omit = (m << 5) + d;
X      ptr = PartOmitArray + NumPartOmit;
X      NumPartOmit++;
X      while (ptr > PartOmitArray && *(ptr-1) > omit) {
X	 *ptr = *(ptr-1);
X	 ptr--;
X      }
X      *ptr = omit;
X
X      /* Check if the bonehead already has it - if so, delete it */
X      if (ptr > PartOmitArray && *(ptr-1) == *ptr) {
X	 if (Debug) Eprint("Duplicated partially-specified OMIT.\n");
X	 NumPartOmit--;
X	 while (ptr < NumPartOmit+PartOmitArray) {
X	    *ptr = *(ptr + 1);
X	    ptr++;
X	 }
X      }
X   } else { /* All three specified */
X      if (NumFullOmit == FOMITSIZE) {
X	 Eprint("Too many fully-specified OMITs.\n");
X	 return 1;
X      }
X      omit = Julian(d, m, y);
X      if (omit < JulianToday) {
X	 if (Debug) Eprint("Omit has expired.\n");
X	 return -1;
X      }
X
X      ptr = FullOmitArray + NumFullOmit;
X      NumFullOmit++;
X      while (ptr > FullOmitArray && *(ptr-1) > omit) {
X	 *ptr = *(ptr-1);
X	 ptr--;
X      }
X      *ptr = omit;
X
X      /* Check if the bonehead already has it - if so, delete it */
X      if (ptr > FullOmitArray && *(ptr-1) == *ptr) {
X	 if (Debug) Eprint("Duplicated fully-specified OMIT.\n");
X	 NumFullOmit--;
X	 while (ptr < NumFullOmit+FullOmitArray) {
X	    *ptr = *(ptr + 1);
X	    ptr++;
X	 }
X      }
X   }
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int strnicmp(char *s1, char*s1, int n)                     */
X/*                                                             */
X/*  Compares first n chars of string ignoring case.            */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint strncmpi(char *s1, char *s2, int n)
X#else
Xint strncmpi(s1, s2, n)
X     char *s1;
X     char *s2;
X     int n;
X#endif
X{
X   register int u1, u2;
X   while (n)
X   {
X      if (!*s1 || !*s2) return upper(*s1) - upper(*s2);
X      u1 = upper(*s1);
X      u2 = upper(*s2);
X      if (u1 != u2) return (u1 - u2);
X      n--;
X      s1++;
X      s2++;
X   }
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  ParseToken(char **s);                                      */
X/*                                                             */
X/*  Parse the next token and adjust the character pointer.     */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
XToken ParseToken(char **s)
X#else
XToken ParseToken(s)
X     char **s;
X#endif
X{
X
X   Token tok;
X   char *t = TmpBuf;
X   int i, h, m;
X   int len;
X   char *colon = (char *) NULL;
X
X   *t = 0;
X   tok.str = TmpBuf;
X   
X   /* Skip blank space */
X   while (isspace(**s)) (*s)++;
X
X   /* End of line ? */
X   if (**s == 0) {
X      tok.type = Eol_t;
X      tok.val = 0;
X      return tok;
X   }
X
X   /* Grab a space-delimited token */
X   while (**s != 0 && !isspace(**s)) {
X      *t++ = **s;
X      (*s)++;
X   }
X   *t = 0;
X   len = t - TmpBuf;
X
X   /* Check if it's a built-in token */
X   for (i=0; i < sizeof(keywd)/sizeof(keywd[0]); i++)
X      if (len >= 3 && !strncmpi(TmpBuf, keywd[i].str, len)) return keywd[i];
X
X   /* ARGH.  Check for "AT" as special case: < 3 chars */
X   if (len == 2 && !strncmpi(TmpBuf, "AT", 2)) {
X      tok.type = At_t;
X      return tok;
X   }
X
X   tok.type = Unknown_t;
X
X   /* If it's a comment, ignore the rest of the line */
X   if (*(tok.str) == '#') {
X      tok.type = Eol_t;
X      return tok;
X   }
X
X   /* Check if it's a number (optional +/-/* ahead of number */
X   t = TmpBuf;
X   if (isdigit(*t)) {
X      while (*++t){
X         if (*t == ':') {
X            if (colon) return tok; else colon = t;
X         } else if (!isdigit(*t)) return tok;
X      }
X   }
X   else if (*t == '+' || *t == '-' || *t == '*') {
X      if (!isdigit(*++t)) return tok;
X      while (*++t) if (!isdigit(*t)) return tok;
X   }
X   else return tok;
X
X   /* OK, here we have a number - either a pure number, a delta, a time,
X      back or a repeat marker */
X
X   if (colon) { /* got a time here */
X      h = atoi(TmpBuf);
X      m = atoi(colon + 1);
X      if (h >= 0 && h <= 23 && m >= 0 && m <= 59) {
X         tok.type = Time_t;
X	 tok.val = 60*h+m;
X      }
X      else return tok;
X   }
X   else if (*TmpBuf == '+') {
X      tok.type = Delta_t;
X      tok.val = atoi(TmpBuf + 1);
X   }
X   else if (*TmpBuf == '-') {
X      tok.type = Back_t;
X      tok.val = atoi(TmpBuf + 1);
X   }
X   else if (*TmpBuf == '*') {
X      tok.type = Repeat_t;
X      tok.val = atoi(TmpBuf + 1);
X   }
X   else {
X      tok.val = atoi(TmpBuf);
X      if (tok.val > 0 && tok.val <= 31) tok.type = Day_t;
X      else if (tok.val >= 100) tok.type = Year_t;
X      else {
X	 tok.type = Year_t;
X	 tok.val += 1900;
X      }
X   }
X   return tok;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int FromJulian(int jul, int *d, int *m, int *y)            */
X/*                                                             */
X/*  Convert a date from Julian to normal form.  Returns        */
X/*  0 if conversion ok, -1 otherwise.                          */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint FromJulian(int jul, int *d, int *m, int *y)
X#else
Xint FromJulian(jul, d, m, y)
X     int jul;
X     int *d;
X     int *m;
X     int *y;
X#endif
X{
X   int t;
X
X   if (jul < 0) return 1;
X
X   if (jul >= JulFirst && JulFirst != -1) {
X      *y = FirstYear;
X      jul -= JulFirst;
X   } else *y = BASE;
X
X   *m = 0;
X
X   t = DaysInYear(*y);
X   while (jul >= t) {
X      jul -= t;
X      (*y)++;
X      t = DaysInYear(*y);
X   }
X
X   t = DaysInMonth(*m, *y);
X   while (jul >= t) {
X      jul -= t;
X      (*m)++;
X      t = DaysInMonth(*m, *y);
X   }
X   *d = jul + 1;
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int Julian(d, m, y)                                        */
X/*                                                             */
X/*  Converts a date to the number of days after Jan 1 1990.    */
X/*  Returns -1 if date is before Jan 1 1990.                   */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint Julian(int d, int m, int y)
X#else
Xint Julian(d, m, y)
X     int d;
X     int m;
X     int y;
X#endif
X{
X   int iy;
X   int jul = 0;
X
X   if (y < BASE) return -1;
X   if (JulFirst == -1 || y < FirstYear) 
X      for (iy = BASE; iy < y; iy++) jul += DaysInYear(iy);
X   else {
X      jul = JulFirst;
X      for (iy = FirstYear; iy < y; iy++) jul += DaysInYear(iy);
X   }
X      
X   return jul + MonthIndex[IsLeapYear(y)][m] + d - 1;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  int FindTodaysDate(int *d, int *m, int *y)                 */
X/*                                                             */
X/*  Obtains today's date.  Returns Julian date or -1 for       */
X/*  failure.                                                   */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint FindTodaysDate(int *d, int *m, int *y)
X#else
Xint FindTodaysDate(d, m, y)
X     int *d;
X     int *m;
X     int *y;
X#endif
X{
X#ifndef UNIX
X   struct dosdate_t buf;
X
X   _dos_getdate(&buf);
X
X   *d = buf.day;
X   *m = buf.month - 1;
X   *y = buf.year;
X#else
X  time_t tloc;
X  struct tm *t;
X  
X   (void) time(&tloc);
X   t = localtime(&tloc);
X       
X   *d = t->tm_mday;
X   *m = t->tm_mon;
X   *y = t->tm_year + 1900;
X   
X#endif
X   if (CheckDate(*d, *m, *y)) return -1;
X   return Julian(*d, *m, *y);
X}
X
X#ifndef UNIX
Xint main(int argc, char *argv[])
X#else
Xint main(argc, argv)
X     int argc;
X     char *argv[];
X#endif
X{
X#ifdef UNIX
X#ifdef SYSV
X   pid_t pid;
X#else
X   int pid;
X#endif
X#endif
X
X   NumEmitted = 0;
X   NumRem = 0;
X   JulFirst = -1;  /* Initialize JulFirst so it's not used by Julian */
X      
X   JulianToday = FindTodaysDate(&CurDay, &CurMon, &CurYear);
X   if (JulianToday < 0) {
X      fprintf(stderr, "remind: System date is illegal - Ensure that year is at least %d.\n", BASE);
X      return 1;
X   }
X   
X   RealToday = JulianToday;
X   
X   initialize(argc, argv);
X
X   FirstYear = CurYear;
X   JulFirst = Julian(1, 0, CurYear);  /* Do expensive computation once */
X   FirstYear = CurYear;
X
X   if (Calendar) {
X      DoCalendar();
X      return 0;
X   }
X   while (1) {
X      if (ReadLine()) break;
X      ProcessLine();
X   }
X   if (NumEmitted == 0 && NumAtsQueued == 0 && !Purge && !Debug) 
X       printf("No reminders.\n");
X#ifdef UNIX
X   if (NumEmitted == 0 && NumAtsQueued != 0 && !Purge && !Debug)
X       printf("%d reminder%s queued for later today.\n", NumAtsQueued,
X              (NumAtsQueued == 1) ? "" : "s");
X
X   if (NumAtsQueued) {
X      pid = fork();
X      if (pid == -1) Eprint("Can't fork to perform ATs!\n");
X      if (pid != 0) return 0;
X      HandleQueuedAts();
X   }
X#endif
X}
X/***************************************************************/
X/*                                                             */
X/* SystemTime                                                  */
X/*                                                             */
X/* Returns current system time in seconds past midnight.       */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xlong SystemTime(void)
X#else
Xlong SystemTime()
X#endif
X{
X#ifdef UNIX
X  time_t tloc;
X  struct tm *t;
X  
X   (void) time(&tloc);
X   t = localtime(&tloc);
X   return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L + (long) t->tm_sec;
X
X#else
X   struct dostime_t tloc;
X   _dos_gettime(&tloc);
X   return (long) tloc.hour * 3600L + (long) tloc.minute * 60L + (long) tloc.second;
X#endif
X}
SHAR_EOF
$TOUCH -am 1115165290 main.c &&
chmod 0600 main.c ||
echo "restore of main.c failed"
set `wc -c main.c`;Wc_c=$1
if test "$Wc_c" != "21719"; then
	echo original size 21719, current size $Wc_c
fi
fi
# ============= nextdate.c ==============
if test X"$1" != X"-c" -a -f 'nextdate.c'; then
	echo "File already exists: skipping 'nextdate.c'"
else
echo "x - extracting nextdate.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > nextdate.c &&
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X/***************************************************************/
X/*                                                             */
X/*  int TryNextDate(int *retday, int *retmon, int *retyr,      */
X/*                  int startday, int startmon, int startyr,   */
X/*                  int conday, int conmon, int conyr,         */
X/*                  int wkday, int cons, int inc)              */
X/*                                                             */
X/*  This function tries to find the next date satisfying       */
X/*  the given constraints.  Returns                            */
X/*  0 for success.  Returns non-zero if a constraint would     */
X/*  be violated.  Note that if DAY and WEEKDAY are both        */
X/*  constrained, then MONTH and YEAR may be violated.          */
X/*  Otherwise, all constraints are honoured.                   */
X/*  The starting point for the search is thisday, etc.         */
X/*                                                             */
X/*  If inc is non-zero, then the search begins from the day    */
X/*  after the specified date.  Note that this function assumes */
X/*  that the given date is valid.                              */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint TryNextDate(int *retday, int *retmon, int *retyr,
X		int d, int m, int y,
X		int conday, int conmon, int conyr,
X		int wkday, int cons, int inc)
X#else
Xint TryNextDate(retday, retmon, retyr, d, m, y, conday, conmon, conyr,
X                wkday, cons, inc)
X     int *retday, *retmon, *retyr, d, m, y, conday, conmon, conyr, wkday, cons, inc;
X     
X#endif
X
X{
X   int jul, jul2;
X   int dd = d, mm = m, yy = y;
X
X   if (inc)
X   {
X      d++;
X      if (d > DaysInMonth(m, y)) {
X	 m++; d = 1;
X	 if (m > 11) {
X	    y++; m = 0;
X	 }
X      }
X   }
X
X
X   switch (cons & 15) {
X
X      case 0:  /* No constraints - just use the start date */
X	 *retday = d;
X	 *retmon = m;
X	 *retyr  = y;
X	 return 0;
X
X      case 1: /* Day constrained to be d */
X	 *retday = conday;
X	 if (d > conday) {
X	    m++;
X	    if (m > 11) {y++; m=0;}
X	 }
X	 while (conday > DaysInMonth(m, y)) {
X	    m++;
X	    if (m > 11) {y++; m=0;}
X	 }
X	 *retmon = m;
X	 *retyr = y;
X	 return 0;
X
X      case 2: /* Month constrained to be m */
X	 *retmon = conmon;
X	 if (m > conmon) {y++; d = 1;}
X	 else if (m < conmon) d = 1;
X	 *retday = d;
X	 *retyr = y;
X	 return 0;
X
X      case 3: /* Month and day constrained */
X	 *retmon = conmon;
X	 *retday = conday;
X	 if (m > conmon || (m == conmon && d > conday)) y++;
X	 while (conday > DaysInMonth(conmon, y)) y++;
X	 *retyr = y;
X	 return 0;
X
X      case 4: /* Year alone constrained */
X	 if (y > conyr) return 1;
X	 *retyr = conyr;
X	 if (y < conyr) {
X	    *retmon = 0;
X	    *retday = 1;
X	 }
X	 else {
X	    *retmon = m;
X	    *retday = d;
X	 }
X	 return 0;
X
X      case 5: /* Year and day constrained */
X	 if (y > conyr) return 1;
X	 *retyr = conyr;
X	 *retday = conday;
X	 if (y < conyr) {
X	    *retmon = 0;
X	    return 0;
X	 }
X	 if (d > conday) {
X	   m++;
X	   if (m > 11) return 1;
X	 }
X	 while (conday > DaysInMonth(m, y)) {
X	    m++;
X	    if (m > 11) return 1;
X	 }
X	 *retmon = m;
X	 return 0;
X
X      case 6: /* Year and month constrained */
X	 if (y > conyr || (y == conyr && m > conmon)) return 1;
X	 *retyr = conyr;
X	 *retmon = conmon;
X	 if (y < conyr || (y == conyr && m < conmon)) {
X	    *retday = 1;
X	    return 0;
X	 }
X	 *retday = d;
X	 return 0;
X
X      case 7: /* Year, month and day constrained */
X	 *retday = conday;
X	 *retmon = conmon;
X	 *retyr = conyr;
X	 if (y > conyr || (y == conyr && m > conmon) ||
X	     (y == conyr && m == conmon && d > conday)) return 1;
X	 return 0;
X
X      case 8: /* Only the weekday constrained.  Let's go to Julian mode */
X	 jul = Julian(d, m, y);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 FromJulian(jul, retday, retmon, retyr);
X	 return 0;
X
X      case 9: /* GASP! day and weekday constrained .. bleah! */
X	 /* First, try last month. */
X	 jul2 = Julian(d, m, y);
X	 if (m != 0 || y != BASE)
X	 {
X	    mm--;
X	    if (mm < 0) {yy--; mm = 11;}
X
X	    /* If there are fewer days in month than required, it
X	       can't possibly match.  */
X	    if (conday <= DaysInMonth(mm, yy)) {
X	       jul = Julian(conday, mm, yy);
X	       while (!(wkday & (1 << (jul % 7)))) jul++;
X	       if (jul >= jul2) { /* SUCCESS! */
X		  FromJulian(jul, retday, retmon, retyr);
X		  return 0;
X	       }
X	    }
X	 }
X	 /* Didn't work - try this month */
X	 if (conday <= DaysInMonth(m, y)) {
X	    jul = Julian(conday, m, y);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    if (jul >= jul2) { /* SUCCESS! */
X	       FromJulian(jul, retday, retmon, retyr);
X	       return 0;
X	    }
X	 }
X	 /* Argh!  Try next available month */
X	 mm = m;
X	 yy = y;
X	 do {
X	    mm++;
X	    if (mm > 11) {mm = 0; yy++;}
X	 } while (conday > DaysInMonth(mm, yy));
X	 jul = Julian(conday, mm, yy);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 FromJulian(jul, retday, retmon, retyr);
X	 return 0;
X
X      case 10: /* Month and Weekday constrained */
X	 if (m < conmon) {
X	    jul = Julian(1, conmon, y);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 } else if (m == conmon) {
X	    jul = Julian(d, conmon, y);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    if (*retmon != conmon) {
X	       jul = Julian(1, conmon, y+1);
X	       while (!(wkday & (1 << (jul % 7)))) jul++;
X	       FromJulian(jul, retday, retmon, retyr);
X	    }
X	    return 0;
X	 } else { /* m > conmon */
X	    jul = Julian(1, conmon, y+1);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 }
X
X      case 11: /* Day, Month and Weekday constrained */
X	 jul2 = Julian(d, m, y);
X
X	 /* Blip up to next valid year */
X	 while (conday > DaysInMonth(m, y)) y++;
X
X	 /* Try this year */
X	 jul = Julian(conday, conmon, y);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 if (jul >= jul2) {
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 }
X
X	 /* Must be next year */
X	 jul = Julian(conday, conmon, y+1);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 FromJulian(jul, retday, retmon, retyr);
X	 return 0;
X
X      case 12: /* Weekday and year specified */
X	 if (y > conyr) return 1;
X	 if (y == conyr) {mm = m; dd = d;} else {mm = 0; dd = 1;}
X	 jul = Julian(dd, mm, conyr);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 FromJulian(jul, retday, retmon, retyr);
X	 return 0;
X
X      case 13: /* Weekday, year and day specified */
X	 if (y > conyr+1 || (y > conyr && m>0)) return 1;
X	 jul2 = Julian(d, m, y);
X	 if (y > conyr ) {
X	    jul = Julian(conday, 11, conyr);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    if (jul >= jul2) {
X	       FromJulian(jul, retday, retmon, retyr);
X	       return 0;
X	    }
X	 } else if (y < conyr) {
X	    jul = Julian(conday, 0, conyr);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 }
X	 else {
X	    /* Try last month */
X	    if (m > 0) {
X	       mm = m - 1;
X	       while (conday > DaysInMonth(mm, y)) mm--;
X	       jul = Julian(conday, mm, y);
X	       while (!(wkday & (1 << (jul % 7)))) jul++;
X	       if (jul >= jul2) {
X		  FromJulian(jul, retday, retmon, retyr);
X		  return 0;
X	       }
X	    }
X	    /* Try this month */
X	    if (conday <= DaysInMonth(m,y)) {
X	       jul = Julian(conday, m, y);
X	       while (!(wkday & (1 << (jul % 7)))) jul++;
X	       if (jul >= jul2) {
X		  FromJulian(jul, retday, retmon, retyr);
X		  return 0;
X	       }
X	    }
X	    /* Try next month */
X	    if (m == 11) return 1;
X	    m++;
X	    while (conday > DaysInMonth(m, y)) m++;
X	    jul = Julian(conday, m, y);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 }
X
X      case 14:  /* Weekday, Month and Year specified */
X	 if (y > conyr || (y == conyr && m > conmon)) return 1;
X	 if (conyr > y || (conyr == y && conmon > m)) {
X	    jul = Julian(1, conmon, conyr);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    return 0;
X	 } else {
X	    jul = Julian(d, m, y);
X	    while (!(wkday & (1 << (jul % 7)))) jul++;
X	    FromJulian(jul, retday, retmon, retyr);
X	    if (*retmon == conmon) return 0; else return 1;
X
X	 }
X
X      case 15: /* Weekday, day, month and year specified */
X	 jul2 = Julian(d, m, y);
X	 jul = Julian(conday, conmon, conyr);
X	 while (!(wkday & (1 << (jul % 7)))) jul++;
X	 FromJulian(jul, retday, retmon, retyr);
X	 if (jul < jul2) return 1;
X	 return 0;
X   }
X}
SHAR_EOF
$TOUCH -am 1115093690 nextdate.c &&
chmod 0600 nextdate.c ||
echo "restore of nextdate.c failed"
set `wc -c nextdate.c`;Wc_c=$1
if test "$Wc_c" != "8637"; then
	echo original size 8637, current size $Wc_c
fi
fi
# ============= protos.h ==============
if test X"$1" != X"-c" -a -f 'protos.h'; then
	echo "File already exists: skipping 'protos.h'"
else
echo "x - extracting protos.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > protos.h &&
X
X/* Function Prototypes go here */
X
X#ifndef UNIX
Xint   CheckDate   (int d,   int m,  int y);
Xint   DoBanner    (char **s);
Xvoid  DoCalendar  (void);
Xint   DoGlobalOmit(char **s);
Xvoid  DoInclude   (char **s);
Xint   DoRem       (char **s);
Xint   DoSubst     (char *src, char *dst, int d, int m, int y, int jul, enum Token_t t, int tim);
Xvoid  Eprint(const char *f, ...);
Xint   FindTodaysDate(int *d, int *m, int *y);
Xint   FromJulian  (int jul, int *d, int *m, int *y);
Xint   GetTriggerDate (int d, int m, int y, int wd, int cons, int back, int repeat, int omit);
Xvoid  HandleQueuedAts(void);
Xvoid  initialize  (int argc, char *argv[]);
Xint   int_comp    (int *, int *);
Xint   Julian      (int d,   int m,  int y);
Xint   MoveBack    (int jul, int back, int d, int m, int y, int omit);
Xvoid  OpenFile    (char *s);
Xvoid  Output      (char *s);
XToken ParseToken  (char **s);
Xint   ProcessLine (void);
Xint   ReadLine    (void);
Xlong  SystemTime  (void);
Xint   TopLevel    (void);
Xint   TryNextDate(int *retday, int *retmon, int *retyr,
X		  int startday, int startmon, int startyr,
X		  int conday, int conmon, int conyr,
X		  int wkday, int cons, int inc);
X#else
Xint   CheckDate   ();
Xint   DoBanner    ();
Xvoid  DoCalendar  ();
Xint   DoGlobalOmit();
Xvoid  DoInclude   ();
Xint   DoRem       ();
Xint   DoSubst     ();
Xvoid  Eprint();
Xint   FindTodaysDate();
Xint   FromJulian  ();
Xint   GetTriggerDate();
Xvoid  HandleQueuedAts();
Xvoid  initialize  ();
Xint   int_comp    ();
Xint   Julian      ();
Xint   MoveBack    ();
Xvoid  OpenFile    ();
Xvoid  Output      ();
XToken ParseToken  ();
Xint   ProcessLine ();
Xint   ReadLine    ();
Xvoid  SigHupHandler();
Xlong  SystemTime  ();
Xint   TopLevel    ();
Xint   TryNextDate ();
X#endif
X
SHAR_EOF
$TOUCH -am 1115095890 protos.h &&
chmod 0600 protos.h ||
echo "restore of protos.h failed"
set `wc -c protos.h`;Wc_c=$1
if test "$Wc_c" != "1717"; then
	echo original size 1717, current size $Wc_c
fi
fi
# ============= remind-all.csh ==============
if test X"$1" != X"-c" -a -f 'remind-all.csh'; then
	echo "File already exists: skipping 'remind-all.csh'"
else
echo "x - extracting remind-all.csh (Text)"
sed 's/^X//' << 'SHAR_EOF' > remind-all.csh &&
X#!/bin/csh -f
X
X# Shell script to mail all users reminders.
X
X# Run it AFTER MIDNIGHT so that date is correct!
X# On our system, we have the following in our crontab:
X# 05 5 * * * /usr/share/lib/remind/remind-all > /dev/null 2>&1
X
X# Also, you MUST use the -r and -q options on REMIND, otherwise SEVERE
X# security hole could develop.  I recommend making this script
X# readable and executable only by root to minimize security problems.
X# DO NOT make the script setuid!
X
X# The following line gets a list of users for systems using SUN's
X# NIS service:
Xset USERS  = `ypcat passwd | awk -F: '{print $1}'`
X
X# The following line gets a list of users by examining /etc/passwd:
X# set USERS = `awk -F: '{print $1}' /etc/passwd`
X
X# If neither of the above methods works, you must come up with some
X# way of getting a list of users on the system
X
X# Set the following variables as appropriate for your system
Xset REMIND = /usr/local/bin/remind
Xset MAIL   = /usr/ucb/mail
Xset CMP    = /usr/bin/cmp
Xset RM     = "/usr/bin/rm -f"
X
Xset EMPTY  = /tmp/Empty.$$
Xset FULL   = /tmp/Full.$$
X
X# Create the dummy empty reminder file
X$REMIND -rq /dev/null > $EMPTY
X
X# Scan each user's directory for a .reminders file
Xforeach i ($USERS)
X   if (-r ~$i/.reminders) then
X
X#     echo "$i has a .reminders file."     DEBUGGING PURPOSES ONLY
X
X      $REMIND -rq ~$i/.reminders > $FULL
X      $CMP -s $EMPTY $FULL
X      if ($status != 0) then
X
X#        echo "Sending mail to $i"         DEBUGGING PURPOSES ONLY
X
X         $MAIL -s "Reminders" $i < $FULL
X      endif
X      $RM $FULL
X   endif
Xend
X
X$RM $EMPTY
SHAR_EOF
$TOUCH -am 1113132790 remind-all.csh &&
chmod 0700 remind-all.csh ||
echo "restore of remind-all.csh failed"
set `wc -c remind-all.csh`;Wc_c=$1
if test "$Wc_c" != "1568"; then
	echo original size 1568, current size $Wc_c
fi
fi
# ============= remind-all.sh ==============
if test X"$1" != X"-c" -a -f 'remind-all.sh'; then
	echo "File already exists: skipping 'remind-all.sh'"
else
echo "x - extracting remind-all.sh (Text)"
sed 's/^X//' << 'SHAR_EOF' > remind-all.sh &&
X# Shell script to mail all users reminders.
X
X# Thanks to Bill Aten for this script.
X
X# Run it AFTER MIDNIGHT so that date is correct!
X# On our system, we have the following in our crontab:
X# 02 00 * * * /usr/local/adm/remind-all >/dev/null 2>&1
X
X# Also, you MUST use the -r and -q options on REMIND, otherwise a SEVERE
X# security hole could develop.  I recommend making this script
X# readable and executable only by root to minimize security problems.
X# DO NOT make the script setuid!
X
X# The following line gets a list of users for systems using SUN's
X# NIS service:
X# USERS=`ypcat passwd | awk -F: '{print $1}'`
X
X# The following line gets a list of users by examining /etc/passwd:
XUSERS=`awk -F: '{print $1}' /etc/passwd`
X
X# If neither of the above methods works, you must come up with some
X# way of getting a list of users on the system
X
X# Set the following variables as appropriate for your system
XREMIND=/usr/local/bin/remind
XMAIL=/usr/bin/mail
XCMP=/bin/cmp
XRM="/bin/rm -f"
X
XEMPTY=/tmp/Empty.$$
XFULL=/tmp/Full.$$
X
X# Create the dummy empty reminder file
X$REMIND -rq /dev/null > $EMPTY
X
X# Scan each user's directory for a .reminders file
Xfor i in $USERS
Xdo
XHOME=`grep \^$i: /etc/passwd | awk -F: '{print $6}'`
X   if [ -r $HOME/.reminders ]; then
X
X#     echo "$i has a .reminders file."     DEBUGGING PURPOSES ONLY
X
X      $REMIND -rq $HOME/.reminders > $FULL
X      if `$CMP -s $EMPTY $FULL`; then
X         : do nothing
X      else
X
X#        echo "Sending mail to $i"         DEBUGGING PURPOSES ONLY
X
X         $MAIL -s "Reminders" $i < $FULL
X      fi
X      $RM $FULL
X   fi
Xdone
X
X$RM $EMPTY
SHAR_EOF
$TOUCH -am 1116131490 remind-all.sh &&
chmod 0700 remind-all.sh ||
echo "restore of remind-all.sh failed"
set `wc -c remind-all.sh`;Wc_c=$1
if test "$Wc_c" != "1589"; then
	echo original size 1589, current size $Wc_c
fi
fi
echo "End of part 3, continue with part 4"
exit 0