program remind (input, output, remindfile); {**************************************************************} { } { Copyright (c) 1982, 1985 Bob Schor } { Rockefeller University } { 1230 York Ave } { New York, NY 10021 } { } { All rights reserved. May not be copied without this notice. } { } {**************************************************************} {**************************************************************} { } { Copyright (c) 1988 Bob Schor } { Eye and Ear Hospital } { 230 Lothrop Street } { Pittsburgh, PA 15213 } { } { All rights reserved. May not be copied without this notice. } { } {**************************************************************} { allow reminding of significant events } { information stored in reminder files (.REM) first line, integer entry for number of days advance notice remaining lines are reminders in following format Date information program will look at two files, REMIND.REM and ANNUAL.REM the second file contains annual reminders, e.g. birthdays } { As of version 8.12, the first-line number-of-days parameters will be taken from the command line, with defaults for missing arguments } { date and time obtained from Oregon Software timestamp procedure } { Version 2.10 -- previous incarnation } { Version 5.1 -- end-of-year bug fixed } { Version 8.12 -- days-in-advance now come from command line } CONST version = 'REMIND Version 8.12'; namelength = 10; maxcmdlen = 20; defaultreminddays = 10; { default days-in-advance for REMIND.REM } defaultannualdays = 15; { default days-in-advance for ANNUAL.REM } TYPE juliandatetype = real; nametype = PACKED ARRAY [1 .. namelength] OF char; cmdindextype = 1 .. maxcmdlen; cmdtype = PACKED ARRAY [cmdindextype] OF char; monthindextype = 1 .. 3; monthstringtype = PACKED ARRAY [monthindextype] OF char; datetype = RECORD day, month, year : integer END; VAR remindfile : text; weekday : integer; juliantoday : juliandatetype; today, entrydate : datetype; entrymonthstring : monthstringtype; reminddays, annualdays : integer; { external julian date routines } FUNCTION juliandate (day, month, year : integer) : juliandatetype; EXTERNAL; PROCEDURE calendardate(julian : juliandatetype; VAR weekday, day, month, year : integer); EXTERNAL; { external command line routines } PROCEDURE getcmd (VAR command : cmdtype; VAR length : cmdindextype); EXTERNAL; PROCEDURE clrcmd; EXTERNAL; { Pascal-2 date/time routine } PROCEDURE timestamp (VAR day, month, year, hour, minute, second : integer); EXTERNAL; PROCEDURE initialize; VAR hour, minute, second : integer; command : cmdtype; cmdlength : cmdindextype; PROCEDURE parsecommand (cmd : cmdtype; length : cmdindextype; VAR remind, annual : integer); VAR index : cmdindextype; value : integer; FUNCTION integerinstring (VAR value : integer) : boolean; FUNCTION numeric (letter : char) : boolean; BEGIN { numeric } numeric := letter IN ['0' .. '9'] END; PROCEDURE skipnonnumeric; BEGIN { skipnonnumeric } IF (index < length) AND (ord(cmd[index]) <> 0) THEN IF NOT numeric (cmd[index]) THEN BEGIN index := succ(index); skipnonnumeric END END; FUNCTION integervalue (valuesofar : integer) : integer; BEGIN { integervalue } IF index > length THEN integervalue := valuesofar ELSE IF NOT numeric (cmd[index]) THEN integervalue := valuesofar ELSE BEGIN valuesofar := 10*valuesofar + ord(cmd[index]) - ord('0'); index := succ(index); integervalue := integervalue (valuesofar) END END; BEGIN { integerinstring } skipnonnumeric; IF numeric (cmd[index]) THEN BEGIN value := integervalue (0); integerinstring := true END ELSE BEGIN value := 0; integerinstring := false END END; BEGIN { parsecommand } index := 1; IF integerinstring (value) THEN remind := value ELSE remind := defaultreminddays; IF integerinstring (value) THEN annual := value ELSE annual := defaultannualdays END; BEGIN { initialize } { writeln; writeln (version); writeln; } getcmd (command, cmdlength); clrcmd; parsecommand (command, cmdlength, reminddays, annualdays); WITH today DO BEGIN timestamp (day, month, year, hour, minute, second); juliantoday := juliandate (day, month, year); calendardate (juliantoday, weekday, day, month, year) END END; PROCEDURE writecurrentdate; PROCEDURE writeweekday (weekday : integer); BEGIN { writeweekday } CASE weekday OF 0 : write ('Sunday'); 1 : write ('Monday'); 2 : write ('Tuesday'); 3 : write ('Wednesday'); 4 : write ('Thursday'); 5 : write ('Friday'); 6 : write ('Saturday') END END; PROCEDURE writemonth (month : integer); BEGIN { writemonth } CASE month OF 1 : write ('January'); 2 : write ('February'); 3 : write ('March'); 4 : write ('April'); 5 : write ('May'); 6 : write ('June'); 7 : write ('July'); 8 : write ('August'); 9 : write ('September'); 10 : write ('October'); 11 : write ('November'); 12 : write ('December') END END; BEGIN { writecurrentdate } writeln; writeweekday (weekday); write (', '); WITH today DO BEGIN writemonth (month); writeln (' ', day:1, ', ', year:4) END; writeln; END; FUNCTION readable (filename : nametype) : boolean; VAR filelength : integer; BEGIN { readable } reset (remindfile, filename, '.REM', filelength); readable := filelength > 0 END; PROCEDURE readentrydate; VAR dash : char; FUNCTION lowercase (letter : char) : char; BEGIN { lowercase } IF letter IN ['A' .. 'Z'] THEN lowercase := chr(ord(letter) - ord('A') + ord('a')) ELSE lowercase := letter END; FUNCTION uppercase (letter : char) : char; BEGIN { uppercase } IF ('a' <= letter) AND (letter <= 'z') THEN uppercase := chr(ord(letter) - ord('a') + ord('A')) ELSE uppercase := letter END; FUNCTION monthis (monthstring : monthstringtype) : integer; BEGIN { monthis } IF monthstring = 'Jan' THEN monthis := 1 ELSE IF monthstring = 'Feb' THEN monthis := 2 ELSE IF monthstring = 'Mar' THEN monthis := 3 ELSE IF monthstring = 'Apr' THEN monthis := 4 ELSE IF monthstring = 'May' THEN monthis := 5 ELSE IF monthstring = 'Jun' THEN monthis := 6 ELSE IF monthstring = 'Jul' THEN monthis := 7 ELSE IF monthstring = 'Aug' THEN monthis := 8 ELSE IF monthstring = 'Sep' THEN monthis := 9 ELSE IF monthstring = 'Oct' THEN monthis := 10 ELSE IF monthstring = 'Nov' THEN monthis := 11 ELSE IF monthstring = 'Dec' THEN monthis := 12 ELSE monthis := -1 END; BEGIN { readentrydate } WITH entrydate DO BEGIN read (remindfile, day, dash, entrymonthstring, dash, year); entrymonthstring[1] := uppercase (entrymonthstring[1]); entrymonthstring[2] := lowercase (entrymonthstring[2]); entrymonthstring[3] := lowercase (entrymonthstring[3]); month := monthis (entrymonthstring) END END; FUNCTION baddate (date : datetype) : boolean; FUNCTION outofbounds (test, low, high : integer) : boolean; BEGIN { outofbounds } outofbounds := (test < low) OR (test > high) END; BEGIN { baddate } WITH date DO baddate := (outofbounds (day, 1, 31) OR outofbounds (month, 1, 12) OR outofbounds (year, 0, 99)) END; FUNCTION withinlimits (advancenotice : integer; chosenyear : integer) : boolean; VAR julianentry : juliandatetype; { uses global "juliantoday" and "entrydate" } BEGIN { withinlimits } WITH entrydate DO julianentry := juliandate (day, month, chosenyear); withinlimits := (((julianentry - juliantoday) >= 0) AND ((julianentry - juliantoday) <= advancenotice)) END; PROCEDURE writeentireentry; VAR message : char; BEGIN { writeentireentry } WITH entrydate DO BEGIN write (day:2, '-', entrymonthstring:3, '-'); IF year >= 10 THEN write (year:2, ' ':3) ELSE write ('0', year:1, ' ':3) END; WHILE NOT eoln (remindfile) DO BEGIN read (remindfile, message); write (message) END; readln (remindfile); writeln END; PROCEDURE writeerror; BEGIN { writeerror } writeln ('*** Date error in entry! ***'); writeentireentry END; PROCEDURE ignoreentry; BEGIN { ignoreentry } readln (remindfile) END; PROCEDURE catchinitialentry (VAR dayparameter : integer; defaultparameter : integer; filename : nametype); VAR trialdays : integer; ofcourse : boolean; BEGIN { catchinitialentry } read (remindfile, trialdays); IF remindfile^ = '-' THEN ofcourse := readable (filename) ELSE BEGIN readln (remindfile); IF dayparameter = defaultparameter THEN dayparameter := trialdays END END; PROCEDURE remindonce; BEGIN { remindonce } IF readable ('REMIND.REM') THEN BEGIN catchinitialentry (reminddays, defaultreminddays, 'REMIND.REM'); WHILE NOT eof (remindfile) DO BEGIN readentrydate; IF baddate (entrydate) THEN writeerror ELSE IF withinlimits (reminddays, 1900 + entrydate.year) THEN writeentireentry ELSE ignoreentry END; close (remindfile); writeln END END; PROCEDURE remindyearly; VAR currentyear : integer; BEGIN { remindyearly } IF readable ('ANNUAL.REM') THEN BEGIN catchinitialentry (annualdays, defaultannualdays, 'ANNUAL.REM'); WHILE NOT eof (remindfile) DO BEGIN readentrydate; IF baddate (entrydate) THEN writeerror ELSE IF (withinlimits (annualdays, today.year) OR withinlimits (annualdays, succ(today.year))) THEN writeentireentry ELSE ignoreentry END; close (remindfile); writeln END END; BEGIN { remind } initialize; writecurrentdate; IF juliantoday > 0 THEN BEGIN remindonce; remindyearly END END.