REMIND 2.3.0 3/4

David F. Skoll dfs at doe.carleton.ca
Tue Feb 19 05:18:48 AEST 1991


#!/bin/sh
# This is part 03 of Remind 2.3.0
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= omits.c ==============
if test X"$1" != X"-c" -a -f 'omits.c'; then
	echo "File already exists: skipping 'omits.c'"
else
echo "x - extracting omits.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > omits.c &&
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}
SHAR_EOF
$TOUCH -am 0218130591 omits.c &&
chmod 0600 omits.c ||
echo "restore of omits.c failed"
set `wc -c omits.c`;Wc_c=$1
if test "$Wc_c" != "10630"; then
	echo original size 10630, current size $Wc_c
fi
fi
# ============= protos.h ==============
if test X"$1" != X"-c" -a -f 'protos.h'; then
	echo "File already exists: skipping 'protos.h'"
else
echo "x - extracting protos.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > protos.h &&
X
X/* Function Prototypes go here */
X
X#ifdef __STDC__
Xint   CheckDate   (int d,   int m,  int y);
Xvoid  ClearOmitContext(void);
Xvoid  CopyInts    (int *from, int *to, int count);
Xint   DoBanner    (char **s);
Xvoid  DoCalendar  (void);
Xint   DoGlobalOmit(char **s);
Xvoid  DoInclude   (char **s);
Xint   DoRem       (char **s);
Xint   DoSubst     (char *src, char *dst, int d, int m, int y, int jul, enum Token_t t, int tim, int mode);
Xint   FindTodaysDate(int *d, int *m, int *y);
Xvoid  FreeStackedOmits (void);
Xint   FromJulian  (int jul, int *d, int *m, int *y);
Xint   GetTriggerDate (int d, int m, int y, int wd, int cons, int back, int repeat, int omit, int skip);
Xint   IsOmitted   (int jul, int localomit);
Xvoid  HandleQueuedAts(void);
Xvoid  initialize  (int argc, char *argv[]);
Xint   int_comp    (int *, int *);
Xint   Julian      (int d,   int m,  int y);
Xint   MoveBack    (int jul, int back, int omit);
Xint   *my_bsearch (int key, int *array, int number);
Xvoid  OpenFile    (char *s);
Xvoid  Output      (char *s);
XToken ParseToken  (char **s);
Xint   PopOmitContext (void);
Xint   PushOmitContext (void);
Xint   ProcessLine (void);
Xint   ReadLine    (void);
Xvoid  SigIntHandler (void);
Xlong  SystemTime  (void);
Xint   TopLevel    (void);
Xint   TryNextDate(int *retday, int *retmon, int *retyr,
X		  int startday, int startmon, int startyr,
X		  int conday, int conmon, int conyr,
X		  int wkday, int cons, int inc);
X#else
Xint   CheckDate   ();
Xvoid  ClearOmitContext();
Xvoid  CopyInts    ();
Xint   DoBanner    ();
Xvoid  DoCalendar  ();
Xint   DoGlobalOmit();
Xvoid  DoInclude   ();
Xint   DoRem       ();
Xint   DoSubst     ();
Xint   FindTodaysDate();
Xvoid  FreeStackedOmits();
Xint   FromJulian  ();
Xint   GetTriggerDate();
Xint   IsOmitted   ();
Xvoid  HandleQueuedAts();
Xvoid  initialize  ();
Xint   int_comp    ();
Xint   Julian      ();
Xint   MoveBack    ();
Xint  *my_bsearch  ();
Xvoid  OpenFile    ();
Xvoid  Output      ();
XToken ParseToken  ();
Xint   PopOmitContext ();
Xint   ProcessLine ();
Xint   PushOmitContext ();
Xint   ReadLine    ();
Xvoid  SigIntHandler();
Xlong  SystemTime  ();
Xint   TopLevel    ();
Xint   TryNextDate ();
X#endif
X
X#ifndef UNIX
Xvoid  Eprint(const char *f, ...);
X#else
Xvoid  Eprint();
X#endif
SHAR_EOF
$TOUCH -am 0218130591 protos.h &&
chmod 0600 protos.h ||
echo "restore of protos.h failed"
set `wc -c protos.h`;Wc_c=$1
if test "$Wc_c" != "2210"; then
	echo original size 2210, current size $Wc_c
fi
fi
# ============= remind-all.csh ==============
if test X"$1" != X"-c" -a -f 'remind-all.csh'; then
	echo "File already exists: skipping 'remind-all.csh'"
else
echo "x - extracting remind-all.csh (Text)"
sed 's/^X//' << 'SHAR_EOF' > remind-all.csh &&
X#!/bin/csh -f
X
X# Shell script to mail all users reminders.
X
X# Run it AFTER MIDNIGHT so that date is correct!
X# On our system, we have the following in our crontab:
X# 05 5 * * * /usr/share/lib/remind/remind-all > /dev/null 2>&1
X
X# Also, you MUST use the -r and -q options on REMIND, otherwise SEVERE
X# security hole could develop.  I recommend making this script
X# readable and executable only by root to minimize security problems.
X# DO NOT make the script setuid!
X
X# The following line gets a list of users for systems using SUN's
X# NIS service:
Xset USERS  = `ypcat passwd | awk -F: '{print $1}'`
X
X# The following line gets a list of users by examining /etc/passwd:
X# set USERS = `awk -F: '{print $1}' /etc/passwd`
X
X# If neither of the above methods works, you must come up with some
X# way of getting a list of users on the system
X
X# Set the following variables as appropriate for your system
Xset REMIND = /usr/local/bin/remind
Xset MAIL   = /usr/ucb/mail
Xset CMP    = /usr/bin/cmp
Xset RM     = "/usr/bin/rm -f"
X
Xset EMPTY  = /tmp/Empty.$$
Xset FULL   = /tmp/Full.$$
X
X# Create the dummy empty reminder file
X$REMIND -rq /dev/null > $EMPTY
X
X# Scan each user's directory for a .reminders file
Xforeach i ($USERS)
X   if (-r ~$i/.reminders) then
X
X#     echo "$i has a .reminders file."     DEBUGGING PURPOSES ONLY
X
X      $REMIND -rq ~$i/.reminders > $FULL
X      $CMP -s $EMPTY $FULL
X      if ($status != 0) then
X
X#        echo "Sending mail to $i"         DEBUGGING PURPOSES ONLY
X
X         $MAIL -s "Reminders" $i < $FULL
X      endif
X      $RM $FULL
X   endif
Xend
X
X$RM $EMPTY
SHAR_EOF
$TOUCH -am 0218130591 remind-all.csh &&
chmod 0600 remind-all.csh ||
echo "restore of remind-all.csh failed"
set `wc -c remind-all.csh`;Wc_c=$1
if test "$Wc_c" != "1568"; then
	echo original size 1568, current size $Wc_c
fi
fi
# ============= remind-all.sh ==============
if test X"$1" != X"-c" -a -f 'remind-all.sh'; then
	echo "File already exists: skipping 'remind-all.sh'"
else
echo "x - extracting remind-all.sh (Text)"
sed 's/^X//' << 'SHAR_EOF' > remind-all.sh &&
X# Shell script to mail all users reminders.
X
X# Thanks to Bill Aten for this script.
X
X# Run it AFTER MIDNIGHT so that date is correct!
X# On our system, we have the following in our crontab:
X# 02 00 * * * /usr/local/adm/remind-all >/dev/null 2>&1
X
X# Also, you MUST use the -r and -q options on REMIND, otherwise a SEVERE
X# security hole could develop.  I recommend making this script
X# readable and executable only by root to minimize security problems.
X# DO NOT make the script setuid!
X
X# The following line gets a list of users for systems using SUN's
X# NIS service:
X# USERS=`ypcat passwd | awk -F: '{print $1}'`
X
X# The following line gets a list of users by examining /etc/passwd:
XUSERS=`awk -F: '{print $1}' /etc/passwd`
X
X# If neither of the above methods works, you must come up with some
X# way of getting a list of users on the system
X
X# Set the following variables as appropriate for your system
XREMIND=/usr/local/bin/remind
XMAIL=/usr/bin/mail
XCMP=/bin/cmp
XRM="/bin/rm -f"
X
XEMPTY=/tmp/Empty.$$
XFULL=/tmp/Full.$$
X
X# Create the dummy empty reminder file
X$REMIND -rq /dev/null > $EMPTY
X
X# Scan each user's directory for a .reminders file
Xfor i in $USERS
Xdo
XHOME=`grep \^$i: /etc/passwd | awk -F: '{print $6}'`
X   if [ -r $HOME/.reminders ]; then
X
X#     echo "$i has a .reminders file."     DEBUGGING PURPOSES ONLY
X
X      $REMIND -rq $HOME/.reminders > $FULL
X      if `$CMP -s $EMPTY $FULL`; then
X         : do nothing
X      else
X
X#        echo "Sending mail to $i"         DEBUGGING PURPOSES ONLY
X
X         $MAIL -s "Reminders" $i < $FULL
X      fi
X      $RM $FULL
X   fi
Xdone
X
X$RM $EMPTY
SHAR_EOF
$TOUCH -am 0218130591 remind-all.sh &&
chmod 0600 remind-all.sh ||
echo "restore of remind-all.sh failed"
set `wc -c remind-all.sh`;Wc_c=$1
if test "$Wc_c" != "1589"; then
	echo original size 1589, current size $Wc_c
fi
fi
# ============= remind.1 ==============
if test X"$1" != X"-c" -a -f 'remind.1'; then
	echo "File already exists: skipping 'remind.1'"
else
echo "x - extracting remind.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > remind.1 &&
X.TH REMIND 1 "18 February 1991"
X.UC 4
X.SH NAME
Xremind \- a sophisticated reminder service
X.SH SYNOPSIS
X.B remind
X[\fB\-n | \fB\-d\fR | \fB\-p\fR | \fB\-c\fR\fIn\fR [\fB\-w\fR\fIn\fR | \fB\-s\fR]]
X[\fB\-voraq\fR]
X.I filename
X[\fIdate\fR]
X.SH DESCRIPTION
X.B remind
Xreads the supplied
X.I filename
Xfor a list of reminders, and then issues reminders appropriate for
Xthe current date.
XEach reminder can consist of a message sent to standard output, or
Xa program to be executed.
X.SH OPTIONS
X.TP
X.B \-n
XThe \fB\-n\fR flag causes \fBremind\fR to print the \fInext\fR occurrence
Xof each reminder in a simple calendar format.  You can sort this by date by
Xpiping the output through \fBsort(1)\fR.
X.TP
X.B \-d
XThe
X.B \-d
Xflag causes
X.B remind
Xto enter a debugging mode.
XIn this mode, each reminder in the reminder file is examined and its
Xtrigger date is printed to standard output.  Also, some consistency
Xchecking is done, and warnings are issued about constructs which could
Xresult in slow execution times.
X.TP
X.B \-p
XThe
X.B \-p
Xflag causes
X.B remind
Xto go into
X.I purge
Xmode.  This mode is incompatible with the
X.B \-d
Xflag.  It causes
X.B remind
Xto scan the reminder file and echo all reminders which have not expired
Xto the standard output.  All expired reminders are echoed to the standard
Xerror stream.
X.TP
X.B \-c\fR\fIn\fR
XThe
X.B \-c
Xflag causes \fBremind\fR to generate a calendar with reminders indicated on
Xthe calendar.  The calendar printout is formatted and sent to standard output.
XIf you supply a number \fIn\fR from 1 to 12, 
Xthen a calendar will be generated for
X\fIn\fR months, starting with the current month.  The default is to
Xproduce a calendar for one month only.  The
X.B \-c
Xoption is not compatible with the \fB\-p\fR or \fB\-d\fR options.
X.TP
X.B \-w\fR\fIn\fR
XThe \fB\-w\fR flag, when used in conjunction with \fB\-c\fR,
Xcauses \fBremind\fR to format the calendar for a device
Xwhose output width is \fIn\fR columns.  The default is to produce a calendar
Xfor an 80-column device.
X.TP
X.B \-s
XThe \fB\-s\fB flag, when used in conjunction with \fB\-c\fR,
Xcauses \fBremind\fR to send an unformatted "simple
Xcalendar" listing to standard output.  This can be used if you wish to
Xformat the calendar with another program rather than using \fBremind\fR's
Xbuilt-in calendar format.
X.TP
X.B \-v
XThe
X.B \-v
Xflag causes the debugging messages caused by
X.B \-d
Xto be printed in a verbose manner.  Also, any error messages issued by
X.B remind
Xare printed in verbose format.
X.TP
X.B \-o
XThe
X.B \-o
Xflag causes
X.B remind
Xto ignore any
X.B ONCE
Xdirectives in the reminder file.
X.TP
X.B \-r
XThe
X.B \-r
Xflag causes
X.B remind
Xto ignore any
X.B RUN
Xdirectives in the reminder file.
X.TP
X.B \-a
XThe
X.B \-a
Xoption causes
X.B remind
Xnot to issue
X.B AT
Xreminders when the reminder file is read and processed, providing the
X.B AT
Xreminders would normally be queued.  This allows you to have
X.B remind
Xqueue
X.B AT
Xreminders without triggering them when it reads the file.
X.TP
X.B \-q
XThe
X.B \-q
Xflag causes
X.B remind
Xnot to queue
X.B AT
Xreminders for triggering later in the day.  This prevents
X.B remind
Xfrom running as a daemon and popping up reminders as they are triggered.
X.PP
XIf you supply a
X.I date
Xon the command line, it must consist of
X.I day month year
Xwhere
X.I day
Xis the day of the month,
X.I month
Xis (at least the first three letters of) the English name of the month,
Xand
X.I year
Xis a year (all 4 digits) from 1990 to 2075.  These components can appear
Xin any order, but must be separated by spaces.  If a date is supplied,
X.B remind
Xuses it rather than the actual system date as its notion of "today."  You
Xcan use this feature to test how reminders will appear in the future, or
Xto produce a calendar for any month you choose.
X.SH REMINDER FILES
X.B Remind
Xuses scripts called
X.I reminder files
Xto control its operation.  A sample reminder file is shown below:
X.PP
X.nf
X	#!/usr/local/bin/remind
X	# Sample file
X	BANNER Hi there.  Here are your reminders:
X
X	# First some birthdays
X	REM 6 Jan MSG David's birthday.
X	REM 23 Nov +10 MSG El's birthday is %a.
X
X	# Next some housekeeping
X	REM 1 -1 OMIT Sat Sun RUN do_backup
X
X	# Introduce some holidays
X	OMIT 25 Dec +2 MSG %"Christmas%" is %b.
X	OMIT  1 July MSG Canada Day
X	OMIT 22 Nov 1990	# American Thanksgiving
X
X	# Business things
X	REM 23 Nov 1990 +2 AT 10:00 +30 *15 MSG \\
X	%"Meeting with Bill%" at %1 %b.%_Bring papers!
X
X.fi
X.PP
XNote that
X.B remind
Xignores blank lines and lines beginning with "#".  If the character '\\'
Xis the LAST character on the line, it is treated as a continuation
Xcharacter.  You can string many lines together using this character.
XNote that if you use line continuation, then
X.B remind
Xuses the line number of the last line when printing diagnostic messages.
X.PP
XCommands, names of months, etc. can be typed in any
Xmixture of upper- and lower-case.  The convention I use is to
Xuse upper-case for
X.B remind
Xtokens like
X.I
XMSG, OMIT,
Xetc., and mixed case for other words like
X.I January
Xand
X.I Sunday.
X.SH THE REM COMMAND
XThe most-used command in a reminder file is the
X.I REM
Xcommand.  This command is used to denote a reminder.  There are many
Xforms of the
X.I REM
Xcommand; they are represented by:
X.PP
X.RS
XREM [ONCE] [\fIdate_spec\fR]
X[\fIdelta\fR]
X[\fIback\fR]
X[\fIrepeat\fR]
X[\fISKIP\fR | \fIBEFORE\fR | \fIAFTER\fR]
X[OMIT \fIomit_list\fR]
X[AT \fItime\fR [\fItdelta\fR] [\fItrepeat\fR]]
X[UNTIL \fIexpiry_date\fR]
XMSG|RUN
X.I body
X.RE
X.PP
XThe parts of the \fIREM\fR command can be specified in any order, except
Xthat the \fIbody\fR must come immediately after the \fIMSG\fR or \fIRUN\fR
Xkeyword.
X.SH DATE SPECIFICATIONS
XThe
X.I date_spec
Xconsists of zero to four parts.  These parts are
X.I day
X(day of month),
X.I month
X(month name),
X.I year
Xand
X.I weekday.
X.I Month
Xand
X.I weekday
Xare the English names of months and weekdays.  At least the first three
Xcharacters must be used.  Case is irrelevant.  The following are examples
Xof the various parts of a
X.I date_spec:
X.TP
X.I day:
X1, 22, 31, 14, 3
X.TP
X.I month:
XJANUARY, feb, March, ApR, may, Aug
X.TP
X.I year:
X1990, 1993, 2030, 95 (interpreted as 1995).  The year can range
Xfrom 1990 to 2075.
X.TP
X.I weekday:
XMonday, tue, Wed, THU, Friday, saturday, sundAy
X.PP
XNote that there can be several
X.I weekday
Xcomponents separated by spaces in a
X.I date_spec.
X.PP
X.B INTERPRETATION OF DATE SPECIFICATIONS
X.PP
XThe following examples show how date specifications are interpreted.
X.PP
X1. Null date specification - the reminder is triggered every day.
XThe trigger date for a specific run is simply the current system date.
X.PP
X2. Only
X.I day
Xpresent.  The reminder is triggered on the specified day of each month.
XThe trigger date for a particular run is the closest such day to the
Xcurrent system date.  For example:
X.nf
X	REM 1 MSG First of every month.
X	REM 31 MSG 31st of every month that has 31 days.
X.fi
X.PP
X3. Only
X.I month
Xpresent.  The reminder is triggered every day of the specified month.
XExample:
X.nf
X	REM Feb MSG Every day in February
X.fi
X.PP
X4.
X.I day
Xand
X.I month
Xpresent.  Examples:
X.nf
X	REM 6 Jan MSG Every 6th of January
X	REM Feb 29 MSG Every 29th of February
X.fi
X.PP
X5.  Only
X.I year
Xpresent. Example:
X.nf
X	REM 1991 MSG Every day in 1991
X.fi
X.PP
X6.
X.I year
Xand
X.I day
Xpresent.  Examples:
X.nf
X	REM 1 1990 MSG 1st of every month in 1990
X	REM 1992 23 MSG 23rd of every month in 1992
X.fi
X.PP
X7.
X.I year
Xand
X.I month
Xpresent.  Examples:
X.nf
X	REM Feb 1991 MSG Every day in Feb 1991
X	REM 1992 September MSG Every day in Sept 1992
X.fi
X.PP
X8.
X.I year, month
Xand
X.I day
Xpresent.  Examples:
X.nf
X	REM 8 Jan 1991 MSG 8th January 1991.
X	REM 1992 March 9 MSG 9th March 1992.
X.fi
X.PP
X9.
X.I weekday
Xonly.  Examples:
X.nf
X	REM Sat MSG Every Saturday
X	REM Mon Tue Wed Thu Fri MSG Every working day
X	REM Monday Wednesday MSG Every Monday and Wednesday
X.fi
X.PP
X10.
X.I weekday
Xand
X.I day
Xpresent.  Examples:
X.nf
X	REM Sat 1 MSG First Saturday of every month
X	REM Mon Tue Wed Thu Fri 15 \\
X		MSG 1st working day after 15th of every month
X.fi
X.PP
X11.
X.I weekday
Xand
X.I month
Xpresent.  Examples:
X.nf
X	REM Mon March MSG Every Monday in March
X	REM Mon Tue Wed Thu Fri Feb MSG Every working day in February
X.fi
X.PP
X12.
X.I weekday, month
Xand
X.I day
Xpresent.  Examples:
X.nf
X	REM Mon 1 March MSG First Monday in March
X	REM Sat Sun 15 July MSG First Sat or Sun on or after 15 July
X.fi
X.PP
X13.
X.I weekday
Xand
X.I year
Xpresent.  Example:
X.nf
X	REM Sat Sun 1991 MSG Every Saturday and Sunday in 1991
X.fi
X.PP
X14.
X.I weekday, day
Xand
X.I year
Xpresent.  Examples:
X.nf
X	REM Mon 15 1990 MSG 1st Mon after 15th of every month in 1990
X	REM Mon Tue Wed Thu Fri 1 1990 \\
X		MSG 1st working day of every month in 1990
X.fi
X.PP
X15.
X.I weekday, month
Xand
X.I year
Xpresent.  Example:
X.nf
X	REM Mon Wed 1991 Feb MSG Every Mon and Wed in Feb 1991.
X.fi
X.PP
X16.
X.I weekday, day, month
Xand
X.I year
Xpresent.  Example:
X.nf
X	REM Mon Tue Wed Thu Fri 28 Oct 1990 \\
X		MSG 1st working day on or after 28 October 1990.
X.fi
X.PP
XNote that when
X.I weekday
Xand
X.I day
Xare specified,
X.B remind
Xchooses the first date on or after the specified
X.I day
Xwhich also satisfies the
X.I weekday
Xconstraint.  It does this by picking the first date on or after the specified
X.I day
Xwhich is listed in the list of
X.I weekdays.
XThus, a reminder like:
X.PP
X.nf
X	REM Mon Tue 28 Oct 1990 MSG Hi
X.fi
X.PP
Xwould be issued only on Monday, 29 October, 1990.  It would not be issued
Xon Tuesday, 30 October, 1990, since the 29th is the first date to satisfy
Xthe
X.I weekday
Xconstraints.
X.PP
X.B BACKWARD SCANNING
X.PP
XSometimes, you need to incorporate "backward scanning" into your date
Xspecification.  This is accomplished with the
X.I back
Xpart of the reminder specification.  If present, the
X.I back
Xis specified as a number preceded by one or two minus signs.  This causes
X.B remind
Xto compute the trigger date as usual, and then move back the specified
Xnumber of days.  For example:
X.PP
X.nf
X	REM Mon 1 -7 MSG Last Monday of every month.
X.fi
X.PP
XThis works by finding the first Monday of every month, and then moving
X"back" seven days.  This results in the last Monday of the previous
Xmonth.  (Warning:  If you have global 
X.I OMITs, 
Xthis might not work - see
Xthe 
X.I OMIT 
Xcommand.  In this case, you can use the "definite \fIback\fR" which consists
Xof a number preceded by two minus signs like \-\-7)
X.PP
XAnother example is:
X.PP
X.nf
X	REM 1 -1 MSG Last day of every month.
X.fi
X.PP
XSince we don't know how many days are in a month, to get to the last day
Xof a month, we go to the first day of the next month and then move back
Xone day.
X.PP
X.B Remind
Xwill automatically scan forwards and backwards until it finds a suitable
Xtrigger date.  Note that large
X.I back
Xvalues can lead to a lot of backtracking and slow the execution of
X.B remind.
X.PP
X.B ADVANCE WARNING
X.PP
X.B Remind
Xcan warn you of an upcoming reminder as far in advance as you desire.  This
Xis specified by a
X.I delta,
Xwhich is a positive number preceded by one or two "+" signs.  For example:
X.PP
X.nf
X	REM 8 Jan 1992 +3 MSG Test1
X	REM 1 +1 MSG Test2
X.fi
X.PP
XThe reminder "Test1" will be triggered on the 8th of January, 1992, as well
Xas the 3 days preceding it.  All told, "Test1" will be triggered on the
X5th through the 8th of January, 1992.
X.PP
XThe reminder "Test2" will be triggered on the first day of every month, as
Xwell as the day before.  In effect, "Test2" will be triggered on the first
Xand last day of every month.
X.PP
X.I Delta
Xand
X.I back
Xcan be combined:
X.PP
X.nf
X	REM Mon 1 --7 ++3 MSG Test3
X.fi
X.PP
XThe reminder "Test3" will be triggered on the last Monday of every month,
Xas well as the three days preceding it.
X.PP
XSee "The OMIT Command" for an explanation of the distinction between the "-"
Xand "--" forms of the \fIback\fR, and the "+" and "++" forms of the
X\fIdelta\fR.
X.PP
X.B REPEATING REMINDERS
X.PP
XThe
X.I repeat
Xcomponent in a reminder is a number preceded by an asterisk; for example,
X"*14".  The
X.I repeat
Xis used to remind you of events which occur regularly after a specified
Xstart date.  For example, suppose you get paid every second Thursday,
Xand that your last payday was 8 November 1990.  The following reminder
Xwould remind you of each payday 3 days in advance:
X.PP
X.nf
X	REM 8 Nov 1990 *14 +3 MSG Payday is %b!
X.fi
X.PP
XThe
X.I repeat
Xcomponent of "*14" would cause the reminder to be triggered every 14
Xdays from the start date.
X.PP
XIn order to use a
X.I repeat,
Xyou must fully specify the start date.  That means that if
Xyou use a
X.I repeat,
Xyou must specify the
X.I month, day
Xand
X.I year.
XYou can also specify
X.I weekday, back
Xand
X.I local OMITs
Xbut these only affect the calculation of the start date.  Once the
Xstart date is calculated, the reminder is issued regularly every
X.I repeat
Xdays.  Note, in particular, that once the start date is calculated,
X.B remind
Xignores local and global
X.I OMITs
Xwhen calculating the trigger date.  The final trigger date is strictly
Xa multiple of
X.I repeat
Xdays from the start date.  The only exceptions are those cases covered
Xby the \fIBEFORE, AFTER\fR and \fISKIP\fR keywords, discussed in the
Xnext section.
X.PP
X.B THE BEFORE, AFTER AND SKIP KEYWORDS
X.PP
XShould a reminder's trigger date happen to fall on an OMITted day, the
Xdefault action is to go ahead and trigger the reminder anyway.  For example,
Xsuppose you have a meeting every Tuesday.  Consider the following:
X.PP
X.nf
X	OMIT 1 Jan MSG New Year's Day
X	OMIT 25 December MSG Christmas
X	REM Tue AT 13:00 MSG Code review meeting.
X.fi
X.PP
XEven though Christmas 1990 is on a Tuesday, you will still be reminded
Xof a code review meeting on that day.  Surely that is not desirable.  You
Xcan tell \fBremind\fR to skip reminders which fall on OMITted days with the
X.I SKIP
Xkeyword.  Assuming we have the same \fIOMIT\fR commands, then:
X.PP
X.nf
X	REM Tue SKIP AT 13:00 MSG Code review meeting.
X.fi
X.PP
Xwould be triggered every Tuesday, but not on Christmas or New Year's
XDay, because both of these holidays happen to fall on Tuesdays.
X.PP
XIn some situations, we don't want to skip the reminder; we want to move
Xit to just before or just after a holiday.  Consider these examples:
X.PP
X.nf
X	REM Fri AFTER OMIT Sat Sun MSG Do your backup!
X.fi
X.PP
XThis reminder is normally triggered every Friday.  However, if a particular
XFriday happens to be a holiday, it will be triggered on the Monday after
Xthe holiday instead.  Note that the \fIAFTER\fR keyword keeps scanning
Xforwards until it hits a day which is not in the global or local \fIOMIT\fR
Xlists.  (Of course, if both the Friday and Monday are holidays, then the
Xreminder would be deferred until Tuesday, and so on.)
X.PP
X.nf
X	REM Fri BEFORE MSG Do your backup!
X.fi
X.PP
XThis is similar, except that the reminder is issued on the previous Thursday
Xif a particular Friday happens to be a holiday.  This form, too, will
Xmove back to Wednesday if Thursday also happens to be a holiday, and so on.
X.PP
XNote:  These keywords skip both local and global \fIOMIT\fRs.  Also,
X\fBremind\fR will complain about certain illegal constructions, such as:
X.PP
X.nf
X	REM Mon SKIP OMIT Mon MSG Impossible!
X.fi
X.PP
XIf \fBremind\fR makes too many attempts to find a trigger date for
Xcertain forms, it assumes that no satisfiable date exists, and will warn
Xyou to check the date specification.  In practice, this should never happen.
X.PP
X.B THE UNTIL KEYWORD
X.PP
XThe \fIUNTIL\fR keyword can be used to force a reminder to expire at a
Xparticular date.  It must be followed by a \fIyear\fR, \fImonth\fR and
X\fIday\fR specifying the expiry date.  Here are some examples:
X.PP
XSuppose you have a conference from 25 Feb 1991 to 1 March 1991.  You
Xcould use:
X.PP
X.nf
X	REM 25 Feb 1991 *1 UNTIL 1 Mar 1991 MSG Conference today!
X.fi
X.PP
XYou must include the "*1" to have the reminder repeated every day
Xduring the conference.  If you did not include the "*1", then the
Xreminder would only have been issued on 25 Feb 1991.
X.PP
XSuppose you have jury duty from 4-15 March 1991, not including weekends.
XSuppose further that you wish to be reminded of this duty starting on 1
XMarch 1991 (hence the "++3"):  (See the section "Substitution" for an
Xexplanation of all the percent signs.)
X.PP
X.nf
X	REM 4 Mar 1991 ++3 *1 OMIT Sat Sun SKIP UNTIL 15 Mar 1991 \\
X	MSG %"Jury duty%" %b.
X.fi
X.PP
XFinally, suppose you have a class every Tuesday and Thursday until 30 May
X1991:
X.PP
X.nf
X	REM Tue Thu UNTIL 30 May 1991 MSG Class tonight.
X.fi
X.PP
X.B THE MSG KEYWORD
X.PP
XThe
X.I MSG
Xkeyword causes the remaining part of the line to be passed through a
Xsubstitution filter and then printed to standard output whenever the
Xreminder is triggered.  See the section "Substitution" for more information
Xabout the substitution filter.
X.PP
X.B THE RUN KEYWORD
X.PP
XThe
X.I RUN
Xkeyword causes the remaining part of the line to be passed through the
Xsubstitution filter and then passed to the default shell for execution
Xwhenever the reminder is triggered.  If the
X.B \-r
Xcommand-line option is specified, all
X.I REM
Xcommands with the
X.I RUN
Xkeyword are ignored.
X.PP
X.B
XGETTING REMINDED ONLY ONCE PER DAY
X.PP
XIf you run
X.B remind
Xfrom your
X.I .login
Xscript, you may only want certain reminders to be run once per day, not
Xonce per login.  Typically, this is used to control
X.I RUN
Xcommands so that they only execute once per day.  To specify this,
Xplace the
X.I ONCE
Xkeyword in the reminder file.  When
X.B remind
Xencounters a
X.I ONCE
Xkeyword, it checks the last-access date of the reminder file.  If it is
Xequal to the current date,
X.B remind
Xassumes that the reminder file has already been run once, and ignores the
Xreminder.  If you start
X.B remind
Xwith the
X.B \-o
Xcommand-line option,
X.B remind
Xignores the
X.I ONCE
Xkeyword.
X.PP
X.B LOCALLY OMITTING WEEKDAYS
X.PP
XA
X.I REM
Xcommand containing the
X.I OMIT
Xkeyword followed by a list of weekdays causes the
X.I delta
Xand
X.I back
Xto ignore the specified weekdays when counting days.  This is called a
X.I local OMIT.
XFor example:
X.PP
X.nf
X	REM 1 +1 OMIT Sat Sun MSG Test4
X.fi
X.PP
XThis reminder prints "Test4" on the first day of every month, as well
Xas the previous day.  If, however, the first day of the month falls on
Xa Sunday or Monday, the reminder is also triggered on the previous
XFriday, since the
X.I delta
Xof +1 does not count Saturdays or Sundays when moving backwards.  Here's
Xanother example:
X.PP
X.nf
X	REM 1 -1 +1 OMIT Sat Sun MSG Test5
X.fi
X.PP
XThis reminder is triggered on the last working day of each month, as well
Xas the working day preceding it.  Let's look at it in detail:
X.PP
XThe
X.I day
Xof "1" specifies the first day of each month.  The
X.I back
Xof "-1" tells
X.B remind
Xto go backwards by one day, not counting Saturday and Sunday as it moves.
XThis takes us the the last working day of the preceding month, which is the
Xtrigger date.  The
X.I delta
Xof "+1" ensures that the reminder will be triggered on the day preceding
Xthis trigger date also.  Finally, if the trigger date happens to be
Xa Monday, the
X.I delta
Xcombined with the
X.I local OMIT
Xcauses the reminder to be triggered on the Friday (and Saturday and Sunday)
Xpreceding the trigger date.
X.PP
X.B THE AT KEYWORD
X.PP
XReminders with the
X.I AT
Xkeyword are called "timed reminders" or "AT reminders."  The
X.I AT
Xkeyword must be followed by a
X.I time,
Xwhich is a time in 24-hour format, from 0:00 to 23:59.  The
X.I time
Xmust be of the form \fIhh\fR:\fImm\fR.
XAlso, after the
X.I AT
Xkeyword, you can supply a
X.I tdelta
Xand a
X.I trepeat.
XThese have the same form as a
X.I delta
Xand a
X.I repeat.
XIn other words, a
X.I tdelta
Xis a number preceded by a "+" sign, and a
X.I trepeat
Xis a number preceded by an asterisk.
X.PP
XWhen
X.B remind
Xencounters a timed reminder, it examines the actual trigger date.  This
Xis the date specified by the date_spec, not counting any
X.I delta
Xwhich may be present.  If the trigger date is the same as the current
Xdate, the timed reminder will be placed on a queue in addition to
Xbeing triggered like a normal reminder.  (If you supply the
X.B \-a
Xcommand-line option, a timed reminder which would be queued will not
Xbe triggered like a normal reminder.)  After
X.B remind
Xhas finished processing the reminder file, it starts up a background
Xprocess to trigger all the queued timed reminders.  As each reminder's
Xtrigger time is reached, it is triggered.  Thus, you can have
X.B remind
Xissue reminders just before important meetings or time-sensitive tasks.
X.PP
XThe
X.I tdelta
Xand
X.I trepeat
Xare interpreted in minutes.  This is how they work:
X.PP
X.nf
X	REM AT 17:00 +60 *15 MSG Hello!
X.fi
X.PP
XThis timed reminder has a trigger time of 5:00pm.  It is also triggered
Xone hour before 5:00pm (because of the
X.I tdelta
Xof +60)
Xand every 15 minutes thereafter until 5:00pm (because of the
X.I trepeat
Xof +15.)  All told, the reminder would be triggered at 4:00pm, 4:15pm,
X4:30pm, 4:45pm and 5:00pm.
X.PP
X.nf
X	REM Fri AT 17:00 +45 *30 MSG Work ends at 5!!
X.fi
X.PP
XThis timed reminder would be placed on the queue every Friday.  It
Xwould be triggered at 4:15pm, 4:45pm and 5:00pm every Friday.  Note
Xthat a timed reminder is always triggered at the time specified
Xafter the
X.I AT
Xkeyword, even if the
X.I tdelta
Xis not a multiple of
X.I trepeat.
X.PP
XIf a reminder has a regular
X.I delta,
Xit will not be queued unless the current system date equals the actual
Xtrigger date.  Thus:
X.PP
X.nf
X	REM Fri +1 AT 17:00 MSG Go home at 5:00!
X.fi
X.PP
XOn Thursdays, this reminder would simply be treated as a regular reminder
Xand printed.  On Fridays, however, it would be queued for timed triggering.
XNote that the
X.B \-q
Xcommand-line option causes
X.B remind
Xto treat timed reminders as normal reminders, and not queue them.
X.SH THE OMIT COMMAND
X.I OMIT
Xexists as a separate command as well as a keyword within a
X.I REM
Xstatement.  When used as a separate command, it is called a
X.I global OMIT,
Xand has the following form:
X.RS
XOMIT
X.I day
X.I month
X[
X.I year
X]
X.RE
XThe arguments can be specified in any order.
X.PP
XThe form without the
X.I year
Xcomponent is used for holidays which fall on the same date each year.
XFor example:
X.PP
X.nf
X	OMIT 25 December	# Christmas
X	OMIT  1 January		# New Year's Day
X.fi
X.PP
X(Note that
X.I OMIT
Xcan have a following comment on the same line, unlike the
X.I REM
Xcommand.)
X.PP
XThe form with the
X.I year
Xcomponent is used for holidays which vary from year to year.  For example:
X.PP
X.nf
X	OMIT 12 October 1990	# Columbus Day 1990
X	OMIT 22 November 1990	# Thanksgiving Day 1990
X.fi
X.PP
XIn its debugging messages,
X.B remind
Xcalls the first form a
X.I partially-specified global OMIT
Xand the second form a
X.I fully-specified global OMIT.
X.PP
XThe dates specified by
X.I global OMITs
Xare omitted by the
X.I back
Xand
X.I delta
Xportions of a reminders, in addition to any
X.I local OMITs.
XFor example:
X.PP
X.nf
X	OMIT 25 December
X	REM 26 +1 OMIT Sat Sun MSG Test6
X.fi
X.PP
XThis would issue a reminder on the 26th of each month, as well as the
Xpreceding working day.  Also, on the 24th of December 1990, the reminder
Xwould be issued.  Even though the 25th of December 1990 is a Tuesday, the
X.I global OMIT
Xwould cause the
X.I delta
Xto skip it.
X.PP
XHowever, if you use the "--" and "++" forms of \fIback\fR and \fIdelta\fR,
Xthen OMITted days are not skipped - strict date subtraction is performed.
XThus, to have a reminder issued on the last Monday of every month, regardless
Xof any \fIglobal OMITs\fR in force, you could use:
X.PP
X.nf
X	REM 1 Mon --7 MSG Forced to be last Monday in month
X.fi
X.PP
XIf you want the reminder to be issued on the last Monday of every month,
Xunless it is a holiday, in which case the reminder should be issued the
Xfollowing Tuesday, use:
X.PP
X.nf
X	REM 1 Mon --7 AFTER MSG Example!
X.fi
X.PP
X.I Global OMITs
Xare in force for all reminders following them in the reminder file until
Xa CLEAR-OMIT-CONTEXT or POP-OMIT-CONTEXT command is encountered.  Thus,
Xyou could have a series of reminders for which
X.I global OMITs
Xare inappropriate (such as birthdays or reminders which rely on
X.I back
Xto get to a specific weekday) ahead of any
X.I global OMITs,
Xwith business reminders (for which omission of holidays is appropriate)
Xfollowing the
X.I global OMITs.
X.PP
XFor convenience, you can include a \fIdelta\fR and a \fIMSG\fR or \fIRUN\fR
Xtoken in a global \fIOMIT\fR.  This is useful to avoid duplication of holiday
Xentries.  For example, the line:
X.PP
X.nf
X	OMIT 25 Dec +2 MSG %"Christmas%" is %b.
X.fi
X.PP
Xis exactly equivalent to:
X.PP
X.nf
X	OMIT 25 Dec
X	REM 25 Dec +2 MSG %"Christmas%" is %b.
X.fi
X.SH CONTROLLING THE OMIT CONTEXT
XIn many cases, it is convenient to temporarily change the \fIglobal OMITs\fR
Xwhich are in force.  This allows you to easily isolate holidays to act only
Xon certain reminders, or to temporarily introduce a \fIglobal OMIT\fR for a
Xspecial-case reminder.  Three commands allow you to do this:
X.PP
X.I CLEAR-OMIT-CONTEXT
Xdeletes all the \fIglobal OMITs\fR and starts you off with a clean slate.
X.PP
X.I PUSH-OMIT-CONTEXT
Xsaves all the \fIglobal OMITs\fR on an internal stack and leaves the current
X\fIglobal OMITs\fR in effect.
X.PP
X.I POP-OMIT-CONTEXT
Xrestores the \fIglobal OMITs\fR which were in effect at the time of the last
Xmatching \fIPUSH-OMIT-CONTEXT\fR command.
X.PP
XSuppose you have a block of reminders which require a clear OMIT context
Xto work properly, and which also introduce several \fIglobal OMITs\fR which
Xyou don't want to affect any other reminders.  The following fragment shows
Xhow to isolate these reminders:
X.PP
X.nf
X	# Sample fragment
X	PUSH-OMIT-CONTEXT   # Save for later
X	CLEAR-OMIT-CONTEXT  # Start out with clean slate
X
X	# Block of reminders goes here
X
X	POP-OMIT-CONTEXT    # Restore the saved OMITs
X	# Remaining reminders go here
X.fi
X.SH THE INCLUDE COMMAND
XThe
X.I INCLUDE
Xcommand has the following form:
X.PP
X.RS
XINCLUDE
X.I filename
X.RE
X.PP
XThis causes
X.B remind
Xto suspend the current file and read the contents of the specified
X.I filename.
XOnce that file has been read,
X.B remind
Xcontinues reading the original file from where it left off.
X.I INCLUDE
Xcommands can be nested to a depth of 10.
X.PP
X.I INCLUDE
Xallows you or someone else to maintain a file of holidays or system-wide
Xreminders that everyone should get.  For example, your reminder file could
Xlook something like this:
X.PP
X.nf
X	#!/usr/local/bin/remind
X	#
X	# Place personal stuff here - birthdays, etc.
X
X	# Now get system-wide global OMITS (holidays)
X	INCLUDE /usr/share/holidays
X
X	# and system-wide reminders
X	INCLUDE /usr/share/reminders
X
X	# Place local business stuff here.
X.fi
X.PP
XThe
X.I ONCE
Xkeyword operates on the last-access date of the top-level file.
XThe access dates of
X.I INCLUDEd
Xfiles are not used to control the operation of the
X.I ONCE
Xkeyword.
X.SH SUBSTITUTION
XBefore being processed, the body of a
X.I REM
Xcommand is passed through a substitution filter.  The filter scans for
Xsequences "%x" (where "x" is any letter and certain other characters)
Xand performs substitutions as
Xshown below.  (All dates refer to the trigger date of the reminder.)
X.TP
X.B %a
Xis replaced with "on \fIweekday, day month, year\fR"
X.RS
XFor example, consider the reminder:
X.PP
XREM 18 Oct 1990 +4 MSG Meeting with Bob %a.
X.PP
XOn 16 October 1990, it would print "Meeting with Bob on Thursday, 18 October,
X1990."
X.PP
XOn 17 October 1990, it would print "Meeting with Bob tomorrow."
X.PP
XOn 18 October 1990, it would print "Meeting with Bob today."
X.RE
X.TP
X.B %b
Xis replaced with "in \fIdiff\fR day's time" where
X.I diff
Xis the
X.B actual
Xnumber of days between the current date and the trigger date.
X(\fIOMITs\fR have no effect.)
X.RS
XFor example, consider:
X.PP
XREM 18 Oct 1990 +4 MSG Meeting with Bob %b.
X.PP
XOn 16 October 1990, it would print "Meeting with Bob in 2 days' time."
X.PP
XOn 17 October 1990, it would print "Meeting with Bob tomorrow."
X.PP
XOn 18 October 1990, it would print "Meeting with Bob today."
X.RE
X.TP
X.B %c
Xis replaced with "on \fIweekday\fR"
X.RS
XExample: REM 18 Oct 1990 +4 MSG Meeting with Bob %c.
X.PP
XOn 16 October 1990, it would print "Meeting with Bob on Thursday."
X.PP
XOn 17 October 1990, it would print "Meeting with Bob tomorrow."
X.PP
XOn 18 October 1990, it would print "Meeting with Bob today."
X.RE
X.TP
X.B %d
Xis replaced with "\fIday\fR", the day of the month.
X.TP
X.B %e
Xis replaced with "on \fIdd/mm/yyyy\fR"
X.TP
X.B %f
Xis replaced with "on \fImm/dd/yyyy\fR"
X.TP
X.B %g
Xis replaced with "on \fIweekday, day month\fR"
X.TP
X.B %h
Xis replaced with "on \fIdd/mm\fR"
X.TP
X.B %i
Xis replaced with "on \fImm/dd\fR"
X.TP
X.B %j
Xis replaced with "on \fIweekday, month day-th, year\fR"  This form appends the
Xcharacters "st", "nd", "rd" or "th" to the day of the month, as appropriate.
X.TP
X.B %k
Xis replaced with "on \fIweekday, month day-th\fR"
X.TP
X.B %l
Xis replaced with "on \fIyyyy/mm/dd\fR"
X.TP
X.B %m
Xis replaced with "\fImonth\fR", the name of the month.
X.TP
X.B %n
Xis replaced with the number (1 to 12) of the month.
X.TP
X.B %o
Xis replaced with " (today)" if and only if the current system date is the same
Xas the date being used by
X.B remind
Xas the current date.  Recall that you can specify a date for
X.B remind
Xto use on the command line.  This substitution is not generally useful in a
X.I REM
Xcommand, but is useful in a
X.I BANNER
Xstatement.  (See the section "The BANNER Command.)
X.TP
X.B %p
Xis replaced with "s" if the
X.I diff
Xbetween the current date and the trigger date is not 1.  You can use this
Xto construct reminders like:
X.RS
XREM 1 Jan +4 MSG %x day%p to go before New Year!
X.RE
X.TP
X.B %q
Xis replaced with "'s" if the
X.I diff
Xbetween the trigger date and the current date is 1.  Otherwise, it is replaced
Xwith "s'"  This can be used as follows:
X.RS
XREM 1 Jan +4 MSG New Year in %x day%q time!
X.RE
X.TP
X.B %r
Xis replaced with the day of the month (01 to 31) padded with a leading zero
Xif needed to pad to two digits.
X.TP
X.B %s
Xis replaced with "st", "nd", "rd" or "th" depending on the day of the month.
X.TP
X.B %t
Xis replaced with the number of the month (01 to 12) padded to two digits
Xwith a leading zero.
X.TP
X.B %u
Xis replaced with "on \fIweekday, day-th month, year\fR"  This is similar
Xto
X.B %a
Xexcept that "st", "nd", "rd" or "th" is added to the
X.I day
Xas appropriate.
X.TP
X.B %v
Xis replaced with "on \fIweekday, day-th month\fR"
X.TP
X.B %w
Xis replaced with "\fIweekday\fR", the name of the day of the week.
X.TP
X.B %x
Xis replaced with the
X.I diff
Xbetween the current date and the trigger date.  The
X.I diff
Xis defined as the actual number of days between these two dates;
X.I OMITs
Xare not counted.  (Strict date subtraction is performed.)
X.TP
X.B %y
Xis replaced with "\fIyear\fR", the year of the trigger date.
X.TP
X.B %z
Xis replaced with "\fIyy\fR", the last two digits of the year.
X.TP
X.B %_
X(percent-underscore) is replaced with a newline.  You can use this to
Xachieve multi-line reminders.
X.TP
X.B %1
Xis replaced with "now", "\fIm\fR minutes from now", "\fIm\fR minutes ago",
X"\fIh\fR hours from now", "\fIh\fR hours ago", "\fIh\fR hours and \fIm\fR
Xminutes from now" or "\fIh\fR hours and \fIm\fR minutes ago", as appropriate
Xfor a timed reminder.  Note that unless you specify the \fB\-a\fR option,
Xtimed reminders will be triggered like normal reminders, and thus a timed
Xreminder which occurred earlier in the day may be triggered.  This
Xcauses the need for the "...ago" forms.
X.TP
X.B %2
Xis replaced with "\fIhh\fR:\fImm\fRam" or "..pm" depending on the
X.I AT
Xtime of the reminder.
X.TP
X.B %3
Xis replaced with "\fIhh\fR:\fImm\fR" in 24-hour format.
X.TP
X.B %4
Xis replaced with "\fImm\fR" where \fImm\fR is the number of minutes between
X"now" and the time specified by \fIAT\fR.  If the \fIAT\fR time is
Xearlier than the current time, then the result is negative.
X.TP
X.B %5
Xis replaced with "\fIma\fR" where \fIma\fR is the absolute value of the number
Xproduced by \fB%4\fR.
X.TP
X.B %6
Xis replaced with "ago" or "from now", depending on the relationship between
Xthe \fIAT\fR time and the current time.
X.TP
X.B %7
Xis replaced with the number of hours between the \fIAT\fR time and the
Xcurrent time.  It is always non-negative.
X.TP
X.B %8
Xis replaced with the number of minutes between the \fIAT\fR time and
Xthe current time, after the hours (\fB%7\fR) have been subtracted out.
XThis is a number ranging from 0 to 59.
X.TP
X.B %9
Xis replaced with "s" if the value produced by \fB%8\fR is not 1.
X.TP
X.B %0
Xis replaced with "s" if the value produced by \fB%7\fR is not 1.
X.TP
X.B %!
Xis replaced with "is" if the current time is before the \fIAT\fR time,
Xor "was" if it is after.
X.TP
X.B 
X%"
X(percent-doublequote) is removed.  This sequence is not 
Xused by the substitution filter,
Xbut is used to tell \fBremind\fR which text to include in a calendar
Xentry when the \fB\-c\fR option is chosen.  See the section "Producing
Xa Calendar"
X.PP
XNotes:
X.TP
Xo
X.B Remind
Xnormally prints a blank line after each reminder; if the last character
Xof the body is '%', the blank line will not be printed.
X.TP
Xo
XSubstitutions a, b, c, e, f, g, h, i, j, k, l, u and v all are replaced
Xwith "today" if the current date equals the trigger date, or "tomorrow"
Xif the trigger date is one day after the current date.  Thus, they are
X.B not
Xthe same as substitutions built up from the simpler %w, %y, etc.
Xsequences.
X.TP
Xo
XAny of the substitutions dealing with time (0 through 9 and '!')
Xproduce undefined results if used in a reminder which does not have
Xan \fIAT\fR keyword.  Also, if a reminder has a \fIdelta\fR and may
Xbe triggered on several days, the time substitutions ignore the date.  Thus,
Xthe \fB%1\fR substitution may report that a meeting is in 15 minutes, for
Xexample, even though it may only be in 2 days time, because a \fIdelta\fR has
Xtriggered the reminder.  It is recommended that you use the time substitutions
Xonly in timed reminders with no \fIdelta\fR which are designed to be
Xqueued for timed activation.
X.TP 
Xo
XCapital letters can be used in the substitution sequence, in which case
Xthe first character of the substituted string is capitalized (if it is
Xnormally a lower-case letter.)
X.TP 
Xo
XAll other characters following a "%" sign are simply copied.  In particular,
Xto get a "%" sign out, use "%%" in the body.  To start the body of a reminder
Xwith a space, use "% ", since
X.B remind
Xnormally scans for the first non-space character after a
X.I MSG
Xor
X.I RUN
Xtoken.
X.SH PRODUCING A CALENDAR
XIf you provide the \fB\-c\fR option, the \fBremind\fR will scan your
Xreminder file and produce a calendar with your reminders entered on the
Xappropriate dates.
X.PP
XWhen you produce a calendar, \fBremind\fR will pass the text
Xthrough the substitution mechanism.  However, you may indicate to \fBremind\fR
Xwhich portions of the text should be placed in the calendar.  This is done
Xwith the \fB%"\fR sequence.  Here's an example:
X.PP
X.nf
X	REM 6 Jan +6 MSG %"David's birthday%" is %a.
X.fi
X.PP
XWhen you run \fBremind\fR normally, it executes the reminder as if the
X.B 
X%"
Xsequences were not there.  When you produce a calendar with the \fB\-c\fR
Xoption, however, only the text "David's birthday" is entered into the
Xcalendar.  For convenience, timed reminders are sorted by time before
Xbeing entered into the calendar, and the time of each reminder is printed
Xbefore the reminder text.
X.PP
XNote that by default, only \fIMSG\fR reminders are included in the calendar; 
X\fIRUN\fR reminders are ignored.  However, if you explicitly place the %"
Xescape sequences in a \fIRUN\fR reminder, then it will be included in the
Xcalendar.
XIf you indicate empty text for the calendar entry
Xwith the sequence \fB%"%"\fR, then the reminder is ignored when producing
Xthe calendar, whether is is a \fIMSG\fR or \fIRUN\fR type.
X.PP
XIf your reminder file has errors in it, when you produce a calendar, you
Xwill get many error messages apparently coming from the file "*cache*"
XThis is because \fBremind\fR caches the reminder file for efficient
Xproduction of the calendar.  Thus, you should check that your reminder
Xfile is error-free (by running \fBremind\fR with the \fB\-d\fR option)
Xbefore attempting to produce a calendar.
X.PP
XThe "Simple Calendar" format is useful if you want to format the calendar
Xwith another program.  You can save this output to a file or pipe it to
Xyour formatting program.  The output format is:
X.PP
X.nf
X	yyyy/mm/dd: hr:min \fIbody\fR
X.fi
X.PP
XThe \fIhr:min\fR portion is present only if the reminder has an \fIAT\fR
Xclause.  You can produce this format by using the \fB\-s\fR option in
Xconjunction with the \fB-c\fR option.  The \fB\-n\fR option produces
Xa listing in this format also; however, it is not sorted by date, and
Xindicates the next occurrence of each reminder in the reminder file.
X.SH THE BANNER COMMAND
XWhen
X.B remind
Xissues reminders, it normally prints a message saying:
X.PP
X"Reminders for \fIweekday, day-th month, year\fR (today):"
X.PP
X(If you supply a date on the command line different from the system date,
Xthe "(today)" portion is omitted.)
X.PP
XYou can change this default to anything you want with the
X.I BANNER
Xcommand.  It should appear in the reminder file before any
X.I REM
Xcommands.  (This is not enforced, but a
X.I BANNER
Xcommand encountered after a reminder has been triggered will be ignored.)
X.PP
XThe format of
X.I BANNER
Xis:
X.PP
XBANNER
X.I string
X.PP
XThe
X.I string
Xis passed through the substitution mechanism described before, using the
Xcurrent date and time for substitution.  The standard banner is described by:
X.PP
X.nf
X	BANNER Reminders for %w, %d%s %m, %y%o:
X.fi
X.PP
XYou can have a blank banner by using:
X.PP
X.nf
X	BANNER %
X.fi
X.PP
X.SH PURGING REMINDER FILES
XYou should periodically purge your reminder file of "expired"
X.I REM
Xcommands and global
X.I OMITs.
XThese are reminders which took place in the past and can never occur
Xagain, or
X.I OMITs
Xwhich are for past years.  Of course, only
X.I REMs
Xand
X.I OMITs
Xwith the
X.I year
Xspecified can ever expire.
X.PP
XTo purge your reminder file, run
X.B remind
Xin
X.I purge
Xmode.  This mode simply echoes non-expired lines to standard output, and
Xexpired lines to standard error.  Thus, to purge a file, you could type:
X.PP
X.nf
X	REMIND -p notes.old > notes.new
X.fi
X.PP
XThe file "notes.new" would contain all non-expired lines from "notes.old."
XAll expired lines are echoed to the screen.
X.I INCLUDEd
Xfiles are read and processed, but not purged.  You must purge each
X.I INCLUDEd
Xfile separately.
X.PP
XYou can supply a date on the command line when purging, but beware:  If you
Xsupply a date in the future, you may inadvertently purge lines which have not
Xreally expired yet.
X.SH DEBUGGING A REMINDER FILE
XThe debug command-line option \-d is used to debug reminder files.  It tells
Xyou useful information about the reminder file.  It displays the trigger date
Xof each reminder, and places an asterisk by those which would be issued
Xon the current date.  It also informs you of
X.I OMITs
Xwhich have expired, and gives warnings about situations which could lead
Xto long execution times or unexpected results.
X.PP
XAll debugging messages are sent to standard error.
X.SH EXAMPLES OF REMINDERS
XThis section is a sampling of what you can do with \fBremind\fR.
X.PP
X.nf
X	REM 5 Feb 1991 AT 14:00 +45 *30 \\
X	RUN mail -s "Meeting at %2" $LOGNAME </dev/null &
X.fi
X.PP
XOn 5 February, 1991, this reminder will mail 
Xyou reminders of a 2:00pm meeting at 1:15, 
X1:45 and 2:00.  The subject of the mail message will be "Meeting at 2:00pm"
Xand the body of the message will be blank.
X.PP
X.nf
X	REM AT 17:00 RUN echo "5:00pm - GO HOME!" | xless -g +0+0 &
X.fi
X.PP
XThis reminder will pop up an xless window at 5:00pm every day.  The xless
Xwindow will contain the line "5:00pm - GO HOME!"
X.PP
X.nf
X	REM AT 23:59 RUN (sleep 120; remind -a .reminders) &
X.fi
X.PP
XThis reminder will run at one minute to midnight.  It will cause a new
Xremind process to start at one minute past midnight.  This allows you to
Xhave a continuous reminder service so you can work through the night and
Xstill get timed reminders for early in the morning.
X.PP
X.nf
X	remind -c12 /dev/null 1 Jan 1991
X.fi
X.PP
XThis invocation of \fBremind\fR will cause it to print a calendar for
X1991, with all entries left blank.
X.SH AUTHOR
XDavid F. Skoll
X.SH BUGS
X.B Remind
Xwas originally written for MS-DOS and ported to UNIX.  It does some things
Xin an ungainly way.
X.PP
XDate calculation algorithms are fairly "brute force."
X.PP
XThe MS-DOS version of \fBremind\fR does not support queuing or timed
Xactivation of reminders.  It does, however, support the other semantics
Xof the \fIAT\fR keyword.
X.PP
X.B Remind
Xhas some built-in limits on total line length (511 characters),
Xthe length of an expanded \fIREM\fR body (511 characters), the number
Xof global \fIOMIT\fRs, etc.
SHAR_EOF
$TOUCH -am 0218130591 remind.1 &&
chmod 0600 remind.1 ||
echo "restore of remind.1 failed"
set `wc -c remind.1`;Wc_c=$1
if test "$Wc_c" != "40229"; then
	echo original size 40229, current size $Wc_c
fi
fi
echo "End of part 3, continue with part 4"
exit 0



More information about the Alt.sources mailing list