program calendar; {************************************************************************* Program: CALENDAR Author: Richard Conn Date: 4 Feb 82 Description: CALENDAR is used to display a Calendar to the user. The Calendar may be that of a particular Month in a particular Year or that of all Months in a Particular Year. The calendar displayed is the Gregorian Calendar. The Calendar display may be sent to the user's Console (by default) or optionally to the user's LST: device or a disk file. Usage: calendar [month] year [/o] where month may be one of january, february, ..., december (optional and only first three letters are req'd) year may be any year after byear o may be one of the following -- p - send output to Printer d - send output to Disk (o is optional and defaults to Console if omitted) Examples: CALENDAR JANUARY 1982 -- Calendar of Month of January of 1982 CALENDAR JAN 1982 -- Same as Above CALENDAR 1982 -- Calendar of all months of 1982 CALENDAR 1982 /P -- Same as Above but Output to Printer CALENDAR 1982 /D -- Same as Above but Output to Disk CALENDAR 1982 /P/D -- Same as Above but Output to Disk (Disk has priority) *****************************************************************************} {*************************************************************************** 'version' is the Version Number of CALENDAR. 'byear1' is the Base Year of CALENDAR. This year MUST be a Leap Year. Since CALENDAR uses integer arithmetic to do its calculations, the range of years that may be addressed by CALENDAR is from byear to byear + 30,000 (approx). 'bday1' is the Base Day of CALENDAR. This is the number (1 to 7) of the First Sunday in January of the Base Year. ****************************************************************************} const version = 13; byear1 = 1804; { Base Year for this program } bday1 = 1; { Base Day for the Base Year } {*********************************************** Global Types and Variables ************************************************} type strptr = ^string; var ofile : text; filename : string[14]; month1, year1, dow : integer; mposfnd, mpos, ypos : integer; mdays : array [1..12] of integer; month : array [1..12] of string[10]; year : string; command : strptr; cmdline, yline : string; lyear : boolean; icount : integer; match, conout, diskout : boolean; byear, bday, bdow : integer; {**************************************************** External PASCAL/MT+ System Functions *****************************************************} external function @cmd : strptr; {************************************************************************** Function: day_count Computes the number of days since the beginning of the year. (Jan 1 = Day 0) Input Parameters: day: integer in range 1-31 month: integer in range 1-12 year: integer mdays[i, 1<=i<=12 ]: number of days in month i, i=1=January (Global Parameter) Output Parameters: day_count: Number of days since 1st day of year (0=1st day) ***************************************************************************} function day_count (day, month, year : integer) : integer; var ndays, i : integer; begin ndays := day - 1; { Adjust for first day being day 0} if month <> 1 then for i:=1 to month-1 do ndays := ndays + mdays[i]; { Compute Number of Days since Year Start } day_count := ndays; lyear := false; { Assume NOT Leap Year } if (year mod 4) <> 0 then exit; { If not Leap Year, Done } if ((year mod 100) = 0) and ((year mod 400) <> 0) then exit; { 2000, 2400, etc are Leap, other centurys not } lyear := true; { Leap Year } if month < 3 then exit; { If in Feb or Jan, Done } day_count := ndays + 1; { Adjust for Leap Year } end; {********************************************************************* Function: day_of_week Computes day of the week that a given date falls on. Input Parameters: day : integer in range 1-31 month : integer in range 1-12 year : integer Output Parameters: day_of_week : integer in range 1-7 (bday = Sunday) **********************************************************************} function day_of_week (day, month, year : integer) : integer; var ndays, tyear : integer; begin ndays := day_count (day, month, year); { Compute Number of Days } ndays := ndays + 365*(year - byear) + ((year - byear + 3) div 4); tyear := (year div 100) * 100; { Century below given year } if ((tyear mod 400) <> 0) and (byear < tyear) and (tyear < year) then ndays := ndays - 1; { Adjust for NO Leap Year century } day_of_week := (ndays mod 7) + 1; end; {************************************************************************ Function: CLINE Print syntax of Command Line for Calendar Program. Input/Output Parameters: None *************************************************************************} procedure cline; { Print Syntax of Command Line } begin writeln(' Calendar Command Line should be:'); writeln(' calendar month year /o'); writeln(' ', byear1, ' <= YEAR <= 30,000 (approx)'); writeln(' Only first three characters of MONTH are meaningful'); writeln(' /O may be one of --'); writeln(' /P to send output to Printer'); writeln(' /D to send output to Disk File'); writeln; writeln(' Examples:'); writeln(' CALENDAR JAN 1982'); writeln(' CALENDAR DECEMBER 2000'); writeln(' CALENDAR 1982 /D'); writeln(' CALENDAR 1984 /P'); end; {************************************************************************* Function: NUMBER Converts the input string of digits to an integer. Input Parameter: value: string of digits Output Parameter: number: value of digit string; evaluation stops at first non-digit character **************************************************************************} function number (valstr : string) : integer; var idx, numb : integer; cont : boolean; digit : char; idigit : integer; val1 : string; begin val1 := valstr; { Temp Variable } numb := 0; { Initialize result } { Test for Empty Input String; if empty, return zero value } if length(val1) = 0 then begin number := numb; { Pass out value } exit; end; { Extract each digit from string and convert into result } cont := true; idx := 1; while cont do begin digit := val1[idx]; { Get next digit } if (digit < '0') or (digit > '9') then idigit := 10 else idigit := ord(digit) - ord('0'); { Convert to bin } if idigit = 10 then cont := false; if cont then numb := numb * 10 + idigit; { Update Value } idx := idx + 1; { Increment Char Pointer } if length (val1) < idx then cont := false; end; number := numb; { Final Value } end; {************************************************************************ Function: CAL Prints one line of the calendar. Input Parameters: dow: Day of the Week to Start On day: Number of Day in Month month: Month of Year lyear: Leap Year (T/F) Output Parameter: cal: Number of next Day in Month (0=done) ************************************************************************} function cal (dow, day, month : integer) : integer; var i : integer; monlen, nday, ndays : integer; begin { If day is zero, print blank entry } if day=0 then begin for i:=1 to 7 do write(ofile, ' '); write(ofile, ' '); cal := 0; exit; end; { Determine number of days in month } monlen := mdays[month]; { If month is Feb and it is a leap year, then add 1 } if (month=2) and lyear then monlen := monlen + 1; { If number < Sunday, set dow to 7+ } if dow < bday then dow := dow + 7; { If not Sunday, space over to proper starting column of month cal } if dow <> bday then for i:=1 to dow-bday do write(ofile, ' '); { Compute number of days in current line } ndays := 7 - (dow-bday); { If we exceed number of days in month, adjust to limit } if day+ndays > monlen then ndays := monlen-day+1; { We are in proper position, to print day entries in Calendar line } if ndays<>0 then for i:=1 to ndays do begin nday := day + i - 1; write(ofile, nday:2, ' '); end; { Fill out rest of line if end of calendar } if (day<>1) and (ndays<>7) then for i:=ndays+1 to 7 do write(ofile, ' '); { Write ending spaces } write(ofile, ' '); { Set return value to be day of month to start on or zero if done } if monlen < (ndays+day) then cal := 0 else cal := day + ndays; end; { CAL } {********************************************************************** Function: DOMONTH Prints Calendar for Month 'month1' of Year 'year1'. Input Parameters: month1: month number (1 to 12) year1: year number (byear to 30,000) Output Parameters: - None - ***********************************************************************} procedure domonth; var day1 : integer; begin { Determine what day of the week the first day of month falls on } day1 := day_of_week (1,month1,year1); { Day of 1st Day of Month } { Write header for Calendar Month } writeln(ofile); writeln(ofile, 'Calendar for ',month[month1],' ', year1); writeln(ofile, 'Su Mo Tu We Th Fr Sa'); { Print first line of Calendar } day1 := cal (day1, 1, month1); writeln(ofile); { Print rest of Calendar } while day1 <> 0 do begin day1 := cal (bday, day1, month1); writeln(ofile); end; end; { DOMONTH } {************************************************************** Function: DOYEAR Prints Calendar for Year 'year1'. Input Parameters: year1: year number Output Parameters: - None - **************************************************************} procedure doyear; var dayx : array [1..3] of integer; idx, mbase, group3, group4 : integer; begin { Write Header for Calendar } writeln(ofile, ' Calendar of Year ', year1); writeln(ofile); { Loop over Calendar as 4 rows of three months each } for group3 := 1 to 4 do begin { Compute Base Month Number } mbase := (group3-1) * 3 + 1; { Page if output to CON: and beginning 3rd group of months } if (group3 = 3) and conout then begin write('Strike RETURN Key to Continue - '); readln; writeln; end; { Print Heading of Each Month } writeln(ofile); for group4 := mbase to mbase+2 do write(ofile, 'Calendar for ',month[group4], ' '); if ((group3 = 1) or (group3 = 3)) and conout then writeln(ofile, year1) else writeln(ofile); for group4 := mbase to mbase+2 do begin write(ofile, 'Su Mo Tu We Th Fr Sa '); idx := group4 mod 3; if idx=0 then idx := 3; dayx[idx] := day_of_week(1,group4,year1); end; writeln(ofile); { Print first line of Calendar } dayx[1] := cal (dayx[1], 1, mbase); dayx[2] := cal (dayx[2], 1, mbase+1); dayx[3] := cal (dayx[3], 1, mbase+2); writeln(ofile); { Print rest of Calendar } repeat dayx[1] := cal (bday, dayx[1], mbase); dayx[2] := cal (bday, dayx[2], mbase+1); dayx[3] := cal (bday, dayx[3], mbase+2); writeln(ofile); until dayx[1]+dayx[2]+dayx[3] = 0; writeln(ofile); end; end; { DOYEAR } {************************************************************************* Function: Initialize Initialize the command line pointer, the number of days in each month, and the names of the months. Input/Output Parameters: None **************************************************************************} procedure initialize; begin { Point to Command Line } command := @cmd; cmdline := command^; { Number of days in each month } mdays[1] := 31; mdays[2] := 28; mdays[3] := 31; mdays[4] := 30; mdays[5] := 31; mdays[6] := 30; mdays[7] := 31; mdays[8] := 31; mdays[9] := 30; mdays[10] := 31; mdays[11] := 30; mdays[12] := 31; { Names of each month } month[1] := 'JANUARY '; month[2] := 'FEBRUARY '; month[3] := 'MARCH '; month[4] := 'APRIL '; month[5] := 'MAY '; month[6] := 'JUNE '; month[7] := 'JULY '; month[8] := 'AUGUST '; month[9] := 'SEPTEMBER'; month[10] := 'OCTOBER '; month[11] := 'NOVEMBER '; month[12] := 'DECEMBER '; end; { Initialize } {Mainline} begin { Initialize Month Data and Command Line Pointer } initialize; { Print Banner } writeln('Calendar, Version ',(version div 10),'.',(version mod 10)); { Determine Output Direction } diskout := false; { Assume no disk output } conout := false; { Assume no console output } if pos ('/D',cmdline) <> 0 then begin diskout := true; write('Name of Disk Output File? '); readln(filename); end else if pos ('/P',cmdline) <> 0 then filename := 'LST:' else begin filename := 'CON:'; conout := true; end; { Open Output File or Device } assign (ofile, filename); rewrite(ofile); if ioresult = 255 then begin writeln ('Fatal Error: Cannot Open ', filename, ' for Output'); exit; end; writeln('Calendar Output File/Device is ',filename); { Determine which month was specified in command line } month1 := 0; { Assume none for all months } match := false; { No match found } for icount:=1 to 12 do begin mpos := pos (copy (month[icount],1,3), cmdline); if mpos <> 0 then begin if match then begin writeln('Error -- More than one month given'); exit; end; match := true; { We have a match } month1 := icount; mposfnd := mpos; end; end; { Extract Year from command line } yline := copy (cmdline, mposfnd, length(cmdline)-mposfnd+1); ypos := pos (' ', yline); year := copy (yline, ypos, length(yline)-ypos+1); while (length(year) <> 0) and (year[1] = ' ') do year := copy (year, 2, length(year)-1); year1 := number(year); { Convert Year String into Number } { If no year specified, give syntax of command } if year1 = 0 then begin cline; { Print syntax of command line } exit; end; { If year specified is out of range, say so } if year1 < byear1 then begin write('Invalid Year Specification'); writeln(' -- Year Specified was ',year1); writeln('Year MUST be such that ', byear1, ' <= Year'); cline; { Print syntax of command line } exit; end; { Determine Base Year from byear1 and Base Day from bday1 } byear := byear1; bday := bday1; while year1 > byear+44 do begin bdow := day_of_week (1,1,byear+44); { First day of leap year } byear := byear + 44; { Set byear to next 11th leap year } if bdow <= bday then bday := bday - bdow + 1 else bday := 7 - (bdow - bday) + 1; { bday = 1st Sunday of Leap Year } end; { Do Calendar } if ?match then doyear else domonth; if diskout then close (ofile, icount); end. {Mainline}