(**************************************************************************
*
* This program maintains a small work schedule covering the hours 8-6.
* It processes the following commands:
*
* sched employee startday endday starthour endhour
* The employee is added to the schedule in the range of days and
* hours indicated.
*
* clear startday endday starthour endhour
* The part of the schedule indicated is cleared any assignment
* of an employee during those hours is removed.
*
* unsched employee
* Remove the employee from all places in the schedule to which
* s/he has been assigned.
*
* print
* Print the schedule. Show a table with days along the top, and
* hours down the side, giving the scheduled employee in each
* position.
*
* total employee
* Print the total hours for which the employee is scheduled. If
* the employee does not appear in the table, the total is 0.
*
* quit
* Terminate the program.
**************************************************************************)
PROGRAM a1 (input,output);
USES dayio;
CONST
{ Open positions in the schedule. }
NotScheduled = ' ';
{ Max length of an employee name. }
EmployeeMaxLen = 8;
{ Hours in a day. }
FirstHour = 8;
LastHour = 17; { 5:00 PM in 24-hour time }
PastLastHour = 18; { One past, for while loops. }
{ How much room to allow for each day in the table. }
TableDayWidth = 9;
TYPE
{ The employee name type. }
EmployeeType = string[EmployeeMaxLen];
{ The type of the schedule ARRAY. }
{ HourType = FirstHour..LastHour; }
HourType = 8..17;
ScheduleType = ARRAY [HourType, DayType] OF EmployeeType;
{ HourScanType = FirstHour..PastLastHour; }
HourScanType = 8..18;
(***********************************************************************
* Procedure to read the next non-blank. It skips leading blanks, then
* reads the string up to the first blank or eoln.
***********************************************************************)
PROCEDURE ReadString(VAR Str: string);
VAR
Ch: char;
BEGIN
Ch := ' ';
WHILE (Ch = ' ') AND NOT eoln DO
read(Ch);
IF Ch = ' ' THEN
{ There is no command on this line. }
Str := ''
ELSE
BEGIN
{ Read the beast. }
Str := '';
WHILE (Ch <> ' ') AND NOT eoln DO
BEGIN
Str := Str + Ch;
read(Ch)
END;
IF Ch <> ' ' THEN
{ Command ended at eoln. }
Str := Str + Ch
END
END; { ReadString }
(***********************************************************************
* Procedure to read the arguments held in common by the sched
* clear commands. Returns them through the arguments. If there
* is some error, that is reported through the argument error.
* Precondition: Following the read pointer, the input contains
* two days of the week, then two integers. If all days are present and
* correct, the integers must be present and correct.
* Postcondition: If both strings are recognized day names,
* they are read, and the integers are read as well, and their values
* are loaded into StartDay, EndDay, StartHour, and EndHour, and Error
* is set to false. The hours are mapped to 24-hour clock time under
* the rule that hours less than 6 are PM, and others are AM. If a day
* is missing or not recognized, the rest of the input line is
* discarded, and Error is set to true. If there is extra information
* on the line, it is discared. The read pointer is left at the start
* of the following line.
***********************************************************************)
PROCEDURE ReadSchedClrArgs(
VAR StartDay, EndDay: DayType; { Input days. }
VAR StartHour, EndHour: HourType; { Input hour range. }
VAR Error: boolean); { Input error indicator.}
VAR
InputHour: integer; { Input hour value. }
{ Map time to 24-hours based on the AM/PM rules. }
FUNCTION MapTo24(Hour: integer): HourType;
CONST
{ AM/PM time cut-off. }
LastPM = 5;
BEGIN
IF Hour <= LastPM THEN
MapTo24 := Hour + 12
ELSE
MapTo24 := Hour
END;
BEGIN { ReadSchedClrArgs }
{ Read the days. }
ReadDay(input, StartDay);
ReadDay(input, EndDay);
{ See if they both worked. }
IF (StartDay <> BadDay) AND (EndDay <> BadDay) THEN
BEGIN
{ It worked. Read the hours. }
read(InputHour);
StartHour := MapTo24(InputHour);
read(InputHour);
EndHour := MapTo24(InputHour);
{ Report success }
Error := FALSE
END
ELSE
(* Something went wrong, seriously wrong. *)
Error := TRUE;
(* We're done with this line. *)
readln
END; { ReadSchedClrArgs }
{****************************************************************
* PROCEDURE to print headers of each day.
* Precondition: None.
* Postcondition: A header line with the days of the week has
* been printed. The
****************************************************************}
PROCEDURE WriteDaysHeader;
CONST
{ How many spaces to move over before printing days-of
the week header. }
DaysHeadMoveOver = 6;
{ How much room to assume is needed by each day string. }
AllowForDay = 3;
VAR
Day: DayType;
BEGIN
write(' ': DaysHeadMoveOver);
FOR Day := Sun TO Sat DO
BEGIN
write('[ ');
WriteDay(output, Day);
write(' ]', ' ': TableDayWidth - AllowForDay - 4)
END;
writeln
END; { WriteDaysHeader }
{****************************************************************
* Function that tells if a pending schedule is legal.
* Its arguments are those of sched, excluding the employee name.
* Precondition: FirstHour and LastHour are in range.
* Postcondition: If the indicated area of the schedule contains
* blanks in each entry, then return true, else false.
* Note: Schedule is sent by VAR for efficiency -- it is not
* changed.
****************************************************************}
FUNCTION SchedLegal(
VAR Schedule: ScheduleType; { Schedule to check. }
StartDay, EndDay: DayType; { Days in question. }
FirstHour, LastHour: { Hours in question. }
HourType): boolean;
VAR
ConflictFound: boolean; { Tell if one found. }
DayScan: DayType; { Go through the days. }
HourScan: HourScanType; { Go through the hours. }
BEGIN
{ Scan the days. }
DayScan := StartDay;
ConflictFound := FALSE;
REPEAT
{ For this day, scan the times. }
HourScan := FirstHour;
WHILE NOT ConflictFound AND
(HourScan <= LastHour) DO BEGIN
{ Conflict? }
ConflictFound :=
Schedule[HourScan, DayScan] <> NotScheduled;
{ Next one. }
HourScan := HourScan + 1
END;
{ Next Day. }
DayScan := succ(DayScan)
UNTIL ConflictFound or (DayScan > EndDay);
{ And the answer is.. }
SchedLegal := not ConflictFound
END; { SchedLegal }
{****************************************************************
* This takes care of most of the work of the clear and sched
* commands. Its arguments are those of sched, with blanks in
* Employee for the clear. It places this name in each indicated
* postion.
* Precondition: FirstHour and LastHour are in range.
* Postcondition: The area of the schedule is changed to show
* the indicated employee.
* Note: This will replace any old entry, so the sched command
* should call SchedLegal above to make sure the operation
* is legal before calling this routine.
****************************************************************}
PROCEDURE SetSchedPart(
VAR Schedule: ScheduleType; { Set me! Set me! }
Employee: EmployeeType; { Who gets to work. }
StartDay, EndDay: DayType; { Days in question. }
FirstHour, LastHour: { Hours in question. }
HourType);
VAR
DayScan: DayType; { Go through the days. }
HourScan: HourType; { Go through the hours. }
BEGIN
for DayScan := StartDay to EndDay do
for HourScan := FirstHour to LastHour do
Schedule[HourScan, DayScan] := Employee
END; { SetSchedPart }
{****************************************************************
* Perform the sched command.
* Precondition: The read pointer is followed by the arguments
* for the sched command.
* Postcondition: The arguments have been read and echoed, and the
* read pointer is on the next line. The sched command has been
* performed with appropriate messages.
* Note: DayMap is passed by VAR for efficiency -- it is not
* changed.
****************************************************************}
PROCEDURE DoSched(
VAR Schedule: ScheduleType); { Change this. }
VAR
Employee: EmployeeType; { Input employee name. }
StartDay, EndDay: DayType; { Input days. }
StartHour, EndHour: HourType; { Input hour range. }
Error: boolean; { Input error indicator.}
BEGIN
{ Read the employee name }
ReadString(Employee);
{ Read all the other arguments, and recieve error
indication. }
ReadSchedClrArgs(StartDay, EndDay, StartHour, EndHour, Error);
{ For errors, let 'em know. Otherwise, do it. }
IF Error THEN
writeln('*** Un-recognized day code. ',
'Command not performed. ***')
ELSE
{ See if the scheduling is legal. }
IF SchedLegal(Schedule, StartDay, EndDay,
StartHour, EndHour) THEN
BEGIN
{ Legal. Do it and admit it. }
SetSchedPart(Schedule, Employee,
StartDay, EndDay, StartHour, EndHour);
writeln('>>> ', Employee, ' scheduled. <<<')
END
ELSE
{ Not legal. }
writeln('*** Conflicts with existing schedule. ',
'Command not performed. ***')
END; { DoSched }
{****************************************************************
* Perform the clear command.
* Precondition: The read pointer is followed by the arguments
* for the clear command.
* Postcondition: The arguments have been read and echoed, and the
* read pointer is on the next line. The clear command has been
* performed with appropriate messages.
* Note: DayMap is passed by VAR for efficiency -- it is not
* changed.
****************************************************************}
PROCEDURE DoClear(
VAR Schedule: ScheduleType); { Change this. }
VAR
StartDay, EndDay: DayType; { Input days. }
StartHour, EndHour: HourType; { Input hour range. }
Error: boolean; { Input error indicator.}
BEGIN
{ Read the arguments, and recieve error indication. }
ReadSchedClrArgs(StartDay, EndDay, StartHour, EndHour, Error);
{ For errors, let 'em know. Otherwise, do it. }
IF Error THEN
writeln('*** Un-recognized day code. ',
'Command not performed. ***')
ELSE
BEGIN
SetSchedPart(Schedule, NotScheduled, StartDay, EndDay,
StartHour, EndHour);
writeln('>>> Clear performed. <<<');
END { DoClear }
END;
{****************************************************************
* Peform the unsched command.
* Precondition: The read pointer is followed by an employee
* name.
* Postcondition: The argument has been read and echoed, and the
* read pointer is on the next line. The employee read has been
* removed from Schedule.
****************************************************************}
PROCEDURE DoUnsched(
VAR Schedule: ScheduleType); { Remove from. }
VAR
Employee: EmployeeType; { To remove. }
Day: DayType; { Column scanner. }
Hour: integer; { Row scanner. }
Found: boolean; { Presence indicator }
BEGIN
{ Read the employee. }
readln(Employee);
{ Remove! Remove! }
Found := FALSE;
FOR Day := Sun TO Sat DO
FOR Hour := FirstHour TO LastHour DO
IF Schedule[Hour, Day] = Employee THEN
BEGIN
{ Remove. }
Schedule[Hour, Day] := NotScheduled;
{ Note. }
Found := TRUE
END;
{ Warn if not found. Else just state. }
IF Found THEN
write('>>> ', Employee, ' removed from schedule. <<<')
ELSE
write('>>> ', Employee,
' was not on the schedule. <<<')
END; { DoUnsched }
{****************************************************************
* Peform the print command.
* Precondition: None.
* Postcondition: Schedule has been printed to output.
****************************************************************}
PROCEDURE DoPrint(
VAR Schedule: ScheduleType); { Print me. }
VAR
Hour: HourType; { Hour scan. }
Day: DayType; { Day scan. }
{ Map from 24-hour time to 12-hour time. Arguments less than
13 are simply returned, arguments greater than 12 are
reduced by 12 and returned. }
FUNCTION Map24to12(HourType: HourType): integer;
BEGIN
IF Hour < 13 THEN
Map24to12 := Hour
ELSE
Map24to12 := Hour - 12
END;
BEGIN
readln;
WriteDaysHeader;
FOR Hour := FirstHour TO LastHour DO
BEGIN
write(Map24to12(Hour):2, ':00 ');
FOR Day := Sun TO Sat DO
write(Schedule[Hour, Day],
' ': TableDayWidth - length(Schedule[Hour, Day]));
writeln
END
END;
{****************************************************************
* Peform the total command.
* Precondition: The read pointer is followed by an employee
* name.
* Postcondition: The argument has been read and echoed, and the
* read pointer is on the next line. The total scheduled hours
* for the employee read has been printed.
****************************************************************}
PROCEDURE DoTotal(
VAR Schedule: ScheduleType); { The schedule. }
VAR
Employee: EmployeeType; { To remove. }
Day: DayType; { Column scanner. }
Hour: integer; { Row scanner. }
Total: integer; { Total intgers. }
BEGIN
{ Read the employee. }
readln(Employee);
{ Do the sum. }
Total := 0;
FOR Day := Sun TO Sat DO
FOR Hour := FirstHour TO LastHour DO
IF Schedule[Hour, Day] = Employee THEN
Total := Total + 1;
{ Write the total. }
writeln('>>> ', Employee,
' is scheduled for ', Total:1, ' hours. <<<<')
END; { DoTotal }
{*****************************************************************
* Main line.
*****************************************************************}
VAR
{ The schedule. }
Schedule: ScheduleType;
{ Main loop continue flag. }
KeepRunning: boolean;
{ Command input local to main. }
Command: string;
BEGIN
{ Clear the schedule. }
SetSchedPart(Schedule, NotScheduled, Sun, Sat, FirstHour, LastHour);
{ Do the commands. }
write('==> ');
ReadString(Command);
KeepRunning := TRUE;
WHILE KeepRunning DO
BEGIN
IF Command = 'sched' THEN
DoSched(Schedule)
ELSE IF Command = 'clear' THEN
DoClear(Schedule)
ELSE IF Command = 'unsched' THEN
DoUnsched(Schedule)
ELSE IF Command = 'print' THEN
DoPrint(Schedule)
ELSE IF Command = 'total' THEN
DoTotal(Schedule)
ELSE IF Command = 'quit' THEN
BEGIN
writeln;
writeln('>>> Program terminating. <<<');
KeepRunning := FALSE
END
ELSE
{ Command not recognized. }
BEGIN
readln;
writeln;
writeln('*** Command ', Command,
' not recognized. ***');
END;
{ Go to a new page for next'n. }
write('==> ');
ReadString(Command)
END
END.