[comp.sources.misc] v17i005: remind - A replacement for calendar, Part03/04

dfs@doe.carleton.ca (David F. Skoll) (02/20/91)

Submitted-by: David F. Skoll <dfs@doe.carleton.ca>
Posting-number: Volume 17, Issue 5
Archive-name: remind/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 3 (of 4)."
# Contents:  cache.c dosubst.c files.c globals.h init.c omits.c
#   test.rem timed.c
# Wrapped by kent@sparky on Tue Feb 19 10:16:40 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cache.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cache.c'\"
else
echo shar: Extracting \"'cache.c'\" \(4966 characters\)
sed "s/^X//" >'cache.c' <<'END_OF_FILE'
X/***************************************************************/
X/*                                                             */
X/* CACHE.C                                                     */
X/*                                                             */
X/* Contains routines for caching reminder file to improve      */
X/* calendar performance.                                       */
X/*                                                             */
X/* By David Skoll - 15 November 1990                           */
X/***************************************************************/
X
X#include <stdio.h>
X#ifndef NO_MALLOC_H
X#include <malloc.h>
X#endif
X#include <string.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X#include "cache.h"
X
X/* Define a cached line */
Xtypedef struct cached_line {
X   char *text;
X   struct cached_line *next;
X} Centry;
X
XCentry Cache, *Current;
X
Xstatic int CacheDone, CacheFailed;
X
X/***************************************************************/
X/*                                                             */
X/*  InitCache                                                  */
X/*                                                             */
X/*  Initializes the caching system.                            */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid InitCache(void)
X#else
Xvoid InitCache()
X#endif
X{
X   CacheDone   = 0;
X   CacheFailed = 0;
X   Cache.next  = NULL;
X   Current     = &Cache;
X}
X
X/***************************************************************/
X/*                                                             */
X/* GetLine                                                     */
X/*                                                             */
X/* This function either reads a line from the file, or gets    */
X/* it from memory if it is cached.                             */
X/*                                                             */
X/* Returns 0 if more data to be read; otherwise, non-zero.     */
X/*                                                             */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint GetLine(void)
X#else
Xint GetLine()
X#endif
X{
X   int ret;
X   Token tok;
X   char *s;
X   Centry *c;
X
X   if (CacheFailed) return ReadLine();
X
X   if (!CacheDone) {
X      ret = ReadLine();
X      if (ret) {
X         CacheDone = 1;
X         strcpy(FileName, "* cache *");
X	 CurLine = 0;
X         return ret;
X      }
X      /* Check if we should cache this line */
X
X      s = Line;
X      tok = ParseToken(&s);
X      if (tok.type == Clear_t || tok.type == Push_t ||
X          tok.type == Pop_t || tok.type == Rem_t || tok.type == Omit_t) { 
X         c = (Centry *) malloc(sizeof(Centry));
X         if (c == NULL) {
X            CacheFailed = 1;
X            DestroyCache();
X            return 0;
X         }
X         c->text = (char *) malloc(strlen(Line)+1);
X         if (c->text == NULL) {
X            CacheFailed = 1;
X	    DestroyCache();
X            free(c);
X	    return 0;
X         }
X         /* Insert the cache entry */
X         c->next = NULL;
X         strcpy(c->text, Line);
X         Current->next = c;
X         Current = c;
X      }
X      return ret;
X   } else { /* Over here, we've finished caching, so just return the line */
X      if (Current == NULL) return 1;
X      else {
X         strcpy(Line, Current->text);
X         Current = Current->next;
X         return 0;
X      }
X   }
X}
X/***************************************************************/
X/*                                                             */
X/* ResetCache                                                  */
X/* Reset the cache to beginning, or reopen file if caching     */
X/* failed.                                                     */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid ResetCache(void)
X#else
Xvoid ResetCache()
X#endif
X{
X   /* Reset the OMIT context */
X   ClearOmitContext();
X
X   /* Get rid of any spurious stacked OMIT contexts */
X   FreeStackedOmits();
X
X   if (CacheFailed) OpenFile(FileName);
X   else Current = Cache.next;
X}
X
X/***************************************************************/
X/*                                                             */
X/* DestroyCache                                                */
X/* Frees all memory used by the cache.                         */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid DestroyCache(void)
X#else
Xvoid DestroyCache()
X#endif
X{
X   Centry *p = &Cache;
X   Centry *c = p->next;
X
X   while (c) {
X      if (c->text) free(c->text);
X      p = c;
X      c = c->next;
X      free(p);
X   }
X   Cache.next = NULL;
X}
X
END_OF_FILE
if test 4966 -ne `wc -c <'cache.c'`; then
    echo shar: \"'cache.c'\" unpacked with wrong size!
fi
# end of 'cache.c'
fi
if test -f 'dosubst.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dosubst.c'\"
else
echo shar: Extracting \"'dosubst.c'\" \(8427 characters\)
sed "s/^X//" >'dosubst.c' <<'END_OF_FILE'
X#include <ctype.h>
X#include <stdio.h>
X#include <string.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X/***************************************************************/
X/*                                                             */
X/*  DOSUBST.C                                                  */
X/*                                                             */
X/*  Performs line substitution for reminders.                  */
X/*                                                             */
X/*  If mode = 0, ignore %" escapes.                            */
X/*  If mode = 1, process %" escapes.  If a RUN type reminder   */
X/*     does not have %" escape, return the empty string.       */
X/*                                                             */
X/***************************************************************/
X
Xstatic char TODAY[] = "today";
Xstatic char TOMORROW[] = "tomorrow";
X
X#ifdef __STDC__
Xint DoSubst(char *src, char *dst, int d, int m, int y, int jul, enum Token_t t, int tim, int mode)
X#else
Xint DoSubst(src, dst, d, m, y, jul, t, tim, mode)
X     char *src;
X     char *dst;
X     int d;
X     int m;
X     int y;
X     int jul;
X     enum Token_t t;
X     int tim;
X     int mode;
X#endif
X{
X   int diff = jul - JulianToday;
X   char c;
X   char *od, *s;
X   int wkday = jul % 7;
X   char *plu, *pm;
X   int curtim = (int) (SystemTime() / 60);
X   int done;
X   int h;
X   int hh;
X   int min;
X   int tdiff;
X   int adiff, mdiff, hdiff;
X   char *mplu, *hplu, *when;
X   
X   if (mode == 1) {
X      s = src;
X      od = src;
X      while (*s) {
X         if (*s == '%' && *(s+1) == '\"') {
X	    src = s+2;
X	    break;
X	 }
X	 s++;
X      }
X      if (src == od && t == Run_t) {
X         *dst = 0;
X	 return 0;
X      }
X      if (*src == '%' && *(src+1) == '\"') {
X         *dst = 0;
X	 return 0;
X      }
X      if (tim != -1) {
X         sprintf(dst, "%02d:%02d ", (tim / 60), (tim % 60));
X	 dst += strlen(dst);
X      }
X   }
X       
X   if (tim == -1) tim = curtim;
X   tdiff = tim - curtim;
X   adiff = ABS(tdiff);
X   mdiff = adiff % 60;
X   hdiff = adiff / 60;
X   mplu = (mdiff == 1 ? "" : "s");
X   hplu = (hdiff == 1 ? "" : "s");
X   when = (tdiff < 0 ? "ago" : "from now");
X   
X   h = tim / 60;
X   min = tim % 60;
X
X   if (h >= 12) pm = "pm"; else pm = "am";
X   if (h == 12) hh = 12; else hh = h % 12;
X
X   *dst = 0;
X   
X   switch(d) {
X      case 1:
X      case 21:
X      case 31: plu = "st"; break;
X      
X      case 2:
X      case 22: plu = "nd"; break;
X      
X      case 3:
X      case 23: plu = "rd"; break;
X      
X      default: plu = "th"; break;
X   }
X      
X   
X   while (c = *src++) {
X     if (c == '\n') continue;
X     else if (c != '%') { *dst++ = c; *dst = 0; }
X     else {
X         od = dst;
X         c = *src++;
X	 done = 0;
X         if (diff <= 1) {
X            switch(upper(c)) {
X               case 'A':
X               case 'B':
X	       case 'C':
X	       case 'E':
X	       case 'F':
X	       case 'G':
X	       case 'H':
X	       case 'I':
X	       case 'J':
X	       case 'K':
X	       case 'L':
X	       case 'U':
X	       case 'V': sprintf(dst, "%s", (diff ? TOMORROW : TODAY));
X		         dst += strlen(dst);
X		         done = 1;
X 		         break;
X		     
X               default: done = 0;
X            }
X	 }	     
X     
X	 if (!done) switch(upper(c)) {
X	    case 0: *dst = 0; return 0;
X
X	    case '\"': if (mode == 1) {
X		          *dst = 0;
X			  return 0;
X		       }
X		       break;
X	    
X   	    case 'A':
X               sprintf(dst, "on %s, %d %s, %d", DayName[wkday], d,
X		              MonthName[m], y);
X               dst += strlen(dst);
X	       break;
X	       
X	    case 'B':
X               sprintf(dst, "in %d days' time", diff);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'C':
X               sprintf(dst, "on %s", DayName[wkday]);
X               dst += strlen(dst);
X	       break;
X	       
X	    case 'D':
X	       sprintf(dst, "%d", d);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'E':
X	       sprintf(dst, "on %02d/%02d/%04d", d, m+1, y);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'F':
X	       sprintf(dst, "on %02d/%02d/%04d", m+1, d, y);
X	       dst += strlen(dst);
X               break;
X
X	    case 'G':
X               sprintf(dst, "on %s, %d %s", DayName[wkday], d, MonthName[m]);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'H':
X               sprintf(dst, "on %02d/%02d", d, m+1);
X	       dst += strlen(dst);
X               break;
X
X	    case 'I':
X               sprintf(dst, "on %02d/%02d", m+1, d);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'J':
X               sprintf(dst, "on %s, %s %d%s, %d", DayName[wkday],
X		              MonthName[m], d, plu, y);
X               dst += strlen(dst);
X               break;
X	    
X	    case 'K':
X	       sprintf(dst, "on %s, %s %d%s", DayName[wkday],
X	                      MonthName[m], d, plu);
X	       dst += strlen(dst);
X               break;
X	       		      
X            case 'L':
X	       sprintf(dst, "on %04d/%02d/%02d", y, m+1, d);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'M':
X	       sprintf(dst, "%s", MonthName[m]);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'N':
X	       sprintf(dst, "%d", m+1);
X	       dst += strlen(dst);
X               break;
X
X	    case 'O':
X               if (RealToday == JulianToday) sprintf(dst, " (today)");
X               dst += strlen(dst);
X               break;
X	       
X	    case 'P':
X	       sprintf(dst, (diff == 1 ? "" : "s"));
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'Q':
X	       sprintf(dst, (diff == 1 ? "'s" : "s'"));
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'R':
X	       sprintf(dst, "%02d", d);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'S':
X	       sprintf(dst, plu);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'T':
X	       sprintf(dst, "%02d", m+1);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'U':
X	       sprintf(dst, "on %s, %d%s %s, %d", DayName[wkday], d,
X	                      plu, MonthName[m], y);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'V':
X	       sprintf(dst, "on %s, %d%s %s", DayName[wkday], d, plu,
X	                      MonthName[m]);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'W':
X	       sprintf(dst, DayName[wkday]);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'X':
X	       sprintf(dst, "%d", diff);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'Y':
X	       sprintf(dst, "%d", y);
X	       dst += strlen(dst);
X               break;
X	       
X	    case 'Z':
X	       sprintf(dst, "%d", y % 100);
X	       dst += strlen(dst);
X               break;
X
X	    case '1':
X               if (tdiff == 0) 
X                  sprintf(dst, "now");
X               else if (hdiff == 0) 
X	          sprintf(dst, "%d minute%s %s", mdiff, mplu, when);
X	       else if (mdiff == 0)
X	          sprintf(dst, "%d hour%s %s", hdiff, hplu, when);
X               else
X	          sprintf(dst, "%d hour%s and %d minute%s %s", hdiff, hplu, mdiff, mplu, when);
X	       dst += strlen(dst);
X               break;
X
X            case '2':
X               sprintf(dst, "at %d:%02d%s", hh, min, pm);
X	       dst += strlen(dst);
X	       break;
X
X	    case '3': sprintf(dst, "at %02d:%02d", h, min);
X	       dst += strlen(dst);
X	       break;
X
X	    case '4': sprintf(dst, "%d", tdiff);
X	       dst += strlen(dst);
X	       break;
X	       
X	    case '5': sprintf(dst, "%d", adiff);
X	       dst += strlen(dst);
X	       break;
X
X            case '6': sprintf(dst, when);
X	       dst += strlen(dst);
X	       break;
X
X	    case '7': sprintf(dst, "%d", hdiff);
X	       dst += strlen(dst);
X	       break;
X
X	    case '8': sprintf(dst, "%d", mdiff);
X	       dst += strlen(dst);
X	       break;
X
X	    case '9': sprintf(dst, mplu);
X	       dst += strlen(dst);
X	       break;
X
X	    case '0': sprintf(dst, hplu);
X	       dst += strlen(dst);
X	       break;
X
X            case '!': sprintf(dst, (tdiff >= 0 ? "is" : "was"));
X	       dst += strlen(dst);
X	       break;
X	    
X            case '_': *dst++ = '\n'; *dst = 0;
X	       break;
X
X            default:
X	       *dst++ = c;
X	       *dst = 0;
X	 }
X	 if (isupper(c)) *od = upper(*od);
X      }
X   }
X   if (t == Msg_t && mode == 0) *dst++ = '\n';
X   *dst = 0;
X   return 0;
X}   
END_OF_FILE
if test 8427 -ne `wc -c <'dosubst.c'`; then
    echo shar: \"'dosubst.c'\" unpacked with wrong size!
fi
# end of 'dosubst.c'
fi
if test -f 'files.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'files.c'\"
else
echo shar: Extracting \"'files.c'\" \(8388 characters\)
sed "s/^X//" >'files.c' <<'END_OF_FILE'
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif
X#include <string.h>
X#ifndef NO_MALLOC_H
X#include <malloc.h>
X#endif
X#ifndef UNIX
X#include <dos.h>
X#endif
X#include <fcntl.h>
X#ifdef UNIX
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X#endif
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X#ifdef __STDC__
Xstatic int PopFile(void);
X#else
Xstatic int PopFile();
X#endif
X
X/***************************************************************/
X/*                                                             */
X/*  FILES.C                                                    */
X/*                                                             */
X/*  All the routines for opening initial file, getting         */
X/*  and settting initial file's date, closing files,           */
X/*  handling INCLUDE commands, etc.                            */
X/*                                                             */
X/***************************************************************/
X
X/* Define the structure for saving info about a file */
Xtypedef struct {
X   long offset;
X   int  curline;
X   char *name;
X} FileSave;
X
X#define MAXINCLUDE 10
X/* Set up array of MAXINCLUDE file save areas */
Xstatic FileSave stack[MAXINCLUDE];
Xstatic int      SP;
X
Xstatic FILE *fp;
X
X/***************************************************************/
X/*                                                             */
X/*  OpenFile                                                   */
X/*                                                             */
X/*  Open the named file, initialize stack, get file date.      */
X/*  If there's a problem, print an error msg and die.          */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid OpenFile(char *s)
X#else
Xvoid OpenFile(s)
X     char *s;
X     
X#endif
X{
X   unsigned date, time;
X#ifndef UNIX
X   unsigned handle;
X#endif
X   int d, m, y;
X#ifndef UNIX
X   
X   /* Get the file's modification date */
X   if(_dos_open(s, O_RDONLY, &handle)) {
X      fprintf(stderr, "remind: Can't open %s.\n", s);
X      exit(1);
X#else
X   struct stat t;
X   struct tm *t1;
X      
X   /* Get the file's access date */
X   if (stat(s, &t)) {
X     fprintf(stderr, "remind: Can't find file %s.\n", s);
X     exit(1);
X#endif
X   }
X#ifndef UNIX
X   _dos_getftime(handle, &date, &time);
X   d = date & 0x1F;
X   m = (date >> 5) & 0xF;
X   y = (date >> 9) + 1980;
X#else
X   t1 = localtime(&(t.st_atime));
X#endif
X   
X#ifndef UNIX
X   if (y < BASE) LastRun = 0; else LastRun = Julian(d, m-1, y);
X   _dos_close(handle);
X#else
X   y = t1->tm_year + 1900;
X   m = t1->tm_mon;
X   d = t1->tm_mday;
X   
X   if (y < BASE) LastRun = 0; else LastRun = Julian(d, m, y);
X#endif
X   fp = fopen(s, "r");
X   if (fp == NULL) {
X      fprintf(stderr, "remind: Can't open %s.\n", s);
X      exit(1);
X   }
X   
X   CurLine = 0;
X   strcpy(FileName, s);
X   SP = 0;
X   
X   return;
X}   
X
X/***************************************************************/
X/*                                                             */
X/*  DoInclude                                                  */
X/*                                                             */
X/*  Push the state of the current file and open a new file.    */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid DoInclude(char **s)
X#else
Xvoid DoInclude(s)
X     char **s;
X     
X#endif
X{
X   Token tok;
X   tok = ParseToken(s);
X   
X   /* First, check if there's room on the stack */
X   if (SP == MAXINCLUDE) {
X      Eprint("Too many levels of INCLUDE\n");
X      return;
X   }
X   
X   /* Save current data */
X#ifndef UNIX
X   stack[SP].offset = ftell(fp) - 1L;
X#else
X   stack[SP].offset = ftell(fp);
X#endif
X   stack[SP].curline = CurLine;
X   stack[SP].name = (char *) malloc(strlen(FileName)+1);
X   if (stack[SP].name == NULL) {
X      Eprint("Out of memory for INCLUDE\n");
X      return;
X   }
X   strcpy(stack[SP].name, FileName);
X   
X   SP++;
X   
X   /* Close the current file */
X   fclose(fp);
X   
X   /* Open the new file */
X   fp = fopen(tok.str, "r");
X   if (fp == NULL) {
X      Eprint("Can't open %s for INCLUDE\n", tok.str);
X      PopFile();
X      return;
X   }
X   if (Debug || Purge) {
X      Eprint("INCLUDING file %s\n", tok.str);
X   }
X   
X   /* Set the global variables */
X   CurLine = 0;
X   strcpy(FileName, tok.str);
X   return;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  PopFile                                                    */
X/*                                                             */
X/*  Pop to the previous file, if there is one.  Return 0 for   */
X/*  OK, non-zero for no more files.  If we can't pop back      */
X/*  to a file, print an error message and die.                 */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xstatic int PopFile(void)
X#else
Xstatic int PopFile()
X#endif
X{
X#ifndef UNIX
X   unsigned handle, date, time;
X   struct dostime_t t;
X#endif
X
X   if (fp) fclose(fp);
X#ifndef UNIX
X   if (!SP) {
X      if (!Debug && !Purge && (JulianToday == RealToday)) {
X         if (_dos_open(FileName, O_RDONLY, &handle)) {
X            fprintf(stderr, "Could not reset date of %s\n", FileName);
X            return 1;
X         }
X	 _dos_gettime(&t);
X	 date = CurDay;
X	 date |= (CurMon + 1) << 5;
X	 date |= (CurYear - 1980) << 9;
X	 time = t.second / 2;
X	 time |= t.minute << 5;
X	 time |= t.hour << 11;
X	 _dos_setftime(handle, date, time);
X      }
X      return 1;
X   }
X      
X#else
X   if (!SP) return -1;
X         
X#endif
X   SP--;
X   fp = fopen(stack[SP].name, "r");
X   if (fp == NULL) {
X      Eprint("Argh! Can't return to %s from INCLUDE file %s\n", stack[SP].name, FileName);
X      exit(1);
X   }
X#ifndef UNIX
X   if (fseek(fp, stack[SP].offset, SEEK_SET)) {
X#else
X   if (fseek(fp, stack[SP].offset, 0)) {
X#endif
X      Eprint("Argh! Can't fseek %s after returning from INCLUDE file %s\n", stack[SP].name, FileName);
X      exit(1);
X   }
X   
X   if (Debug || Purge) {
X      Eprint("Returning to file %s\n", stack[SP].name);
X   }
X   CurLine = stack[SP].curline;
X   strcpy(FileName, stack[SP].name);
X   free(stack[SP].name);
X   return 0;
X}
X/***************************************************************/
X/*                                                             */
X/*  ReadLine                                                   */
X/*                                                             */
X/*  Reads a line from the file.  If EOF, pops to previous file */
X/*  if there was one.  Returns 0 if more input, non-zero       */
X/*  if no more input.  Updates CurLine.                        */
X/*                                                             */
X/***************************************************************/
Xint ReadLine()
X{
X   int done = 0;
X   int len;
X   
X   Fresh = 1;
X   while (!done) {
X      CurLine++;
X      if (fgets(Line, 512, fp) == NULL) {
X         if (ferror(fp)) Eprint("Error reading %s\n", FileName);
X	 if (PopFile()) return 1;
X      } else {
X         len = strlen(Line);
X         /* Remove the newline */
X         if (*Line && (*(Line + len-1)=='\n')) {
X            *(Line + strlen(Line)-1) = 0;
X            len--;
X         }
X         done = 1;
X         while(*Line && (*(Line + len-1) == '\\') && len<512) {
X 	    *(Line + len-1) = '\n';
X            if (fgets(Line+len, 512-len,fp) == NULL) {
X               *(Line + len) = 0;
X  	       break;
X	    }
X
X	    CurLine++;
X	    len = strlen(Line);
X            /* Remove the newline */
X            if (*Line && (*(Line + len-1)=='\n')) {
X               *(Line + strlen(Line)-1) = 0;
X               len--;
X            }
X         }
X      }	 
X   }
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  TopLevel - Returns 1 if current file is top level, 0       */
X/*  if it is INCLUDEd.                                         */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint TopLevel(void) { return (SP == 0); }
X#else
Xint TopLevel()
X{
X  return (SP == 0);
X}
X#endif
END_OF_FILE
if test 8388 -ne `wc -c <'files.c'`; then
    echo shar: \"'files.c'\" unpacked with wrong size!
fi
# end of 'files.c'
fi
if test -f 'globals.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'globals.h'\"
else
echo shar: Extracting \"'globals.h'\" \(1469 characters\)
sed "s/^X//" >'globals.h' <<'END_OF_FILE'
X/***************************************************************/
X/*                                                             */
X/*  GLOBALS.H                                                  */
X/*                                                             */
X/*  Global variables for REMIND.                               */
X/*                                                             */
X/*  By David Skoll - 30 Sept. 1990                             */
X/*                                                             */
X/***************************************************************/
X
Xextern char *MonthName[];
Xextern char *DayName[];
Xextern Token keywd[];
Xextern int   MonthDays[];
Xextern int   MonthIndex[2][12];
Xextern int   FullOmitArray[];
Xextern int   PartOmitArray[];
Xextern char  Line[];
Xextern char  WorkBuf[];
Xextern char  TmpBuf[];
Xextern char  Fresh;
Xextern char  Purge;
Xextern char  Debug;
Xextern char  Verbose;
Xextern char  Next;
Xextern char  FileName[];
Xextern int   CurLine;
Xextern int   NumEmitted;
Xextern int   NumRem;
Xextern int   NumFullOmit;
Xextern int   NumPartOmit;
Xextern int   JulianToday;
Xextern int   LastRun;
Xextern int   CurYear;
Xextern int   CurMon;
Xextern int   CurDay;
Xextern char  Banner[];
Xextern int   RealToday;
Xextern char  IgRun;
Xextern char  IgOnce;
Xextern int   NumAtsQueued;
Xextern char  QueueAts;
Xextern char  PrintAts;
Xextern int   Calendar;
Xextern int   CalTime;
Xextern int   CalWidth;
Xextern int   SimpleCalendar;
END_OF_FILE
if test 1469 -ne `wc -c <'globals.h'`; then
    echo shar: \"'globals.h'\" unpacked with wrong size!
fi
# end of 'globals.h'
fi
if test -f 'init.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'init.c'\"
else
echo shar: Extracting \"'init.c'\" \(6305 characters\)
sed "s/^X//" >'init.c' <<'END_OF_FILE'
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif
X#include <string.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X#define PATCHLEVEL 0
X
Xstatic char DPMsg[] = "Debug and Purge options conflict - Purge chosen.\n";
Xstatic char DPCMsg[] = "Calendar overrides Debug and Purge options.\n";
Xstatic char NMsg[] = "Next overrides Calendar, Debug and Purge options.\n";
X
X/***************************************************************/
X/*                                                             */
X/*  void initialize(int argc, char *argv[])                    */
X/*                                                             */
X/*  Reads command line options, sets appropriate flags         */
X/*  and FileName.  Also exits with error if invoked            */
X/*  incorrectly.                                               */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid initialize(int argc, char *argv[])
X#else
Xvoid initialize(argc, argv)
X     int argc;
X     char *argv[];
X#endif
X{
X   int i;
X   char *s;
X   int d, m, y, t;
X   Token tok;
X
X   Debug    = 0;
X   Purge    = 0;
X   Verbose  = 0;
X   IgOnce   = 0;
X   IgRun    = 0;
X   Calendar = 0;
X   Next     = 0;
X   PrintAts = 1;
X   QueueAts = 1;
X   CalWidth = 10;
X   SimpleCalendar = 0;
X
X   if(argc == 1) {
X     fprintf(stderr, "\nREMIND 2.3 Patch Level %d (C) 1990, 1991 by David Skoll.\n\n", PATCHLEVEL);
X#ifdef UNIX
X     fprintf(stderr, "Usage: remind [-n | -d | -p | -c# [-w# | -s]] [-voraq] filename [date]\n\n");
X#else
X     fprintf(stderr, "Usage: remind [-n | -d | -p | -c# [-w# | -s]] [-vor] filename [date]\n\n");
X#endif
X     fprintf(stderr, "-n   Output next occurrence of reminders in simple format\n");
X     fprintf(stderr, "-d   Debug reminder file\n-p   Purge reminder file\n");
X     fprintf(stderr, "-c#  Produce calendar for # months\n");
X     fprintf(stderr, "-w#  Make calendar # columns wide\n");
X     fprintf(stderr, "-s   Produce simple calendar listing (used with -c)\n");
X     fprintf(stderr, "-v   Verbose messages\n-o   Ignore ONCE directives\n");
X     fprintf(stderr, "-r   Ignore RUN directives\n");
X#ifdef UNIX
X     fprintf(stderr, "-a   Do not trigger current AT reminders in foreground\n");
X     fprintf(stderr, "-q   Do not queue current AT reminders\n\n");
X#endif
X     exit(1);
X   }
X
X   i = 1;
X   s = argv[i];
X
X   /* Process options */
X   while (*s == '-') {
X      while (*++s) {
X         switch(upper(*s)) {
X
X	    case 'N': Next = 1;
X	             if (Calendar || Debug || Purge) {
X		        Calendar = Debug = Purge = 0;
X			fprintf(stderr, NMsg);
X	             }
X		     break;
X
X            case 'P':  Purge = 1;
X		     if (Next) {
X		        Calendar = Debug = Purge = 0;
X			fprintf(stderr, NMsg);
X		     }
X		     if (Calendar) {
X		        Debug = Purge = 0;
X			fprintf(stderr, DPCMsg);
X	             }
X                     if (Debug) {
X                        Debug = 0;
X                        fprintf(stderr, DPMsg);
X                     }
X		     break;
X
X            case 'D': Debug = 1;
X		     if (Next) {
X		        Calendar = Debug = Purge = 0;
X			fprintf(stderr, NMsg);
X		     }
X		     if (Calendar) {
X		        Debug = Purge = 0;
X			fprintf(stderr, DPCMsg);
X	             }
X	              if (Purge) {
X		         Debug = 0;
X			 fprintf(stderr, DPMsg);
X		      }
X		      break;
X
X            case 'C': Calendar = 1;
X		     if (Next) {
X		        Calendar = Debug = Purge = 0;
X			fprintf(stderr, NMsg);
X		     }
X		     if (Debug || Purge) {
X		        Debug = Purge = 0;
X			fprintf(stderr, DPCMsg);
X	             }
X                     t = atoi(s + 1);
X		     if (t > 0 && t <= 12) Calendar = t;
X                     /* Skip remaining chars on this option */
X	             while (*++s) ;
X	             s--;
X		     break;
X
X	    case 'W': CalWidth = (atoi(s+1)-9)/7;
X	             if (CalWidth < 10) CalWidth = 10;
X		     if (CalWidth > 40) CalWidth = 40;
X	             while (*++s) ;
X	             s--;
X		     break;
X
X            case 'S': SimpleCalendar = 1; break;
X
X	    case 'V': Verbose = 1; break;
X
X	    case 'O': IgOnce = 1; break;
X
X	    case 'R': IgRun = 1; break;
X#ifdef UNIX
X	    case 'A': PrintAts = 0; break;
X
X            case 'Q': QueueAts = 0; break;	      
X#endif	    
X	    default: fprintf(stderr, "Unknown option '%c' ignored.\n", *s);
X	 }			      
X      }
X      i++;
X      if (i >= argc) {
X	fprintf(stderr, "Missing filename - type 'remind' for usage information.\n");
X	 exit(1);
X      }
X      s = argv[i];
X   }	 
X   
X   /* Set FileName */
X   strcpy(FileName, argv[i++]);
X   
X   /* Get date, if supplied */
X   if (i < argc) {
X      *WorkBuf = 0;
X      while (i < argc) {
X         strcat(WorkBuf, argv[i++]);
X	 strcat(WorkBuf, " ");
X      }
X      /* Parse the date */
X      d = m = y = -1;
X      tok.type = Unknown_t;
X      s = WorkBuf;
X      while (tok.type != Eol_t) {
X         tok = ParseToken(&s);
X	 switch(tok.type) {
X
X	    case Eol_t: break;
X
X	    case Year_t: if (y == -1) 
X	                    y = tok.val;
X			 else {
X			    fprintf(stderr, "Year specified twice!\n");
X			    exit(1);
X			 }
X			 break;
X
X	    case Month_t: if (m == -1) 
X	                    m = tok.val;
X			 else {
X			    fprintf(stderr, "Month specified twice!\n");
X			    exit(1);
X			 }
X			 break;
X			 
X	    case Day_t: if (d == -1) 
X	                    d = tok.val;
X			 else {
X			    fprintf(stderr, "Day specified twice!\n");
X			    exit(1);
X			 }
X			 break;
X	   
X            default: fprintf(stderr, "Illegal token %s on command line.\n", tok.str);
X	             exit(1);
X   
X         }
X      } 
X      
X      if (d == -1 || m == -1 || y == -1) {
X         fprintf(stderr, "Date on command line must be fully specified.\n");
X	 exit(1);
X      }
X      if (CheckDate(d, m, y)) {
X         fprintf(stderr, "Illegal date on command line.\n");
X	 exit(1);
X      }
X
X      CurDay = d;
X      CurMon = m;
X      CurYear = y;
X      JulianToday = Julian(d, m, y);
X   }
X   OpenFile(FileName);
X   if (Debug) {
X      FromJulian(LastRun, &d, &m, &y);
X#ifndef UNIX
X      fprintf(stderr, "\nFile %s last modified on %s, %d %s, %d\n", FileName,
X#else
X      fprintf(stderr, "\nFile %s last accessed on %s, %d %s, %d\n", FileName,
X#endif
X                       DayName[LastRun % 7], d, MonthName[m], y);
X   }
X   return;
X}
END_OF_FILE
if test 6305 -ne `wc -c <'init.c'`; then
    echo shar: \"'init.c'\" unpacked with wrong size!
fi
# end of 'init.c'
fi
if test -f 'omits.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'omits.c'\"
else
echo shar: Extracting \"'omits.c'\" \(10630 characters\)
sed "s/^X//" >'omits.c' <<'END_OF_FILE'
X/***************************************************************/
X/*                                                             */
X/*  OMITS.C                                                    */
X/*                                                             */
X/*  By David Skoll - 12 February 1991                          */
X/*                                                             */
X/*  Handle all code related to global OMITs                    */
X/*                                                             */
X/***************************************************************/
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif
X
X#ifndef NO_MALLOC_H
X#include <malloc.h>
X#endif
X
X#include "defines.h"
X#include "protos.h"
X#include "globals.h"
X
X/* Define an OMIT stack buffer */
Xtypedef struct omit_stack {
X   struct omit_stack *next;
X   int NumFullOmit;
X   int NumPartOmit;
X   int Omits[1];
X} OmitBuffer;
X
X/* Variables that we only want visible here */
Xstatic OmitBuffer *OmitStack = (OmitBuffer *) NULL;
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, -2 if problem.      */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
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   char *olds = *s;
X
X   tok.type = Unknown_t;
X   while(tok.type != Eol_t && tok.type != Run_t && tok.type != Msg_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
X	 case Delta_t:
X	 case Eol_t:
X	 case Msg_t:
X	 case Run_t: break;
X	 default:      Eprint("Invalid token '%s' for OMIT command.\n", tok.str);
X		       return -2;
X      }
X   }
X
X   if (d == -1 || m == -1 || CheckDate(d, m, y)) {
X      Eprint("Invalid date specification.\n");
X      return -2;
X   }
X
X   if (y == -1) { /* Only mm-dd specified */
X      if (NumPartOmit == POMITSIZE) {
X	 Eprint("Too many partially-specified OMITs.\n");
X	 return -2;
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      /* If we got a DELTA, a MSG or a RUN, then execute DoRem */
X      if (tok.type == Delta_t || tok.type == Run_t || tok.type == Msg_t)
X          return DoRem(&olds);
X   } else { /* All three specified */
X      if (NumFullOmit == FOMITSIZE) {
X	 Eprint("Too many fully-specified OMITs.\n");
X	 return -2;
X      }
X      omit = Julian(d, m, y);
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      if (omit < JulianToday) {
X	 if (Debug) Eprint("Omit has expired.\n");
X	 return -1;
X      }
X      /* If we got a DELTA, a MSG or a RUN, then execute DoRem */
X      if (tok.type == Delta_t || tok.type == Run_t || tok.type == Msg_t)
X         return DoRem(&olds);
X   }
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/* PushOmitContext                                             */
X/*                                                             */
X/* Push the Global OMIT context onto a stack.                  */
X/*                                                             */
X/* Returns 0 for success, 1 for failure.                       */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint PushOmitContext(void)
X#else
Xint PushOmitContext()
X#endif
X{
X   OmitBuffer *new = (OmitBuffer *) malloc( sizeof(OmitBuffer) +
X                     (NumFullOmit + NumPartOmit - 1) * sizeof(int));
X
X   if (!new) {
X      Eprint("Unable to allocate memory for PUSH-OMIT-CONTEXT.\n");
X      return 1;
X   }
X
X   new->NumFullOmit = NumFullOmit;
X   new->NumPartOmit = NumPartOmit;
X
X   CopyInts(FullOmitArray, &(new->Omits[0]), NumFullOmit);
X   CopyInts(PartOmitArray, &(new->Omits[NumFullOmit]), NumPartOmit);
X
X   new->next = OmitStack;
X   OmitStack = new;
X
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/* PopOmitContext                                              */
X/*                                                             */
X/* Restore the OMIT context from the stack.                    */
X/*                                                             */
X/* Returns 0 for success, 1 for failure.                       */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint PopOmitContext(void)
X#else
Xint PopOmitContext()
X#endif
X{
X
X   OmitBuffer *temp;
X
X   if (!OmitStack) {
X      Eprint("No saved contexts for POP-OMIT-CONTEXT.\n");
X      return 1;
X   }
X
X   NumFullOmit = OmitStack->NumFullOmit;
X   NumPartOmit = OmitStack->NumPartOmit;
X
X   CopyInts(&(OmitStack->Omits[0]), FullOmitArray, NumFullOmit);
X   CopyInts(&(OmitStack->Omits[NumFullOmit]), PartOmitArray, NumPartOmit);
X
X   temp = OmitStack->next;
X   free(OmitStack);
X   OmitStack = temp;
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  ClearOmitContext                                           */
X/*                                                             */
X/*  Get rid of all global OMITS.                               */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid ClearOmitContext(void)
X#else
Xvoid ClearOmitContext()
X#endif
X{
X   NumFullOmit = NumPartOmit = 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  CopyInts                                                   */
X/*                                                             */
X/*  Copy integer values from one array to another.             */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid CopyInts(int *from, int *to, int count)
X#else
Xvoid CopyInts(from, to, count)
Xint *from, *to, count;
X#endif
X{
X   while (count--) *to++ = *from++;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  FreeStackedOmits                                           */
X/*                                                             */
X/*  Get rid of all the stacked OMIT contexts                   */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid FreeStackedOmits(void)
X#else
Xvoid FreeStackedOmits()
X#endif
X{
X   OmitBuffer *current = OmitStack, *next;
X
X   if (current && Debug) Eprint("Warning - more PUSH-OMIT-CONTEXTs than POP-OMIT-CONTEXTs\n");
X
X   while (current) {
X      next = current->next;
X      free(current);
X      current = next;
X   }
X
X   OmitStack = (OmitBuffer *) NULL;
X}
X
X/***************************************************************/
X/*                                                             */
X/* IsOmitted                                                   */
X/*                                                             */
X/* Returns non-zero if the julian date should be omitted, 0    */
X/* otherwise.                                                  */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint IsOmitted(int jul, int localomit)
X#else
Xint IsOmitted(jul, localomit)
Xint jul, localomit;
X#endif
X{
X   int d, m, y;
X
X   /* Check if we should omit because of local omit */
X   if (localomit & 1 << (jul % 7)) return 1;
X
X   /* Check if we should omit because of fully-specified global omit */
X   if (NumFullOmit && my_bsearch(jul, FullOmitArray, NumFullOmit)) return 1;
X
X   /* Check if we should omit because of partially-specified global omits */
X   if (NumPartOmit) {
X      FromJulian(jul, &d, &m, &y);
X      if (my_bsearch((m << 5)+d, PartOmitArray, NumPartOmit)) return 1;
X   }
X
X   /* Looks cool - don't omit */
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/*  my_bsearch                                                 */
X/*                                                             */
X/*  A simplified version of bsearch() for people whose library */
X/*  does not have the full version.  This only works when      */
X/*  searching a sorted array of integers.                      */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint *my_bsearch(int key, int *array, int number)
X#else
Xint *my_bsearch(key, array, number)
Xint key, *array, number;
X#endif
X{
X   int top = number - 1;
X   int bot = 0;
X   int mid;
X
X   while (top >= bot) {
X      mid = (top+bot)/2;
X      if (*(array+mid) == key) return array+mid;
X      else if (*(array+mid) > key) top = mid-1;
X      else bot = mid+1;
X   }
X
X   /* Oh, well - unsuccessful search.  Return NULL */
X   return NULL;
X}
END_OF_FILE
if test 10630 -ne `wc -c <'omits.c'`; then
    echo shar: \"'omits.c'\" unpacked with wrong size!
fi
# end of 'omits.c'
fi
if test -f 'test.rem' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test.rem'\"
else
echo shar: Extracting \"'test.rem'\" \(2975 characters\)
sed "s/^X//" >'test.rem' <<'END_OF_FILE'
X# Test file for REMIND
X#
X# Use this file to test the date calculation routines
X# of the REMIND program by typing:
X#
X# (UNIX version):
X# 	remind -dv test.rem 16 FEB 1991 >& temp 
X#
X# (DOS version):
X#	e2o remind -dv test.rem 16 FEB 1991 > temp
X#
X# and comparing the results with test.out.
X#
X# The only differences should be the date of last
X# modification/access
X
X# Test each possible case of the basic reminders.
X
XREM MSG Every Day
X
XREM 18 MSG Every 18th 
XREM 15 MSG Every 15th
X
XREM Feb MSG February
XREM Jan MSG January
XREM March MSG March
X
XREM 13 Jan MSG 13 Jan
XREM 15 Feb MSG 15 Feb
XREM 28 Feb MSG 28 Feb
XREM 29 Feb MSG 29 Feb
XREM 5 Mar MSG 5 Mar
X
XREM 1990 MSG 1990
XREM 1991 MSG 1991
XREM 1992 MSG 1991
X
XREM 1 1990 MSG 1 1990
XREM 29 1991 MSG 29 1991
XREM 29 1992 MSG 29 1992
XREM 16 1991 MSG 16 1991
X
XREM Jan 1990 MSG Jan 1990
XREM Feb 1991 MSG Feb 1991
XREM Dec 1991 MSG Dec 1991
XREM May 1992 MSG May 1992
X
XREM 1 Jan 1991 MSG 1 Jan 1991
XREM 16 Feb 1991 MSG 16 Feb 1991
XREM 29 Dec 1992 MSG 29 Dec 1992
X
XREM Sun MSG Sun
XREM Fri Sat Tue MSG Fri Sat Tue
X
XREM Sun 16 MSG Sun 16
XREM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1
X
XREM Sun Feb MSG Sun Feb
XREM Mon Tue March MSG Mon Tue March
X
XREM Sun 16 Feb MSG Sun 16 Feb
XREM Mon Tue 10 March MSG Mon Tue 10 March
X
XREM Sat Sun 1991 MSG Sat Sun 1991
XREM Mon Tue 1992 MSG Mon Tue 1992
X
XREM Sun 16 1991 MSG Sun 16 1991
XREM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992
X
XREM Mon Feb 1991 MSG Mon Feb 1991
XREM Tue Jan 1992 MSG Tue Jan 1992
X
XREM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991
XREM Tue 28 Jan 1992 MSG Tue 28 Jan 1992
X
X# Try some Backs
XCLEAR-OMIT-CONTEXT
XREM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun
XREM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun
X
XOMIT 28 Feb
XREM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted)
XREM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted)
X
XCLEAR-OMIT-CONTEXT
X
X# Try out UNTIL
XREM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991
X
X# Try playing with the OMIT context
X
XOMIT 28 Feb 1991
XREM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
XREM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
XREM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
XREM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
XREM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
X
XPUSH-OMIT-CONTEXT
XCLEAR-OMIT-CONTEXT
XREM 1 Mar -1 MSG 1 mar -1
XREM 1 Mar --1 MSG 1 mar --1
XREM 28 Feb BEFORE MSG 28 Feb BEFORE
XREM 28 Feb SKIP MSG 28 Feb SKIP 
XREM 28 Feb AFTER MSG 28 Feb AFTER
X
XPOP-OMIT-CONTEXT
XREM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
XREM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
XREM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
XREM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
XREM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
X
X
XREM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
X 
X# Test BACK
XCLEAR-OMIT-CONTEXT
XREM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
X
XOMIT 17 Feb 1991
XREM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted)
XREM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
X
XCLEAR-OMIT-CONTEXT
X
END_OF_FILE
if test 2975 -ne `wc -c <'test.rem'`; then
    echo shar: \"'test.rem'\" unpacked with wrong size!
fi
# end of 'test.rem'
fi
if test -f 'timed.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'timed.c'\"
else
echo shar: Extracting \"'timed.c'\" \(7484 characters\)
sed "s/^X//" >'timed.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <signal.h>
X#include <string.h>
X#ifndef NO_MALLOC_H
X#include <malloc.h>
X#endif
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X/***************************************************************/
X/*                                                             */
X/*  TIMED.C                                                    */
X/*                                                             */
X/*  Contains routines for triggering timed reminders           */
X/*                                                             */
X/*  By David Skoll - 12 Nov 1990                               */
X/*                                                             */
X/*  (C) 1990 by David Skoll                                    */
X/*                                                             */
X/***************************************************************/
X
X/* Global pointer to AT reminders */
X 
Xstatic AtEntry AtQueue =
X{
X   0, 0, 0, 0, Unknown_t, NULL, NULL
X};
X
X/***************************************************************/
X/*                                                             */
X/*  AddEntry                                                   */
X/*                                                             */
X/*  Add an entry to the AT queue, keeping things sorted by     */
X/*  trigger time.                                              */
X/*                                                             */
X/*  Returns 0 for success, nonzero for failure                 */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint AddEntry(AtEntry *e)
X#else
Xint AddEntry(e)
XAtEntry *e;
X#endif
X{
X   AtEntry *current, *prev;
X   prev = &AtQueue;
X   current = prev->next;
X   while (current) {
X      if (e->firsttime < current->firsttime) {
X         prev->next = e;
X         e->next = current;
X         break;
X      } else {
X         prev = current;
X         current = prev->next;
X      }
X   }
X   if (!current) {
X      prev->next = e;
X      e->next = NULL;
X   }
X}
X
X/***************************************************************/
X/*                                                             */
X/* DoAt                                                        */
X/* Creates an entry for an At reminder, puts it on the queue   */
X/* Updates the global variable NumAtsQueued                    */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint DoAt(int tim, int tdelta, int trep, char *body, enum Token_t type)
X#else
Xint DoAt(tim, tdelta, trep, body, type)
Xint tim, tdelta, trep;
Xchar *body;
Xenum Token_t type;
X#endif
X{
X   AtEntry *e;
X   int curtime;
X
X   curtime = (int) (SystemTime() / 60);
X   
X   /* If the trigger time is in the past, don't add to the at queue */     
X   if (tim < curtime) return 0;
X
X   /* Allocate space for the entry */
X   e = (AtEntry *) malloc(sizeof(AtEntry));
X   if (e == (AtEntry *) NULL) {
X      Eprint("Can't malloc memory for AT!\n");
X      return 1;
X   }
X   e->text = malloc(strlen(body)+1);
X   if (e->text == NULL) {
X      Eprint("Can't malloc memory for body of AT!\n");
X      return 1;
X   }
X   strcpy(e->text, body);
X
X   /* Find out the next trigger time */
X   e->firsttime = FindNextTriggerTime(tim, trep, tdelta, curtime);
X   e->repeat    = trep;
X   e->type      = type;
X   e->time      = tim;
X   e->delta     = tdelta;
X   AddEntry(e);
X   NumAtsQueued++;
X   return 0;
X}
X
X/***************************************************************/
X/*                                                             */
X/* int FindNextTriggerTime                                     */
X/*                                                             */
X/* Returns the next time a queued AT should be triggered.      */
X/* Returns -1 if the AT has expired.                           */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xint FindNextTriggerTime(int tim, int rep, int delta, int curtime)
X#else
Xint FindNextTriggerTime(tim, rep, delta, curtime)
Xint tim, rep, delta, curtime;
X#endif
X{
X   int trigger = tim;
X   
X   if (delta <= 0)
X      if (trigger < curtime) return -1; else return trigger;
X   
X   trigger -= delta;
X   if (rep == -1) rep = delta;
X
X   if (trigger < curtime) trigger += ((curtime - trigger) / rep) * rep;
X   if (trigger < curtime) trigger += rep;
X   if (trigger > tim) trigger = tim;
X   if (trigger < curtime) return -1; else return trigger;
X}
X
X/***************************************************************/
X/*                                                             */
X/* HandleQueuedAts                                             */
X/*                                                             */
X/* Handles all the queued AT reminders.  Sits in a sleep loop  */
X/* to trigger reminders.                                       */
X/*                                                             */
X/***************************************************************/
X#ifdef __STDC__
Xvoid HandleQueuedAts(void)
X#else
Xvoid HandleQueuedAts()
X#endif
X{
X   AtEntry *e;
X   long TimeToSleep;
X   unsigned SleepTime;
X   long now;
X
X   signal(SIGINT, SigIntHandler);
X
X   while (e = AtQueue.next) {
X      now = SystemTime();
X      TimeToSleep = (long) e->firsttime * 60L - now;
X      if (TimeToSleep < 0L) TimeToSleep = 0L;
X
X      /* Be paranoid and assume that unsigneds are only two bytes long -
X         therefore, do the sleeping in 30000-second chunks. */
X
X      while (TimeToSleep) {
X         if (TimeToSleep > 30000L) SleepTime = 30000;
X                              else SleepTime = (unsigned) TimeToSleep;
X         sleep(SleepTime);
X         TimeToSleep -= (long) SleepTime;
X      }
X
X      /* Over here, we trigger the reminder */
X      DoSubst(e->text, WorkBuf, CurDay, CurMon, CurYear, JulianToday, e->type, e->time, 0);
X      if (e->type == Run_t) system(WorkBuf);
X      else printf("%s\n", WorkBuf);
X
X      /* Remove the entry from the queue */
X      AtQueue.next = e->next;
X
X      /* Check if this reminder should be re-triggered */
X      e->firsttime = FindNextTriggerTime(e->time, e->repeat, 
X                                        e->delta, e->firsttime + 1);
X
X      if (e->firsttime != -1) AddEntry(e);
X      else {
X      /* Not to be added - free the memory */
X         free(e->text);
X         free(e);
X      }
X   }
X}
X
X/***************************************************************/
X/*                                                             */
X/* SigIntHandler                                               */
X/*                                                             */
X/* For debugging purposes, when sent a SIGINT, we print the    */
X/* contents of the queue.                                      */
X/*                                                             */
X/***************************************************************/
Xvoid SigIntHandler()
X{
X   AtEntry *e;
X
X   printf("Contents of AT queue:\n");
X
X   e = AtQueue.next;
X   while (e) {
X      printf("Trigger: %02d:%02d  Activate: %02d:%02d  Rep: %d  Delta: %d\n",
X              e->time / 60, e->time % 60, e->firsttime / 60, e->firsttime % 60,
X              e->repeat, e->delta);
X      printf("Text: %s %s\n\n", ((e->type == Msg_t) ? "MSG" : "RUN"), e->text);
X      e = e->next;
X   }
X   printf("\n");
X}
END_OF_FILE
if test 7484 -ne `wc -c <'timed.c'`; then
    echo shar: \"'timed.c'\" unpacked with wrong size!
fi
# end of 'timed.c'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.