home *** CD-ROM | disk | FTP | other *** search
- PROGRAM dates(input,output);
- { Bruce Hillyer. Keeps a list of memos. Displays appropriate calendars.
- Written for Turbo Pascal. }
-
- {$i zString.tur} { include null-terminated string routines }
-
- CONST
- yearBase = 1900; { add to 0..99 to get year }
- memoMax = 200; { number of memos the program can hold }
- display = 12; { number of memo lines to show under the calendar - 1 }
- statusLine = 10; { line for printing status }
- promptLine = 11;
- memoLine = 12;
- memoFileNm = '\dates.mem'; { file name to store memos, in root }
-
- TYPE
- dayType = 1..31;
- monthType = 1..12;
- yearType = 100..10000;
-
- dateType = RECORD
- day : dayType;
- month : monthType;
- year : yearType
- END;
-
- memoType = RECORD
- startDate : dateType;
- endDate : dateType;
- comment : zString
- END;
-
- memoArrayType = ARRAY[0..memoMax] OF memoType; { 0 is not used }
-
- VAR
- { date-handling global constants }
- monthName : ARRAY[monthType] OF STRING[10]; { month names }
- monthLen : ARRAY[monthType] OF INTEGER; { length of month names in chrs }
- monthSize : ARRAY[monthType] OF INTEGER; { days per month }
- monthOffset : ARRAY[monthType] OF INTEGER; { days before 1st of the month }
- dayName : ARRAY[dayType] OF STRING[10]; { day names }
- dayLen : ARRAY[dayType] OF INTEGER; { length of day names in chrs }
- today : dateType;
- tomorrow : dateType;
-
- { variables }
- memoFile : FILE OF memoType;
- memoArray : memoArrayType;
- nMemo : INTEGER;
- finish : BOOLEAN;
- currentLine : INTEGER;
- currentDate : dateType;
- showingDate : dateType;
- command : zString;
- pos : zStringSub;
-
-
-
-
- PROCEDURE pause;
- BEGIN GotoXY(1,25);
- ClrEol;
- Write(output,' (press return to continue)');
- WHILE NOT Keypressed DO { nothing }
- END; { pause }
-
-
-
-
-
- { ----------------------- date handling --------------------------- }
-
- PROCEDURE initDateConstants;
- BEGIN
- monthName[1] := 'January ';
- monthName[2] := 'February ';
- monthName[3] := 'March ';
- monthName[4] := 'April ';
- monthName[5] := 'May ';
- monthName[6] := 'June ';
- monthName[7] := 'July ';
- monthName[8] := 'August ';
- monthName[9] := 'September ';
- monthName[10]:= 'October ';
- monthName[11]:= 'November ';
- monthName[12]:= 'December ';
-
- monthLen[1] := 7;
- monthLen[2] := 8;
- monthLen[3] := 5;
- monthLen[4] := 5;
- monthLen[5] := 3;
- monthLen[6] := 4;
- monthLen[7] := 4;
- monthLen[8] := 6;
- monthLen[9] := 9;
- monthLen[10]:= 7;
- monthLen[11]:= 8;
- monthLen[12]:= 8;
-
- monthSize[1] := 31;
- monthSize[2] := 28;
- monthSize[3] := 31;
- monthSize[4] := 30;
- monthSize[5] := 31;
- monthSize[6] := 30;
- monthSize[7] := 31;
- monthSize[8] := 31;
- monthSize[9] := 30;
- monthSize[10] := 31;
- monthSize[11] := 30;
- monthSize[12] := 31;
-
- monthOffset[1] := 0;
- monthOffset[2] := 31;
- monthOffset[3] := 59;
- monthOffset[4] := 90;
- monthOffset[5] := 120;
- monthOffset[6] := 151;
- monthOffset[7] := 181;
- monthOffset[8] := 212;
- monthOffset[9] := 243;
- monthOffset[10] := 273;
- monthOffset[11] := 304;
- monthOffset[12] := 334;
-
- dayName[1] := 'Sunday ';
- dayName[2] := 'Monday ';
- dayName[3] := 'Tuesday ';
- dayName[4] := 'Wednesday ';
- dayName[5] := 'Thursday ';
- dayName[6] := 'Friday ';
- dayName[7] := 'Saturday ';
-
- dayLen[1] := 6;
- dayLen[2] := 6;
- dayLen[3] := 7;
- dayLen[4] := 9;
- dayLen[5] := 8;
- dayLen[6] := 6;
- dayLen[7] := 8;
- END; { initDateConstants }
-
-
-
- { ----- compare dates ----- }
-
- FUNCTION dateLT(date1,date2 : dateType) : BOOLEAN;
- { returns false if date2 is before date1 }
- BEGIN IF date1.year < date2.year THEN dateLT := TRUE
- ELSE IF date1.year > date2.year THEN dateLT := FALSE
- ELSE IF date1.month < date2.month THEN dateLT := TRUE
- ELSE IF date1.month > date2.month THEN dateLT := FALSE
- ELSE IF date1.day < date2.day THEN dateLT := TRUE
- ELSE dateLT := FALSE
- END; { dateLT }
-
-
-
- FUNCTION dateEQ(date1,date2 : dateType) : BOOLEAN;
- BEGIN
- dateEq := (date1.year = date2.year) AND (date1.month = date2.month)
- AND (date1.day = date2.day)
- END; { dateEQ }
-
-
-
-
-
-
- { ----- date manipulation ----- }
-
- FUNCTION leapYear(year : yearType) : BOOLEAN;
- { tells if the given year is a leap year }
- BEGIN IF (year Mod 4000) = 0 THEN leapYear := FALSE
- ELSE IF (year Mod 400) = 0 THEN leapYear := TRUE
- ELSE IF (year Mod 100) = 0 THEN leapYear := FALSE
- ELSE IF (year Mod 4) = 0 THEN leapYear := TRUE
- ELSE leapYear := FALSE
- END; { leapYear }
-
-
-
- FUNCTION weekDay(date : dateType) : INTEGER;
- { returns 1 for Sunday, 2 for Monday,...,7 for Friday }
- VAR dayCnt, yearM1 : INTEGER;
- BEGIN
- dayCnt := date.day + monthOffset[date.month];
- IF leapYear(date.year) AND (date.month > 2)
- THEN dayCnt := dayCnt + 1;
- yearM1 := date.year - 1;
- weekDay := 1 + ((dayCnt + yearM1
- + (yearM1 Div 4) - (yearM1 Div 100)
- + (yearM1 Div 400) - (yearM1 Div 4000)) Mod 7)
- END; { weekDay }
-
-
- PROCEDURE incrDate(inDate : dateType; VAR outDate : dateType);
- { increment the input date by one day to get the output date }
- BEGIN
- outDate := inDate;
- WITH outDate DO
- BEGIN
- { last day of year }
- IF (day = 31) AND (month = 12) THEN BEGIN year := year + 1;
- month := 1;
- day := 1;
- END
- { last day of month (leapyear ok by >) }
- ELSE IF (day >= monthSize[month]) THEN BEGIN month := month + 1;
- day := 1
- END
- { usual case }
- ELSE day := day + 1
- END
- END; { incrDate }
-
- { ----- parse dates from zStrings ----- }
-
- FUNCTION monthMatch(monthNum : monthType; inp : zString; start : zStringSub)
- : INTEGER;
- { look in the zString at the indicated starting location to see if it
- contains the name of that month. Return monthNum if it matches, 0 if
- not. If inp contains an abbreviation, that's ok. }
- VAR
- mi : INTEGER;
- zi : zStringSub;
- mChr : CHAR;
- zChr : CHAR;
- continue : BOOLEAN;
- BEGIN
- monthMatch := monthNum; { assume it will work }
- mi := 1;
- zi := start;
- continue := TRUE;
- WHILE continue DO
- IF mi > monthLen[monthNum] THEN continue := FALSE { matched name ok }
- ELSE IF inp[zi] = Chr(0) THEN continue := FALSE { abbreviation ok }
- ELSE BEGIN mChr := monthName[monthNum][mi];
- IF (mChr >= 'a') AND (mChr <= 'z')
- THEN mChr := Chr(Ord(mChr) - 32);
- zChr := inp[zi];
- IF (zChr >= 'a') AND (zChr <= 'z')
- THEN zChr := Chr(Ord(zChr) - 32);
- IF mChr = zChr
- THEN BEGIN mi := mi + 1;
- zi := zi + 1
- END
- ELSE BEGIN continue := FALSE;
- IF (zChr >= 'A') AND (zChr <= 'Z')
- THEN monthMatch := 0 { mismatch }
- { else abbrev ok }
- END
- END
- END; { monthMatch }
-
-
- PROCEDURE parseForMonth(inp : zString; VAR pos : zStringSub; scanSet : charSet;
- VAR monthNum : INTEGER; VAR got : BOOLEAN);
- { Looks in inp starting at pos for the name of a month, after skipping over
- members of the scanSet. If found, sets got TRUE and sets month number.
- If none or invalid, sets got FALSE. In either case, scans past contiguous
- letters starting at pos. Case doesn't matter. }
- VAR ch : CHAR;
- junk : BOOLEAN;
- savePos : zStringSub;
- BEGIN
- savePos := pos;
- monthNum := 0;
- IF scanPastSet(inp,scanSet,pos) THEN
- CASE inp[pos] OF
- 'F','f': monthNum := monthMatch(2,inp,pos);
- 'S','s': monthNum := monthMatch(9,inp,pos);
- 'O','o': monthNum := monthMatch(10,inp,pos);
- 'N','n': monthNum := monthMatch(11,inp,pos);
- 'D','d': monthNum := monthMatch(12,inp,pos);
- 'A','a': IF nextCh(inp,pos,ch)
- THEN IF ch IN ['P','p']
- THEN monthNum := monthMatch(4,inp,pos-1)
- ELSE IF ch IN ['U','u']
- THEN monthNum := monthMatch(8,inp,pos-1);
- 'M','m': IF nextCh(inp,pos,ch) THEN
- IF ch IN ['A','a'] THEN
- IF nextCh(inp,pos,ch)
- THEN IF ch IN ['R','r']
- THEN monthNum := monthMatch(3,inp,pos-2)
- ELSE IF ch IN ['Y','y']
- THEN monthNum := monthMatch(5,inp,pos-2);
- 'J','j': IF nextCh(inp,pos,ch) THEN
- IF ch IN ['A','a'] THEN monthNum := monthMatch(1,inp,pos-1)
- ELSE IF ch IN ['U','u'] THEN
- IF nextCh(inp,pos,ch) THEN
- IF ch IN ['N','n']
- THEN monthNum := monthMatch(6,inp,pos-2)
- ELSE IF ch IN ['L','l']
- THEN monthNum := monthMatch(7,inp,pos-2);
- ELSE { just return FALSE and clean up the input }
- END; { CASE }
- junk := scanPastSet(inp,letters,pos);
- got := monthNum IN [1..12];
- IF NOT got THEN pos := savePos
- END; { parseForMonth }
-
-
-
- PROCEDURE parseForDate(inp : zString; VAR pos : zStringSub; scanSet : charSet;
- VAR date : dateType; VAR gotDate : BOOLEAN);
- { Extract a date from inp starting at position pos (scans past scanSet).
- Return whether a valid date was found.
- Sets date to the value extracted, if any.
- Accepts most any reasonable format, such as
- 9/12/71 Sept. 12 1971 12 Sept 71
- If something like aa/bb is entered, it will be interpreted as day bb of
- month aa >= today, if possible, otherwise it will be interpreted
- as day=1, month aa, year bb. For example, if today is March 3, 1984,
- then 3/7 means March 7, 1984; 2/3 means February 3, 1985; and 9/85
- means September 1, 1985.
-
- }
- VAR
- ok, got : BOOLEAN;
- day, month, year, num1, num2 : INTEGER;
- separators : charSet;
- savePos : zStringSub;
- BEGIN
- savePos := pos;
- separators := [' ', '/', ',', '.', '-', '_', '~'];
-
- parseForInt(inp,pos,scanSet,num1,got);
- IF got
- THEN BEGIN { number first }
- parseForInt(inp,pos,separators,num2,got);
- IF got
- THEN BEGIN { mo#/yr# or mo#/dy#/yr# or mo#/dy#}
- month := num1;
- ok := TRUE;
- parseForInt(inp,pos,separators,year,got);
- IF got THEN day := num2
- ELSE IF num2 > 31
- THEN BEGIN day := 1;
- year := num2
- END
- ELSE BEGIN day := num2;
- year := today.year; { get from current }
- { if before today then must mean next yr}
- IF (month < today.month) OR
- ((month = today.month) AND
- (day < today.day))
- THEN year := year + 1
- END
- END { mo#/yr# or mo#/dy#/yr# }
- ELSE BEGIN { dy# month$ yr# or dy# month$ }
- parseForMonth(inp,pos,separators,month,got);
- IF NOT got
- THEN ok := FALSE
- ELSE BEGIN day := num1;
- parseForInt(inp,pos,separators,year,ok);
- IF NOT ok THEN
- BEGIN ok := TRUE;
- year := today.year;
- { if before today must mean next yr}
- IF (month < today.month) OR
- ((month = today.month) AND
- (day < today.day))
- THEN year := year + 1
- END
- END
- END { dy# month$ yr# or dy# month$ }
- END { number first }
- ELSE BEGIN { month$ dy#,yr# or month$ yr# or month$ dy# }
- parseForMonth(inp,pos,scanSet,month,got);
- IF NOT got
- THEN ok := FALSE
- ELSE BEGIN { get dy#,yr# or just yr# or just dy# }
- parseForInt(inp,pos,separators,num1,got);
- IF NOT got
- THEN ok := FALSE
- ELSE BEGIN { see if second number }
- ok := TRUE;
- parseForInt(inp,pos,separators,year,got);
- IF got THEN day := num1
- { if can't interpret num1 as day, it is yr }
- ELSE IF num1>31
- THEN BEGIN day := 1;
- year := num1
- END
- ELSE BEGIN day := num1;
- year := today.year;
- { before today must mean next yr}
- IF (month < today.month) OR
- ((month = today.month) AND
- (day < today.day))
- THEN year := year + 1
- END
- END { see if second number }
- END { get dy#,yr# or just yr# or just dy# }
- END; { month$ dy#,yr# or month$ yr# or month$ dy#}
-
-
-
- { check if date is valid - if so, return it }
- gotDate := FALSE;
- IF ok
- THEN BEGIN { check validity }
- IF year < 100 THEN year := year + yearBase;
- IF (yearBase <= year) AND (year <= 99+yearBase)
- THEN IF ((month = 2) AND (day IN [1..28]))
- OR ((month = 2) AND (day = 29) AND leapYear(year))
- OR ((month IN [1,3,5,7,8,10,12]) AND (day IN [1..31]))
- OR ((month IN [4,6,9,11]) AND (day IN [1..30]))
- THEN BEGIN gotDate := TRUE;
- date.day := day;
- date.month := month;
- date.year := year
- END
- END; { check validity }
- IF NOT gotDate THEN pos := savePos
- END; { parseForDate }
-
-
-
-
- { ----- input dates ----- }
-
- PROCEDURE askDate(VAR date : dateType; VAR quit : BOOLEAN);
- { accept valid date from input, or <cr> = quit }
- VAR dateOK : BOOLEAN;
- inp : zString;
- pos : zStringSub;
- BEGIN
- quit := FALSE;
- dateOK := FALSE;
- WHILE NOT quit AND NOT dateOK DO
- BEGIN
- readzStr(inp);
- IF inp[1] = Chr(0) THEN quit := TRUE
- ELSE BEGIN pos := 1;
- parseForDate(inp,pos,[' '],date,dateOK);
- IF NOT dateOK THEN
- Write(output,' date: ')
- END
- END
- END; { askDate }
-
-
- { ----- output dates ----- }
-
- PROCEDURE printSdate(date : dateType);
- { print date in ../../.. form }
- BEGIN WITH date DO
- Write(output,month:2,'/',day:2,'/',year-1900:2)
- END; { printSdate }
-
-
-
- PROCEDURE printWdate(date : dateType);
- { print date in Month dd, yyyy form }
- BEGIN WITH date DO
- Write(output,Copy(monthName[month],1,monthLen[month]),
- ' ',day:1,', ',year:1)
- END; { printWdate }
-
-
-
- PROCEDURE printDay(date : dateType);
- { print day of week word }
- VAR day : INTEGER;
- BEGIN
- day := weekDay(date);
- Write(output,Copy(dayName[day],1,dayLen[day]))
- END; { printDay }
-
-
- { ---------------------- system calls ---------------------------- }
-
-
- PROCEDURE systemDate(VAR date : dateType);
- { calls DOS to get the current date }
- VAR
- recpack : RECORD { register interface area for MSdos call }
- ax,bx,cx,dx,bp,si,ds,es,flags: INTEGER;
- END;
- dx,cx : INTEGER;
-
- BEGIN { sysDate }
- recpack.ax := $2A00;
- MSdos(recpack);
- date.year := recpack.cx;
- date.month := recpack.dx SHR 8;
- date.day := recpack.dx AND 255;
- END; { systemDate }
-
-
-
-
-
- { --------------------- memo handling ---------------------- }
-
- { ----- load from and save to file ----- }
-
- PROCEDURE loadMemo(VAR memoArray : memoArrayType; VAR nMemo : INTEGER);
- { read the contents of the memo file }
- BEGIN Assign(memoFile,memoFileNm);
- {$i-} { trap i/o errors }
- Reset(memoFile);
- {$i+}
- IF IOresult <> 0
- THEN BEGIN Rewrite(memoFile);
- Close(memoFile);
- Reset(memoFile)
- END;
- nMemo := 0;
- WHILE (nMemo < memoMax) AND NOT Eof(memoFile) DO
- BEGIN nMemo := nMemo + 1;
- Read(memoFile, memoArray[nMemo])
- END;
- IF NOT Eof(memoFile) THEN
- BEGIN Writeln(output);
- Writeln(output,'Program could not hold all the memos that',
- ' were in the file.');
- Writeln(output,'If you add or delete any memos, those that',
- ' didn''t fit in the program will be lost.');
- pause
- END;
- Close(memoFile);
- END; { loadMemo }
-
-
- PROCEDURE storeMemo(memoArray : memoArrayType; nMemo : INTEGER);
- { overwrite the contents of the memo file with memoArray }
- VAR i : INTEGER;
- BEGIN Assign(memoFile,memoFileNm);
- Rewrite(memoFile);
- FOR i:=1 TO nMemo DO
- Write(memoFile, memoArray[i]);
- Close(memoFile)
- END; { storeMemo }
-
-
- { ----- enter from input ----- }
-
- FUNCTION askMemo(VAR memo : memoType; getDates, getMemo : BOOLEAN) : BOOLEAN;
- { ask input for memo start date, end date, and comment }
- VAR quit,notSame : BOOLEAN;
- i : INTEGER;
- BEGIN quit := FALSE;
- IF getDates THEN
- BEGIN Insline;
- Write(output, 'Enter starting date (just return to quit): ');
- clrEol;
- askDate(memo.startDate,quit);
- IF NOT quit THEN
- BEGIN { not quit }
- Insline;
- Write(output,
- 'Enter ending date (just return for same): ');
- clrEol;
- askDate(memo.endDate,notSame);
- IF notSame THEN memo.endDate := memo.startDate;
- END { not quit }
- END; { askDates }
- IF getMemo AND NOT quit THEN
- BEGIN { getMemo }
- Insline;
- Write(output,' V');
- FOR i:=1 TO stringMax-3 DO
- Write(output,' ');
- Write(output,'V');
- clrEol;
- Writeln(output);
- Insline;
- Write(output,'memo:');
- clrEol;
- readzStr(memo.comment)
- END; { getMemo }
- askMemo := NOT quit
- END; { askMemo }
-
-
- { ----- add to and delete from memo array ----- }
-
- PROCEDURE addMemo(memo : memoType;
- VAR memoArray : memoArrayType; VAR nMemo : INTEGER;
- VAR slot : INTEGER);
- { insert memo in date order into memoArray, increment nMemo,
- set slot to the position inserted into, rewrite file }
- VAR loc : INTEGER;
- BEGIN
- IF nMemo = memoMax
- THEN BEGIN Insline;
- Write(output,' (no room to store this memo)');
- clrEol;
- pause
- END
- ELSE BEGIN
- loc := nMemo;
- memoArray[0] := memo;
- WHILE dateLT(memo.startDate, memoArray[loc].startDate) DO
- BEGIN memoArray[loc+1] := memoArray[loc];
- loc := loc - 1;
- END;
- slot := loc + 1;
- memoArray[slot] := memo;
- nMemo := nMemo + 1;
- storeMemo(memoArray,nMemo)
- END
- END; { addMemo }
-
-
-
- PROCEDURE deleteMemo(line : INTEGER;
- VAR memoArray : memoArrayType; VAR nMemo : INTEGER);
- { delete memo from memoArray, decrement nMemo, rewrite file }
- BEGIN
- IF (line > 0) AND (line <= nMemo) THEN
- BEGIN WHILE line < nMemo DO
- BEGIN memoArray[line] := memoArray[line+1];
- line := line + 1
- END;
- nMemo := nMemo - 1
- END;
- storeMemo(memoArray,nMemo)
- END; { deleteMemo }
-
-
-
-
- PROCEDURE printMemo(memo : memoType);
- { print a memo on one line }
- BEGIN WITH memo DO
- BEGIN printSdate(startDate);
- IF dateEQ(startDate,endDate)
- THEN BEGIN IF dateEQ(startDate,tomorrow)
- THEN Write(output,' -TOMORROW- ')
- ELSE IF dateEQ(startDate,today)
- THEN Write(output,' --TODAY-- ')
- ELSE IF dateLT(startDate,today)
- THEN Write(output,' (past) ')
- ELSE Write(output,' ',dayName[weekDay(startDate)],' ')
- END
- ELSE BEGIN Write(output,' - ');
- printSdate(endDate);
- Write(output,' ')
- END;
- printzStr(comment);
- Writeln(output)
- END
- END; { printMemo }
-
-
-
-
- PROCEDURE showMemos(currentLine : INTEGER; nMemo : INTEGER);
- { show as many memos as will fit, starting with currentLine }
- VAR line : INTEGER;
- BEGIN
- Gotoxy(40,statusLine); ClrEol;
- IF nMemo = 0
- THEN Writeln(output,' (no memos on file)')
- ELSE Writeln(output,nMemo:1,' memos on file');
- FOR line:=25 DOWNTO memoLine DO
- BEGIN Gotoxy(1,line);
- ClrEol;
- END;
- FOR line := 0 TO display DO
- IF (line + currentLine) <= nMemo
- THEN BEGIN Write(output,line+currentLine:3,': ');
- printMemo(memoArray[line+currentLine])
- END
- END; { showMemos }
-
-
-
- { ------------------------ calendar printing ------------------------- }
-
- PROCEDURE printCalendar(date : dateType);
- { prints calendars for the given month, as well as previous and next months }
- VAR
- d1, d2, d3, m1, m1Len, m2, m2Len, m3, m3Len, y1, y2, y3 : INTEGER;
- offset1, offset2, offset3 : INTEGER;
- line : INTEGER;
- blanks : STRING[30];
-
- PROCEDURE printDays(VAR day : INTEGER; monthSize : INTEGER);
- VAR i : INTEGER;
- BEGIN FOR i:=1 TO 7 DO
- BEGIN IF day IN [1..monthSize]
- THEN Write(output,day:3)
- ELSE Write(output,' ');
- day := day + 1
- END;
- END; { printDays, nested in printCalendar }
-
- BEGIN
- Gotoxy(1,1);
- blanks := ' ';
-
- m1 := date.month - 1;
- y1 := date.year;
- IF m1 = 0 THEN BEGIN m1 := 12;
- y1 := y1 - 1
- END;
- m1Len := monthLen[m1];
- m2 := date.month;
- y2 := date.year;
- m2Len := monthLen[m2];
-
- m3 := date.month + 1;
- y3 := date.year;
- IF m3 = 13 THEN BEGIN m3 := 1;
- y3 := y3 + 1
- END;
- m3Len := monthLen[m3];
-
- { print the month headers }
- offset1 := 9 - m1Len Div 2;
- offset2 := 37 - m2Len Div 2;
- offset3 := 65 - m3Len Div 2;
-
- Write(output,Copy(blanks,1,offset1),
- Copy(monthName[m1],1,m1Len),y1:5,
- Copy(blanks,1,offset2-(offset1+m1Len+5)),
- Copy(monthName[m2],1,m2Len),y2:5,
- Copy(blanks,1,offset3-(offset2+m2Len+5)),
- Copy(monthName[m3],1,m3Len),y3:5);
- ClrEol;
- Writeln(output);
-
- Writeln(output,' S M T W R F S S M T W R F S ',
- ' S M T W R F S');
- Writeln(output,' --------------------- ---------------------',
- ' ---------------------');
-
- { now set day counters to place the first of the month for m1,m2,m3 }
- WITH date DO
- BEGIN day := 1;
- month := m1;
- year := y1;
- d1 := 2 - weekDay(date);
- IF leapYear(y1) AND (m1 = 2) THEN m1 := monthSize[m1] + 1
- ELSE m1 := monthSize[m1];
-
- month := m2;
- year := y2;
- d2 := 2 - weekDay(date);
- IF leapYear(y2) AND (m2 = 2) THEN m2 := monthSize[m2] + 1
- ELSE m2 := monthSize[m2];
-
- month := m3;
- year := y3;
- d3 := 2 - weekDay(date);
- IF leapYear(y3) AND (m3 = 2) THEN m3 := monthSize[m3] + 1
- ELSE m3 := monthSize[m3];
- END;
-
- { print the day numbers }
- FOR line := 1 TO 6 DO
- BEGIN printDays(d1,m1);
- Write(output,' ');
- printDays(d2,m2);
- Write(output,' ');
- printDays(d3,m3);
- Writeln(output)
- END
- END; { printCalendar }
-
-
- { ---------------------- command routines ----------------------- }
-
- PROCEDURE helpCommand;
- { list available commands }
- BEGIN Gotoxy(1,promptLine);
- Write(output,'line <num> date <date> add remove <num> quit');
- clrEol;
- pause
- END; { help }
-
-
-
- PROCEDURE lineCommand(command : zString; pos : zStringSub;
- nMemo : INTEGER; memoArray : memoArrayType;
- VAR currentLine : INTEGER; VAR currentDate : dateType);
- { Set current line to the line number indicated, and currentDate to the
- date on that line. }
- VAR
- inpLine : INTEGER;
- ok : BOOLEAN;
- BEGIN
- parseForInt(command,pos,
- ['a'..'z','A'..'Z',' ',':','-',',','.'],inpLine, ok);
- IF ok
- THEN IF (inpLine > 0) AND (inpLine <= nMemo)
- THEN BEGIN currentLine := inpLine;
- currentDate := memoArray[currentLine].startDate
- END
- ELSE BEGIN Insline;
- Write(output,'line ',inpLine:1,' is not on file');
- clrEol;
- pause
- END
- ELSE BEGIN Insline;
- Write(output,
- 'usage: l n where n is the line number you want');
- clrEol;
- pause
- END
- END; { lineCommand }
-
-
-
- PROCEDURE dateCommand(command : zString; pos : zStringSub;
- nMemo : INTEGER; memoArray : memoArrayType;
- VAR line : INTEGER; VAR currentDate : dateType);
- { Set line to the first line after the date requested (may be after
- the last memo line), default today, and currentDate to the date. }
- VAR continue : BOOLEAN;
- change : BOOLEAN;
- got : BOOLEAN;
- BEGIN
- change := FALSE;
- IF scanPastSet(command,['A'..'Z','a'..'z'],pos) AND
- scanToSet(command,[' '],pos)
- THEN BEGIN parseForDate(command,pos,[' '],currentDate,got);
- IF got THEN change := TRUE
- ELSE BEGIN Insline;
- Write(output,' (valid date not found)');
- clrEol;
- pause
- END
- END
- ELSE BEGIN change := TRUE;
- currentDate := today
- END;
-
- { find line for date }
- IF change THEN
- BEGIN line := 1;
- continue := TRUE;
- WHILE continue DO
- IF line > nMemo THEN continue := FALSE
- ELSE IF dateLT(memoArray[line].startDate,currentDate)
- THEN line := line + 1
- ELSE continue := FALSE
- END { find line for date }
- END; { dateCommand }
-
-
-
- PROCEDURE addMemoCommand(command : zString; pos : zStringSub;
- VAR nMemo : INTEGER; VAR memoArray : memoArrayType;
- VAR currentLine : INTEGER; VAR currentDate :dateType);
- VAR memo : memoType;
- date : dateType;
- gotDates, gotMemo : BOOLEAN;
- delims : charSet;
- got : BOOLEAN;
- BEGIN
- gotDates := FALSE;
- gotMemo := FALSE;
- delims := [' ', '-', ':', ','];
-
- IF scanPastSet(command,['A'..'Z','a'..'z'],pos) THEN
- WITH memo DO
- BEGIN parseForDate(command,pos,[' '],startDate,gotDates);
- IF gotDates THEN
- BEGIN parseForDate(command,pos,delims,endDate,got);
- IF NOT got THEN endDate := startDate;
- parseForText(command,pos,delims,memo.comment,gotMemo);
- END
- END;
-
- IF askMemo(memo,NOT gotDates, NOT gotMemo)
- THEN BEGIN addMemo(memo,memoArray,nMemo,currentLine);
- currentDate := memo.startDate
- END
- ELSE BEGIN Insline;
- Write(output,' (no memo added)'); clrEol;
- pause
- END
- END; { addMemoCommand }
-
-
-
-
- PROCEDURE removeMemoCommand(command : zString; pos : zStringSub;
- VAR nMemo : INTEGER; VAR smemoArray : memoArrayType;
- VAR currentLine : INTEGER; VAR currentDate : dateType);
- VAR inpLine : INTEGER;
- ok : BOOLEAN;
- confirmStr : STRING[10];
- BEGIN
- parseForInt(command,pos,
- ['a'..'z','A'..'Z',' ',':','-',',','.'],inpLine, ok);
- IF ok THEN
- IF (inpLine < 1) OR (inpLine > nMemo)
- THEN BEGIN Insline;
- Write(output,'line ',inpLine:1,' is not on file');
- clrEol;
- pause
- END
- ELSE BEGIN Insline;
- printMemo(memoArray[inpLine]);
- Insline;
- Write(output,' [confirm]'); ClrEol;
- Readln(input,confirmStr);
- IF Length(confirmStr) = 0
- THEN BEGIN deleteMemo(inpLine,memoArray,nMemo);
- currentLine := inpLine;
- currentDate :=
- memoArray[currentLine].startDate
- END
- ELSE BEGIN Insline;
- Write(output,' (nothing removed: "',
- confirmStr,'")');
- clrEol;
- pause
- END
- END
- END; { removeMemoCommand }
-
-
-
- BEGIN { main }
- initDateConstants;
- systemDate(today);
- currentDate := today;
- incrDate(today,tomorrow);
- loadMemo(memoArray,nMemo);
- IF nMemo > 0 THEN currentLine := 1
- ELSE currentLine := 0;
- lowVideo;
- clrScr;
-
- finish := FALSE;
- showingDate := currentDate;
- showingDate.month := 0; { force initial display of calendar }
- WHILE NOT finish DO
- BEGIN { WHILE NOT finish }
- IF (showingDate.day <> currentDate.day) OR
- (showingDate.year <> currentDate.year) OR
- (showingDate.month <> currentDate.month)
- THEN BEGIN IF (showingDate.month <> currentDate.month) OR
- (showingDate.year <> currentDate.year)
- THEN printCalendar(currentDate);
- Gotoxy(1,statusLine);
- printDay(currentDate);
- Write(output,', ');
- printWdate(currentDate);
- clrEol;
- showingDate := currentDate
- END;
- { adjust line to show a screen full and prevent line > nMemo }
- IF currentLine > (nMemo-display) THEN currentLine := nMemo-display;
- IF currentLine < 1 THEN currentLine := 1;
-
- showMemos(currentLine,nMemo);
-
- Gotoxy(1,promptLine);
- Write(output,'Dates>');
- ClrEol;
- readzStr(command);
- pos := 1;
- IF scanToSet(command, letters+['?'], pos)
- THEN
- CASE command[pos] OF
- 'H','h','?': helpCommand;
- 'L','l': lineCommand(command,pos,nMemo,memoArray,
- currentLine,currentDate);
- 'D','d': dateCommand(command,pos,nMemo,memoArray,
- currentLine,currentDate);
- 'A','a': addMemoCommand(command,pos,nMemo,memoArray,
- currentLine,currentDate);
- 'R','r': removeMemoCommand(command,pos,nMemo,memoArray,
- currentLine,currentDate);
- 'Q','q': finish := TRUE;
- ELSE
- BEGIN IF Ord(command[pos]) = monthOffset[4]-monthLen[5] {'W'}
- THEN BEGIN Write(output,Chr(monthOffset[3]+monthLen[1]));
- Write(output,Chr(3*monthSize[2]-monthLen[9]));
- Write(output,Chr(1+monthSize[1]));
- pos := monthOffset[4]-10; { 80 }
- Write(output,Chr(pos-8)); {'H'}
- Write(output,Chr(pos-monthLen[1])); {'I'}
- Write(output,Chr(pos-4),Chr(pos-4)); {'LL'}
- Write(output,Chr(pos+9)); {'Y'}
- Write(output,Chr(monthOffset[3]+10)); {'E'}
- Writeln(output,Chr(2+pos))
- END
- ELSE BEGIN Write(output,' (no such command)');
- clrEol
- END;
- pause
- END
- END { case }
- END; { WHILE NOT finish }
- Gotoxy(1,24)
- END. { main }
- -------
-
- { zstring.tur }
-
- {$R+} { subscript range checking }
-
- { null-terminated string routines - Bruce K. Hillyer }
-
- { zString definitions and procedures. Included are global definitions
- for letters, digits, alphamerics charSets. The global constant stringMax
- is defined to be the length of the strings used. }
-
-
- CONST
- stringMax = 50; { this is the length of zStrings we will use }
-
- TYPE
- charSet = SET OF CHAR;
- zStringSub = 1..StringMax;
- zString = STRING[stringMax];
- zStrFilTyp = FILE OF zString;
- zStrAds = ^zString; { in MS-Pascal, this will be ADS OF zString }
-
-
- CONST
- letters : charSet = ['A'..'Z','a'..'z'];
- digits : charSet = ['0'..'9'];
- nameChrs : charSet = ['A'..'Z', 'a'..'z', ',', '.', '''', '-', '&'];
- addrChrs : charSet = ['A'..'Z', 'a'..'z', '0'..'9',
- ',', '.', '''', '-', '&', '#', '%', '/'];
-
-
-
-
- { ---------------------- zString handling ------------------------ }
-
-
-
-
- PROCEDURE readzStr(VAR str : zString);
- { get string from input }
- BEGIN
- Readln(input,str);
- IF Length(str) >= stringMax THEN str[stringMax] := Chr(0)
- ELSE str := str + Chr(0)
- END; { readzStr }
-
-
-
- PROCEDURE printzStr(VAR str : zString);
- { str is VAR just to avoid copying }
- VAR pos : zStringSub;
- BEGIN
- pos := 1;
- WHILE str[pos] <> Chr(0) DO
- BEGIN Write(output,str[pos]);
- pos := pos + 1
- END
- END; { printzStr }
-
-
-
- FUNCTION scanToSet(VAR str : zString; breakSet : charSet;
- VAR pos : zStringSub) : BOOLEAN;
- { Returns whether a member of the breakSet was found starting from pos.
- Sets pos to the position the member was found at; undefined if not found.}
- { str and breakSet (was) are VAR just to avoid copying }
- VAR continue : BOOLEAN;
- BEGIN
- continue := TRUE;
- WHILE continue DO
- IF str[pos] = Chr(0) THEN BEGIN continue := FALSE;
- scanToSet := FALSE
- END
- ELSE IF str[pos] IN breakSet
- THEN BEGIN continue := FALSE;
- scanToSet := TRUE
- END
- ELSE pos := pos + 1;
- END; { scanToSet }
-
-
-
- FUNCTION scanPastSet(VAR str : zString; scanSet : charSet;
- VAR pos : zStringSub) : BOOLEAN;
- { Returns whether a char not in the scanSet was found starting from pos.
- Sets pos to the position the char was found at; undefined if not found. }
- { str and scanSet (was) are VAR just to avoid copying }
- VAR continue : BOOLEAN;
- BEGIN
- continue := TRUE;
- WHILE continue DO
- IF str[pos] = Chr(0) THEN BEGIN continue := FALSE;
- scanPastSet := FALSE
- END
- ELSE IF str[pos] IN scanSet
- THEN pos := pos + 1
- ELSE BEGIN continue := FALSE;
- scanPastSet := TRUE
- END
- END; { scanPastSet }
-
-
-
-
- FUNCTION nextCh(VAR inp :zString; VAR pos :zStringSub; VAR ch :CHAR) : BOOLEAN;
- { Increments pos, sets ch to the next char in inp, and returns TRUE, but
- returns FALSE if no more chars available }
- { inp is VAR just to avoid copying }
- BEGIN
- IF inp[pos] = Chr(0) THEN nextCh := FALSE
- ELSE BEGIN pos := pos + 1;
- IF inp[pos] = Chr(0) THEN nextCh := FALSE
- ELSE BEGIN ch := inp[pos];
- nextCh := TRUE
- END
- END
- END; { nextCh }
-
-
-
- PROCEDURE parseForText(VAR inp : zString; VAR pos : zStringSub;
- scanSet : charSet;
- VAR ans : zString; VAR got : BOOLEAN);
- { returns TRUE and updates pos if there was some chr (past any members
- of the scanSet) not in the scanSet. }
- { inp and scanSet (was) are VAR just to avoid copying }
- VAR savePos, i : zStringSub;
- BEGIN
- savePos := pos;
- got := scanPastSet(inp,scanSet,pos);
- IF got THEN BEGIN i := 1;
- WHILE inp[pos] <> Chr(0) DO
- BEGIN ans[i] := inp[pos];
- i := i + 1;
- pos := pos + 1
- END;
- ans[i] := Chr(0)
- END
- ELSE pos := savePos
- END; { parseForText }
-
-
-
- PROCEDURE parseForInt(VAR inp : zString; VAR pos : zStringSub;
- scanSet : charSet;
- VAR ans : INTEGER; VAR got : BOOLEAN);
- { Looks in inp starting at pos for an integer, after skipping over
- members of the scanSet. If an integer found, sets got TRUE and
- puts value into ans. If no integer, or overflow, sets got FALSE. }
- { inp and scanSet (was) are VAR just to avoid copying }
- VAR bigAns, max : REAL; { to prevent integer ovfl +++ use INT4 in MS-Pas }
- negative : BOOLEAN;
- continue : BOOLEAN;
- savePos : zStringSub;
- BEGIN
- savePos := pos;
- max := Maxint; { REAL copy }
- got := FALSE;
- negative := FALSE;
- IF scanPastSet(inp,scanSet,pos) THEN
- IF inp[pos] IN digits+['-','+'] THEN
- BEGIN IF inp[pos] = '+'
- THEN pos := pos + 1
- ELSE IF inp[pos] = '-' THEN BEGIN negative := TRUE;
- pos := pos + 1
- END;
- bigAns := 0;
- continue := TRUE;
- WHILE continue DO
- BEGIN IF NOT (inp[pos] IN digits) THEN continue := FALSE
- ELSE BEGIN bigAns := 10*bigAns + Ord(inp[pos]) - Ord('0');
- pos := pos + 1;
- IF bigAns <= max THEN got := TRUE
- ELSE BEGIN got := FALSE;
- continue := FALSE
- END
- END
- END; { WHILE continue DO }
- IF got THEN BEGIN ans := Round(bigAns);
- IF negative THEN ans := - ans
- END
- ELSE pos := savePos
- END { IF inp[pos] IN signed digits }
- END; { parseForInt }
-
-
- FUNCTION zStrAdsGE(str1, str2 : zStrAds) : BOOLEAN;
- { return TRUE if str1^ >= str2^. Necessary to compare this way in case
- both strings are the same length, in which case junk after the Chr(0)
- would give spurious failures. }
- VAR
- i : INTEGER;
- continue : BOOLEAN;
- BEGIN
- i := 1; { we won't check stringMax because will hit Chr(0) first }
- continue := TRUE;
- WHILE continue DO
- IF str2^[i] = Chr(0)
- THEN BEGIN continue := FALSE;
- zStrAdsGE := TRUE { greater or equal, since str2 end }
- END
- ELSE IF str1^[i] < str2^[i]
- THEN BEGIN continue := FALSE;
- zStrAdsGE := FALSE { str1 is shorter (Chr(0)) or less }
- END
- ELSE IF str1^[i] > str2^[i]
- THEN BEGIN continue := FALSE;
- zStrAdsGE := TRUE { str1 is greater }
- END
- ELSE i := i + 1
- END; { zStrAdsGE }
-
-
-
- FUNCTION zStrEQ(VAR str1 : zString; VAR str2 : zString) : BOOLEAN;
- { str1 and str2 are VAR just to avoid copying }
- { return TRUE if str1 = str2 in chr and len }
- VAR
- i : INTEGER;
- continue : BOOLEAN;
- BEGIN
- i := 1; { we won't check stringMax because will hit Chr(0) first }
- continue := TRUE;
- WHILE continue DO
- IF str1[i] = Chr(0) THEN
- BEGIN continue := FALSE;
- zStrEQ := (str2[i] = Chr(0))
- END
- ELSE IF str1[i] <> str2[i] THEN
- BEGIN continue := FALSE;
- zStrEQ := FALSE
- END
- ELSE i := i + 1
- END; { zStrEQ }
-
-
-
- FUNCTION zStrPartialMatch(VAR key : zString; VAR str : zString) : BOOLEAN;
- { if the key matches str up to the end of key (str can be longer)
- then return true. Case sensitive; probably caller should upCase key. }
- VAR
- i : INTEGER;
- continue : BOOLEAN;
- BEGIN
- i := 1;
- continue := TRUE;
- WHILE continue DO
- IF key[i] = Chr(0) THEN BEGIN continue := FALSE;
- zStrPartialMatch := TRUE
- END
- ELSE IF key[i] <> str[i] THEN BEGIN continue := FALSE;
- zStrPartialMatch := FALSE
- END
- ELSE i := i + 1
- END; { zStrPartialMatch }
-
-
-
- PROCEDURE zStrUpCase(VAR str : zString);
- { convert str to uppercase }
- VAR i : INTEGER;
- BEGIN
- i := 1;
- WHILE str[i] <> Chr(0) DO
- BEGIN IF (str[i] >= 'a') AND (str[i] <= 'z')
- THEN str[i] := Chr(Ord(str[i]) - 32);
- i := i + 1
- END
- END; { zStrUpCase }
-
-
- PROCEDURE zStrCopy(VAR src : zString; VAR dest : zString);
- { copy the source into the target up to the src's null }
- VAR i : INTEGER;
- BEGIN
- i := 0;
- REPEAT i := i + 1;
- dest[i] := src[i]
- UNTIL src[i] = Chr(0)
- END; { zStrCopy }
-
-
-
- FUNCTION zStrLen(VAR str : zString) : INTEGER;
- { count the number of characters }
- VAR i : INTEGER;
- BEGIN
- i := 0;
- WHILE str[i+1] <> Chr(0) DO
- i := i + 1;
- zStrLen := i
- END; { zStrLen }
-
-
-
-
- PROCEDURE zStrTrimR(VAR str : zString);
- { remove any trailing blanks }
- VAR i : INTEGER;
- continue : BOOLEAN;
- BEGIN
- i := zStrLen(str);
- continue := TRUE;
- WHILE continue DO
- IF i = 0 THEN continue := FALSE
- ELSE IF str[i] <> ' ' THEN continue := FALSE
- ELSE i := i - 1;
- str[i+1] := Chr(0)
- END; { zStrTrimR }
-
-
-