[alt.sources] REMIND 2.0 01/03

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

REMIND is a sophisticated reminder service, with many more features than
the Unix calendar command.  It includes the ability to specify holidays,
advance warning of reminders, sophisticated date specifications, and
the ability to format output in a convenient way.

REMIND was written originally for MS-DOS and then ported to Unix.
It has been compiled on a Sun 3 and a Sun 4, as well as under Microsoft
C (5.1) for MS-DOS.  It should work with minimal modifications on most
Unix systems.
----------------- CUT HERE ------------------------
#!/bin/sh
# This is Remind 2.0, a shell archive (shar 3.32)
# made 11/01/1990 18:55 UTC by dfs@yar
# Source directory /enterprise/navigation/dfs/work/.rem/merged
#
# existing files will NOT be overwritten
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    368 -rw------- Makefile
#    643 -rw------- README.DOS
#    897 -rw------- README.UNIX
#   1609 -rw------- defines.h
#   5618 -rw------- dorem.c
#   9292 -rw------- dosubst.c
#   7981 -rw------- files.c
#   1315 -rw------- globals.h
#   3904 -rw------- init.c
#  18810 -rw------- main.c
#   8674 -rw------- nextdate.c
#   1377 -rw------- protos.h
#  22384 -rw------- remind.1
#    665 -rw------- remind.mak
#
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= Makefile ==============
if test X"$1" != X"-c" -a -f 'Makefile'; then
	echo "File already exists: skipping 'Makefile'"
else
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# Makefile for REMIND - simple file
X
X#WARNING: Don't remove the -DUNIX
XCFLAGS= -O -DUNIX
X
Xall: dorem.o files.o main.o nextdate.o init.o dosubst.o
X	$(LINK.c) -o remind dorem.o files.o main.o nextdate.o init.o dosubst.o
X
Xdorem.o: dorem.c
X
Xfiles.o: files.c
X
Xmain.o:  main.c
X
Xnextdate.o: nextdate.c
X
Xinit.o: init.c
X
Xdosubst.o: dosubst.c
X
Xclean:
X	rm -f *.o core *~ remind
X
SHAR_EOF
$TOUCH -am 1101133790 Makefile &&
chmod 0600 Makefile ||
echo "restore of Makefile failed"
set `wc -c Makefile`;Wc_c=$1
if test "$Wc_c" != "368"; then
	echo original size 368, current size $Wc_c
fi
fi
# ============= README.DOS ==============
if test X"$1" != X"-c" -a -f 'README.DOS'; then
	echo "File already exists: skipping 'README.DOS'"
else
sed 's/^X//' << 'SHAR_EOF' > README.DOS &&
XREMIND 2.0 for MS-DOS
X
XREMIND was originally written for MS-DOS.  To compile it, you need
Xthe Microsoft C compiler (at least version 5.1) and the Microsoft
XMAKE utility.
X
XTo compile the software, simply go to the source directory and type:
X
XMAKE REMIND.MAK
X
XNote that the MS-DOS version of REMIND operates slightly differently from
Xthe UNIX version:  MS-DOS has no concept of file access date.  Thus, to
Ximplement the "ONCE" keyword, REMIND will change the modification date
Xof the top-level reminder file after it has run.  This is equivalent to
Xperforming a "touch" of the file after running REMIND.
X--
XDavid F. Skoll <dfs@doe.carleton.ca>
X
SHAR_EOF
$TOUCH -am 1101134590 README.DOS &&
chmod 0600 README.DOS ||
echo "restore of README.DOS failed"
set `wc -c README.DOS`;Wc_c=$1
if test "$Wc_c" != "643"; then
	echo original size 643, current size $Wc_c
fi
fi
# ============= README.UNIX ==============
if test X"$1" != X"-c" -a -f 'README.UNIX'; then
	echo "File already exists: skipping 'README.UNIX'"
else
sed 's/^X//' << 'SHAR_EOF' > README.UNIX &&
XREMIND version 2.0 for UNIX
X
XTo compile REMIND, just go to the source directory and type "make".
XThis creates the executable file "remind".  You can type  "make CC=gcc"
Xif you want to use the Gnu C Compiler.
X
XThe Makefile is very simply and naive.
X
XREMIND has only been compiled on Sun3s and Sun4s under Sun OS 4.0.
XHowever, it makes very few system calls and should work on most BSD systems.
XIt shouldn't take too much hacking to get REMIND to work on almost any
Xsystem.  The troublesome files are main.c which gets the current system
Xdate, and files.c which opens and closes files.
X
XOnce remind has been compiled, install it in your favourite system directory.
XThe manual is in the file "remind.1".  You can install it in the appropriate
Xman page directory.  Remember to change the file suffix if you install it
Xin the "l" (local) or "n" (new) directory.
X--
XDavid F. Skoll <dfs@doe.carleton.ca>
SHAR_EOF
$TOUCH -am 1101133690 README.UNIX &&
chmod 0600 README.UNIX ||
echo "restore of README.UNIX failed"
set `wc -c README.UNIX`;Wc_c=$1
if test "$Wc_c" != "897"; then
	echo original size 897, current size $Wc_c
fi
fi
# ============= defines.h ==============
if test X"$1" != X"-c" -a -f 'defines.h'; then
	echo "File already exists: skipping 'defines.h'"
else
sed 's/^X//' << 'SHAR_EOF' > defines.h &&
X/***************************************************************/
X/*                                                             */
X/*  DEFINES.H                                                  */
X/*                                                             */
X/*  Contains macros and #defines for REMIND program.           */
X/*                                                             */
X/*  By David Skoll - 30 Sept 1990.                             */
X/*                                                             */
X/***************************************************************/
X
X/* User-definable variables.  BASE *must* be a year for which the
X   first of January is a Monday!!!  FOMITSIZE and POMITSIZE control
X   the number of fully-specified (dd-mm-yy) and partially-specified
X   (dd-mm) holidays. */
X#define BASE 1990
X#define FOMITSIZE 100
X#define POMITSIZE 75
X
X/* Useful macros */
X
X#define upper(c) ( ((c) >= 'a' && (c) <= 'z') ? ((c)-32) : (c) )
X#define DaysInYear(y) (((y) % 4) ? 365 : ((!((y) % 100) && ((y) % 400)) ? 365 : 366 ))
X#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
X#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
X
X/* Bit masks for constraint map */
X#define DAY_M 1
X#define MONTH_M 2
X#define YEAR_M 4
X#define WKDAY_M 8
X
Xenum Token_t { Unknown_t, Year_t, Month_t, Day_t, WkDay_t, Msg_t, Run_t,
X	       Omit_t, Banner_t, Rem_t, Delta_t, Back_t, Once_t, Include_t, Eol_t };
X			   
X/* Define the Token structure */
X
Xtypedef struct {
X   char *str;
X   enum Token_t type;
X   int val;
X} Token;
X#ifndef UNIX
X
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 defines.h &&
chmod 0600 defines.h ||
echo "restore of defines.h failed"
set `wc -c defines.h`;Wc_c=$1
if test "$Wc_c" != "1609"; then
	echo original size 1609, current size $Wc_c
fi
fi
# ============= dorem.c ==============
if test X"$1" != X"-c" -a -f 'dorem.c'; then
	echo "File already exists: skipping 'dorem.c'"
else
sed 's/^X//' << 'SHAR_EOF' > dorem.c &&
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif UNIX
X#include <ctype.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X/***************************************************************/
X/*                                                             */
X/*  int DoRem(char **s)                                        */
X/*                                                             */
X/*  Process a reminder.  Return 0 if reminder not emitted,     */
X/*  positive if emitted, or negative if in the past and        */
X/*  will never be emitted.                                     */
X/*                                                             */
X/***************************************************************/
X#ifndef UNIX
Xint DoRem(char **s)
X#else UNIX
Xint DoRem(s)
Xchar **s;
X     
X#endif UNIX
X{
X   int d, m, y, wd, cons, delta, back, omit, done, i, jul, once;
X   int d2, m2, y2;
X   Token tok;
X   int trigger;
X
X   d = m = y = back = delta = -1;
X   cons = wd = omit = once = 0;
X
X
X   done = 0;
X   while (!done) {
X      tok = ParseToken(s);
X      switch (tok.type) {
X	 case Eol_t:
X	    Eprint("Missing MSG or RUN in reminder.\n");
X	    return 0;
X
X	 case Omit_t:
X	 case Run_t:
X	 case Msg_t: done = 1; break;
X
X	 case Unknown_t:
X	    Eprint("Unknown token %s in reminder.\n", tok.str);
X	    return 0;
X
X	 case Banner_t:
X	    Eprint("BANNER can't be used here.\n");
X	    return 0;
X
X	 case WkDay_t:
X	    if (wd & (1 << tok.val)) {
X	       Eprint("%s duplicated.\n", tok.str);
X	       return 0;
X	    }
X	    wd |= 1 << tok.val;
X	    cons |= WKDAY_M;
X	    break;
X
X	 case Year_t:
X	    if (y != -1) {
X	       Eprint("Year specified twice.\n");
X	       return 0;
X	    }
X	    y = tok.val;
X	    cons |= YEAR_M;
X	    break;
X
X	 case Month_t:
X	    if (m != -1) {
X	       Eprint("Month specified twice.\n");
X	       return 0;
X	    }
X	    m = tok.val;
X	    cons |= MONTH_M;
X	    break;
X
X	 case Day_t:
X	    if (d != -1) {
X	       Eprint("Day specified twice.\n");
X	       return 0;
X	    }
X	    d = tok.val;
X	    cons |= DAY_M;
X	    break;
X
X	 case Delta_t:
X	    if (delta != -1) {
X	       Eprint("Delta specified twice.\n");
X	       return 0;
X	    }
X	    delta = tok.val;
X	    break;
X
X	 case Back_t:
X	    if (back != -1) {
X	       Eprint("Back specified twice.\n");
X	       return 0;
X	    }
X	    back = tok.val;
X	    break;
X
X	 case Once_t:
X	    if (once) {
X	       Eprint("ONCE specified twice.  (How's that for an error message??)\n");
X	       return 0;
X	    }
X	    once = 1;
X	    break;
X
X	 default:
X	    Eprint("Software error: Token %s, type %d, val %d",
X		    tok.str, tok.type, tok.val);
X	    return 0;
X      }
X   }
X   if (tok.type == Omit_t) {
X      done = 0;
X      while (!done) {
X	 tok = ParseToken(s);
X	 switch(tok.type) {
X
X	    case Msg_t:
X	    case Run_t: done = 1; break;
X
X	    case Eol_t:
X	       Eprint("Missing MSG or RUN in reminder.\n");
X	       return 0;
X
X	    case WkDay_t:
X	       if (omit & (1 << tok.val)) {
X		  Eprint("%s duplicated.\n", tok.str);
X		  return 0;
X	       }
X	       omit |= 1 << tok.val;
X	       break;
X
X	    default:
X	       Eprint("Only weekdays are valid after a local OMIT.\n");
X	       return 0;
X	 }
X      }
X   }
X   
X   if (d != -1 && m != -1 && CheckDate(d, m, y)) {
X      Eprint("Illegal date specification.\n");
X      return 0;
X   }
X   if (y != -1 && (y < BASE || y > BASE + 85)) {
X      Eprint("Illegal date specification.\n");
X      return 0;
X   }
X
X   /* Print some helpful stuff if debugging */
X   if (Debug) {
X      if (back > 7) Eprint("Warning: 'back' > 7 could slow execution severely.\n");
X      if (delta > 30 && (omit || NumFullOmit || NumPartOmit))
X	 Eprint("Warning: 'delta' > 30 with OMITs could slow execution severely.\n");
X   }
X
X   if (omit == 127) {
X      Eprint("Can't omit every day!\n");
X      return 0;
X   } 
X
X   if (IgOnce) once = 0; 
X   
X   /* Check if we can quickly determine reminder is not to be emitted */
X   if (once && !Debug && !Purge && (LastRun == JulianToday)) return 0;
X
X   i = TryNextDate(&d2, &m2, &y2, CurDay, CurMon, CurYear,
X		   d, m, y, wd, cons, 0);
X   if (i) {
X      if (Debug) Eprint("Reminder has expired.\n");
X      return -1;
X   }
X
X   /* Tweak the back and delta values to their defaults */
X   if (back == -1) back = 0;
X   if (delta == -1) delta = 0;
X
X   jul = Julian(d2, m2, y2);
X   if (back) {
X      jul = MoveBack(jul, back, d2, m2, y2, omit);
X      while (jul < JulianToday) {
X	 i = TryNextDate(&d2, &m2, &y2, d2, m2, y2,
X			 d, m, y, wd, cons, 1);
X	 if (i) {
X	    if (Debug) Eprint("Reminder has expired.\n");
X	    return -1;
X	 }
X         jul = Julian(d2, m2, y2);
X	 jul = MoveBack(jul, back, d2, m2, y2, omit);
X      }
X      FromJulian(jul, &d2, &m2, &y2);
X   }
X   
X   /* Figure out if the reminder should be triggered */
X   
X   trigger = MoveBack(jul, delta, d2, m2, y2, omit);
X
X   if(Debug) {
X      Eprint("%sTrigger date: %s, %d %s, %d.\n", 
X              (trigger <= JulianToday ? "*" : ""), DayName[jul % 7], 
X	      d2, MonthName[m2], y2);
X      return 0;
X   }
X   if (Purge || (once && (LastRun == JulianToday))) return 0;
X   while (isspace(**s)) (*s)++;
X   if (trigger <= JulianToday && !(tok.type == Run_t && IgRun)) { /* Trigger a reminder */
X      if (NumEmitted == 0 && !Purge && !Debug) {
X         DoSubst(Banner, WorkBuf, CurDay, CurMon, CurYear, JulianToday, Msg_t);
X	 printf("%s\n", WorkBuf);
X      }
X      
X      DoSubst(*s, WorkBuf, d2, m2, y2, jul, tok.type);
X      if (tok.type == Msg_t) printf("%s\n", WorkBuf);
X      else if (tok.type == Run_t) system(WorkBuf);
X      else Eprint("Error: Invalid token type %d\n", tok.type);
X      return 1;
X   } else return 0;
X
X}
X#ifndef UNIX
X
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 dorem.c &&
chmod 0600 dorem.c ||
echo "restore of dorem.c failed"
set `wc -c dorem.c`;Wc_c=$1
if test "$Wc_c" != "5618"; then
	echo original size 5618, current size $Wc_c
fi
fi
# ============= dosubst.c ==============
if test X"$1" != X"-c" -a -f 'dosubst.c'; then
	echo "File already exists: skipping 'dosubst.c'"
else
sed 's/^X//' << 'SHAR_EOF' > dosubst.c &&
X#include <ctype.h>
X#include <stdio.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X
X/***************************************************************/
X/*                                                             */
X/*  DOSUBST.C                                                  */
X/*                                                             */
X/*  Performs line substitution for reminders.                  */
X/*                                                             */
X/***************************************************************/
X
Xstatic char TODAY[] = "today";
Xstatic char TOMORROW[] = "tomorrow";
X
X#ifndef UNIX
Xint DoSubst(char *src, char *dst, int d, int m, int y, int jul, enum Token_t t)
X#else UNIX
Xint DoSubst(src, dst, d, m, y, jul, t)
X     char *src;
X     char *dst;
X     int d;
X     int m;
X     int y;
X     int jul;
X     enum Token_t t;
X#endif UNIX
X{
X   int diff = jul - JulianToday;
X   char c;
X   char *od;
X   int wkday = jul % 7;
X   char *plu;
X   int done;
X#ifndef UNIX
X   
X#else UNIX
X   char *origDst = dst;
X      
X#endif UNIX
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:  plu = "rd"; break;
X      
X      default: plu = "th"; break;
X   }
X      
X   
X   while (c = *src++) {
X#ifndef UNIX
X      if (c != '%') *dst++ = c;
X      else {
X#else UNIX
X     if (c != '%') { *dst++ = c; *dst = 0; }
X     else {
X#endif UNIX
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 'L':
X	       case 'U':
X#ifndef UNIX
X	       case 'V': dst += sprintf(dst, "%s", (diff ? TOMORROW : TODAY));
X	                 done = 1;
X#else UNIX
X	       case 'V': (void) sprintf(dst, "%s", (diff ? TOMORROW : TODAY));
X		         dst = origDst + strlen(origDst);
X		         done = 1;
X#endif UNIX
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 'A':
X#ifndef UNIX
X               dst += sprintf(dst, "on %s, %d %s, %d", DayName[wkday], d,
X#else UNIX
X               (void) sprintf(dst, "on %s, %d %s, %d", DayName[wkday], d,
X#endif UNIX
X		              MonthName[m], y);
X#ifdef UNIX
X               dst = origDst + strlen(origDst);
X#endif UNIX
X	       break;
X	       
X	    case 'B':
X#ifndef UNIX
X               dst += sprintf(dst, "in %d days' time", diff);
X	       break;
X#else UNIX
X               (void) sprintf(dst, "in %d days' time", diff);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'C':
X#ifndef UNIX
X               dst += sprintf(dst, "on %s", DayName[wkday]);
X#else UNIX
X               (void) sprintf(dst, "on %s", DayName[wkday]);
X               dst = origDst + strlen(origDst);
X#endif UNIX
X	       break;
X	       
X	    case 'D':
X#ifndef UNIX
X	       dst += sprintf(dst, "%d", d);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%d", d);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'E':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %02d/%02d/%04d", d, m+1, y);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "on %02d/%02d/%04d", d, m+1, y);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'F':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %02d/%02d/%04d", m+1, d, y);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "on %02d/%02d/%04d", m+1, d, y);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X
X	    case 'G':
X#ifndef UNIX
X               dst += sprintf(dst, "on %s, %d %s", DayName[wkday], d, MonthName[m]);
X	       break;
X#else UNIX
X               (void) sprintf(dst, "on %s, %d %s", DayName[wkday], d, MonthName[m]);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'H':
X#ifndef UNIX
X               dst += sprintf(dst, "on %02d/%02d", d, m+1);
X	       break;
X#else UNIX
X               (void) sprintf(dst, "on %02d/%02d", d, m+1);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X
X	    case 'I':
X#ifndef UNIX
X               dst += sprintf(dst, "on %02d/%02d", m+1, d);
X	       break;
X#else UNIX
X               (void) sprintf(dst, "on %02d/%02d", m+1, d);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'J':
X#ifndef UNIX
X               dst += sprintf(dst, "on %s, %s %d%s, %d", DayName[wkday],
X#else UNIX
X               (void) sprintf(dst, "on %s, %s %d%s, %d", DayName[wkday],
X#endif UNIX
X		              MonthName[m], d, plu, y);
X#ifdef UNIX
X               dst = origDst + strlen(origDst);
X#endif UNIX
X               break;
X	    
X	    case 'K':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %s, %s %d%s", DayName[wkday],
X#else UNIX
X	       (void) sprintf(dst, "on %s, %s %d%s", DayName[wkday],
X#endif UNIX
X	                      MonthName[m], d, plu);
X#ifndef UNIX
X	       break;
X#else UNIX
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       		      
X            case 'L':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %04d/%02d/%02d", y, m+1, d);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "on %04d/%02d/%02d", y, m+1, d);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'M':
X#ifndef UNIX
X	       dst += sprintf(dst, "%s", MonthName[m]);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%s", MonthName[m]);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'N':
X#ifndef UNIX
X	       dst += sprintf(dst, "%d", m+1);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%d", m+1);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X
X	    case 'O':
X#ifndef UNIX
X               if (RealToday == JulianToday) dst += sprintf(dst, " (today)");
X#else UNIX
X               if (RealToday == JulianToday) (void) sprintf(dst, " (today)");
X               dst = origDst + strlen(origDst);
X#endif UNIX
X               break;
X	       
X	    case 'P':
X#ifndef UNIX
X	       dst += sprintf(dst, (diff == 1 ? "" : "s"));
X	       break;
X#else UNIX
X	       (void) sprintf(dst, (diff == 1 ? "" : "s"));
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'Q':
X#ifndef UNIX
X	       dst += sprintf(dst, (diff == 1 ? "'s" : "s'"));
X	       break;
X#else UNIX
X	       (void) sprintf(dst, (diff == 1 ? "'s" : "s'"));
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'R':
X#ifndef UNIX
X	       dst += sprintf(dst, "%02d", d);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%02d", d);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'S':
X#ifndef UNIX
X	       dst += sprintf(dst, plu);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, plu);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'T':
X#ifndef UNIX
X	       dst += sprintf(dst, "%02d", m+1);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%02d", m+1);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'U':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %s, %d%s %s, %d", DayName[wkday], d,
X#else UNIX
X	       (void) sprintf(dst, "on %s, %d%s %s, %d", DayName[wkday], d,
X#endif UNIX
X	                      plu, MonthName[m], y);
X#ifndef UNIX
X	       break;
X#else UNIX
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'V':
X#ifndef UNIX
X	       dst += sprintf(dst, "on %s, %d%s %s", DayName[wkday], d, plu,
X#else UNIX
X	       (void) sprintf(dst, "on %s, %d%s %s", DayName[wkday], d, plu,
X#endif UNIX
X	                      MonthName[m]);
X#ifndef UNIX
X	       break;
X#else UNIX
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'W':
X#ifndef UNIX
X	       dst += sprintf(dst, DayName[wkday]);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, DayName[wkday]);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'X':
X#ifndef UNIX
X	       dst += sprintf(dst, "%d", diff);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%d", diff);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'Y':
X#ifndef UNIX
X	       dst += sprintf(dst, "%d", y);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%d", y);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    case 'Z':
X#ifndef UNIX
X	       dst += sprintf(dst, "%d", y % 100);
X	       break;
X#else UNIX
X	       (void) sprintf(dst, "%d", y % 100);
X	       dst = origDst + strlen(origDst);
X               break;
X#endif UNIX
X	       
X	    default:
X	       *dst++ = c;
X#ifndef UNIX
X         }
X#else UNIX
X	       *dst = 0;
X	 }
X#endif UNIX
X	 if (isupper(c)) *od = upper(*od);
X      }
X   }
X   if (t == Msg_t) *dst++ = '\n';
X   *dst = 0;
X   return 0;
X}   
X#ifndef UNIX
X
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 dosubst.c &&
chmod 0600 dosubst.c ||
echo "restore of dosubst.c failed"
set `wc -c dosubst.c`;Wc_c=$1
if test "$Wc_c" != "9292"; then
	echo original size 9292, current size $Wc_c
fi
fi
# ============= files.c ==============
if test X"$1" != X"-c" -a -f 'files.c'; then
	echo "File already exists: skipping 'files.c'"
else
sed 's/^X//' << 'SHAR_EOF' > files.c &&
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif UNIX
X#include <string.h>
X#include <malloc.h>
X#ifndef UNIX
X#include <dos.h>
X#endif UNIX
X#include <fcntl.h>
X#ifdef UNIX
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X#endif UNIX
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
X#ifndef UNIX
Xstatic int PopFile(void);
X#else UNIX
Xstatic int PopFile();
X#endif UNIX
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#ifndef UNIX
Xvoid OpenFile(char *s)
X#else UNIX
Xvoid OpenFile(s)
X     char *s;
X     
X#endif UNIX
X{
X   unsigned date, time;
X#ifndef UNIX
X   unsigned handle;
X#endif UNIX
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 UNIX
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 UNIX
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 UNIX
X   t1 = localtime(&(t.st_atime));
X#endif UNIX
X   
X#ifndef UNIX
X   if (y < BASE) LastRun = 0; else LastRun = Julian(d, m-1, y);
X   _dos_close(handle);
X#else UNIX
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 UNIX
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#ifndef UNIX
Xvoid DoInclude(char **s)
X#else UNIX
Xvoid DoInclude(s)
X     char **s;
X     
X#endif UNIX
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 UNIX
X   stack[SP].offset = ftell(fp);
X#endif UNIX
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#ifndef UNIX
Xstatic int PopFile(void)
X#else UNIX
Xstatic int PopFile()
X#endif UNIX
X{
X#ifndef UNIX
X   unsigned handle, date, time;
X   struct dostime_t t;
X#endif UNIX
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", 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 UNIX
X   if (!SP) return -1;
X         
X#endif UNIX
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", stack[SP].name, FileName);
X      exit(1);
X   }
X#ifndef UNIX
X   if (fseek(fp, stack[SP].offset, SEEK_SET)) {
X#else UNIX
X   if (fseek(fp, stack[SP].offset, 0)) {
X#endif UNIX
X      Eprint("Argh! Can't fseek %s after returning from INCLUDE file %s", 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   
X   Fresh = 1;
X   while (!done) {
X      CurLine++;
X      if (fgets(Line, 256, fp) == NULL) {
X         if (ferror(fp)) Eprint("Error reading %s", FileName);
X	 if (PopFile()) return 1;
X      } else {
X         /* Remove the newline */
X         if (*Line && (*(Line + strlen(Line)-1)=='\n'))
X            *(Line + strlen(Line)-1) = 0;
X         done = 1;
X      }	 
X   }
X   return 0;
X}
X
X#ifndef UNIX
X/***************************************************************/
X/*                                                             */
X/*  TopLevel - Returns 1 if current file is top level, 0       */
X/*  if it is INCLUDEd.                                         */
X/*                                                             */
X/***************************************************************/
Xint TopLevel(void) { return (SP == 0); }
X
X#else UNIX
Xint TopLevel()
X{
X  return (SP == 0);
X}
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 files.c &&
chmod 0600 files.c ||
echo "restore of files.c failed"
set `wc -c files.c`;Wc_c=$1
if test "$Wc_c" != "7981"; then
	echo original size 7981, current size $Wc_c
fi
fi
# ============= globals.h ==============
if test X"$1" != X"-c" -a -f 'globals.h'; then
	echo "File already exists: skipping 'globals.h'"
else
sed 's/^X//' << 'SHAR_EOF' > globals.h &&
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 int   Fresh;
Xextern int   Purge;
Xextern int   Debug;
Xextern int   Verbose;
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 int   IgRun;
X#ifndef UNIX
Xextern int   IgOnce;
X#else UNIX
Xextern int   IgOnce;
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 globals.h &&
chmod 0600 globals.h ||
echo "restore of globals.h failed"
set `wc -c globals.h`;Wc_c=$1
if test "$Wc_c" != "1315"; then
	echo original size 1315, current size $Wc_c
fi
fi
# ============= init.c ==============
if test X"$1" != X"-c" -a -f 'init.c'; then
	echo "File already exists: skipping 'init.c'"
else
sed 's/^X//' << 'SHAR_EOF' > init.c &&
X#include <stdio.h>
X#ifndef UNIX
X#include <stdlib.h>
X#endif UNIX
X#include <string.h>
X#include "defines.h"
X#include "globals.h"
X#include "protos.h"
X
Xstatic char ErrMsg[] = "\n\t\tREMIND version 2.0 (C) 1990 by David Skoll.\n\nUsage: REMIND [-p | -d] [-v] [-o] [-r] filename [date]\n\n";
Xstatic char DPMsg[] = "Debug and Purge options conflict - Purge chosen.\n";
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#ifndef UNIX
Xvoid initialize(int argc, char *argv[])
X#else UNIX
Xvoid initialize(argc, argv)
X     int argc;
X     char *argv[];
X#endif UNIX
X{
X   int i;
X   char *s;
X   int d, m, y;
X   Token tok;
X
X   Debug   = 0;
X   Purge   = 0;
X   Verbose = 0;
X   IgOnce  = 0;
X   IgRun   = 0;
X
X   if(argc == 1) {
X      fprintf(stderr, ErrMsg);
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 'P':  Purge = 1;
X                     if (Debug) {
X                        Debug = 0;
X                        fprintf(stderr, DPMsg);
X                     }
X		     break;
X		     
X            case 'D': Debug = 1;
X	              if (Purge) {
X		         Debug = 0;
X			 fprintf(stderr, DPMsg);
X		      }
X		      break;
X
X	    case 'V': Verbose = 1; break;
X
X	    case 'O': IgOnce = 1; break;
X
X	    case 'R': IgRun = 1; break;
X	    
X	    default: fprintf(stderr, "Unknown option '%c' ignored.\n", *s);
X	 }			      
X      }
X      i++;
X      if (i >= argc) {
X         fprintf(stderr, ErrMsg);
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 UNIX
X      fprintf(stderr, "\nFile %s last accessed on %s, %d %s, %d\n", FileName,
X#endif UNIX
X                       DayName[LastRun % 7], d, MonthName[m], y);
X   }	       
X   return;
X}
X#ifndef UNIX
X      
X#endif UNIX
SHAR_EOF
$TOUCH -am 1024164690 init.c &&
chmod 0600 init.c ||
echo "restore of init.c failed"
set `wc -c init.c`;Wc_c=$1
if test "$Wc_c" != "3904"; then
	echo original size 3904, current size $Wc_c
fi
fi
echo "End of part 1, continue with part 2"
exit 0