
{This program was originally written by Charles Brown.
 It was modified on 11-18-87 by Alta M. Paul.

 Simplified, JUNE, 1988. A.M.P.

 Modified AUGUST and SEPTEMBER 1988 by Carla C. Bazemore.

 This program produces 4 sorted reports from the file 
 SYSTEM3:[BB$PROGRAM.DATA]BB$TRACKER.LOG.  These reports are used 
 by management and are produced at the beginning of each  month or
 whenever requested.  The bulletin board manager must have access to
 the tracker log file.  The file must be copied to the manager's
 disk and edited so that the file only contains the current month's
 data. This program should be edited to reflect the Bulletin Board
 manager's area on the file specification.

 The modifications made by Carla C. Bazemore include two new routines
 HISTOGRAM_TIME and CALCULATE.   The routine HISTOGRAM_TIME generates
 an hourly chart for the number of times the Bulletin Board was
 accessed.  The routine CALCULATE calculates the number of users who
 accessed the Bulletin Board.
 
}
program bb$reporter(input,output,bblog,bbreport,infile);

const {location of input and output files}
  REPORT_LOCATION = 'DUA107:[BAZEMORE.PASCAL]';
  FILE_LOCATION = 'DUA107:[BAZEMORE.PASCAL]';

type 
  sort_type = (name,length_used,date_used,number_of_times);
  numb = array[0..23] of integer;
  string = varying[80] of char;
  string1 = varying[90] of char;
  c11 = packed array[1..11] of char;
  user = ^user_rec;
  user_rec = record 
    start_time : string;
    name : string;
    length_used : string;
    length_used_next : user;
    length_used_last : user;
    date_used : string;
    date_used_next : user;
    date_used_last : user;
    finish_time : string;
    number_of_uses : integer;
    number_next : user;
    number_last : user;
    next : user;
    last : user;
  end;

  time_type = packed array[1..11] of char;

var 
  access_count,i : integer;
  histogram : numb;
  average_time,access_time,first,second : string;
  name_root,date_root,time_root,number_root : user;
  inline : string1;
  infile,bblog : text;
  bbreport : text;
  sort_by : sort_type;
  start_date,end_date: packed array[1..11] of char;
  count : integer;
  ok : boolean;

procedure node_maker(var node : user);

{ This procedure initializes the pointers. }

var 
  p:user;
begin
  new(p);
  with p^ do
    begin
      start_time := '';
      name := '';
      length_used := '';
      date_used := '';
      finish_time := '';
      number_of_uses := 1;
      next := nil;
      last := nil;
      length_used_next := nil;
      length_used_last := nil;
      date_used_next := nil;
      date_used_last := nil;
      number_next := nil;
      number_last := nil;
    end;
  node := p;
end;
		

procedure starter;

{ This procedure opens input/output files and calls the procedure
  to initialize the pointers. }

begin
  open(bblog,file_location+'bb$tracker.log',history:=old);
  reset(bblog);
  open(bbreport,report_location+'bbreport.dat',history:=new);
  rewrite(bbreport);
  node_maker(name_root);
  name_root := nil;
  node_maker(time_root);
  time_root := nil;
  node_maker(date_root);
  date_root := nil;
  node_maker(number_root);
  number_root := nil;
end;

procedure print_report(root:user;sort_by:sort_type);

{This procedure prints the reports for the four different sort 
 types (name, date_used, length_used and number_of_times). }

var temp : user;
    b1,b2,line_count,page_count :integer;
begin
  page_count := 1;
  line_count := 3;
  writeln(bbreport,' Report covering period ',
          Start_Date,' to ',end_date,' sorted by ',sort_by);
  write(bbreport,'         User      |   Times    |');
  writeln(bbreport,'  Total Time      |  Date Last  |');
  write(bbreport,'         Name      |  Accessed  |');
  write(bbreport,'     Accessed     |  Accessed   |  page ');
  writeln(bbreport,page_count:1);
  write(bbreport,'----------------------------------');
  writeln(bbreport,'------------------------------------------');
  temp := root;
  while (temp <> nil) do
    begin
      with temp^ do
        begin
          if line_count > 49 then
            begin 
              page(bbreport);
              page_count := page_count + 1;
              writeln(bbreport,' Report covering period ',
                      Start_Date,' to ',end_date,' sorted by ',sort_by);
              write(bbreport,'         User      |   Times    |');
              writeln(bbreport,'  Total Time      |  Date Last  |');
              write(bbreport,'         Name      |  Accessed  |');
              write(bbreport,'     Accessed     |  Accessed   |  page ');
              writeln(bbreport,page_count:1);
              write(bbreport,'----------------------------------');
              writeln(bbreport,'------------------------------------------');
              writeln(bbreport);
              line_count := 3;
            end;
  	  line_count := line_count + 1;
  	  b1 := 22 - length(name);
  	  writeln(bbreport,name,' ':b1,number_of_uses:4,' ':8,
                    length_used:14,' ':5,date_used);
        end;
      case sort_by of
        name : temp := temp^.next;
	length_used : temp := temp^.length_used_next;
	date_used : temp := temp^.date_used_next;
	number_of_times : temp := temp^.number_next;
      end;{case}
    end;	
  page(bbreport)
end;

procedure calculate(root:user;
                       var access_count: integer;
                       var access_time,average_time : string);

{  This procedure calculates the number of users, total time 
   accessed and average time accessed. }

var temp : user;
    temp_hours,temp_minutes,temp_seconds,
    temp_huns : varying[2] of char;
    temp1_hours,temp1_minutes,temp1_seconds,temp1_huns,
    temp2_hours,temp2_minutes,temp2_seconds,temp2_huns,
    avg_time,tot_time,hours,minutes,seconds,huns,
    avg_hours,avg_minutes,avg_seconds,avg_huns : integer;

begin
  temp2_hours := 0;
  temp2_minutes := 0;
  temp2_seconds := 0;
  temp2_huns := 0;
  avg_time := 0;
  temp := root;

 { convert from character string to integers }

  while (temp <> nil) do
    begin 
    { extract the character strings for hours, minutes,
     seconds and hundreds }
           temp_hours := substr(temp^.length_used,1,2);
           temp_minutes := substr(temp^.length_used,4,2);
           temp_seconds := substr(temp^.length_used,7,2);
           temp_huns := substr(temp^.length_used,10,2);

    { convert the character values to integer values }
           temp1_hours := ((ord(temp_hours[1])-ord('0'))*10) +
                           (ord(temp_hours[2])-ord('0'));
           temp1_minutes := ((ord(temp_minutes[1])-ord('0'))*10) +
                              (ord(temp_minutes[2])-ord('0'));
           temp1_seconds := ((ord(temp_seconds[1])-ord('0'))*10) +
                               (ord(temp_seconds[2])-ord('0'));
           temp1_huns := ((ord(temp_huns[1])-ord('0'))*10) +
                          (ord(temp_huns[2])-ord('0'));

    { calculate the total values for hours, munites, seconds and 
      hundreds }
           temp2_hours := temp2_hours + temp1_hours;
           temp2_minutes := temp2_minutes + temp1_minutes;
           temp2_seconds := temp2_seconds + temp1_seconds;
           temp2_huns := temp2_huns + temp1_huns;
        temp := temp^.next;

    { count the number of user accesses }
        access_count := access_count + 1 
    end; 

    { reduce time to hundreds and calculate the total time accessed }

           tot_time := temp2_hours*60;
           tot_time := (tot_time + temp2_minutes)*60;
           tot_time := (tot_time + temp2_seconds)*100;
           tot_time := (tot_time + temp2_huns);

    { calculate the average time }
           if access_count <> 0 then
            avg_time := tot_time div count;

    { convert total time back to hours, minutes, seconds and hundreds }
           huns := (tot_time mod 100);
           seconds := (tot_time div 100);
           minutes := (seconds div 60);
           hours   := (minutes div 60);
           seconds := seconds - (minutes*60);
           minutes := minutes - (hours*60);

    { convert average time to hours, minutes, seconds and hundreds }
           if avg_time <> 0 then
            begin
             avg_huns :=(avg_time mod 100);
             avg_seconds := (avg_time div 100);
             avg_minutes := (avg_seconds div 60);
             avg_hours := (avg_minutes div 60);
             avg_seconds := avg_seconds - (avg_minutes*60);
             avg_minutes := avg_minutes - (avg_hours*60);

    { convert integer values for average time to character string }
           if (avg_hours in [0..9])then 
             average_time[1] := '0'
           else
            begin
             temp1_hours := avg_hours div 10;
             average_time[1] :=chr(temp1_hours+48)
            end;
           temp1_hours := avg_hours mod 10;
           average_time[2] := chr(temp1_hours+48);
           average_time[3] := ':';
           if (avg_minutes in [0..9])then 
             average_time[4] := '0'
           else
            begin
             temp1_minutes := avg_minutes div 10;
             average_time[4] :=chr(temp1_minutes+48)
            end;
           temp1_minutes := avg_minutes mod 10;
           average_time[5] := chr(temp1_minutes+48);
           average_time[6] := ':';
           if (avg_seconds in [0..9])then 
             average_time[7] := '0'
           else
            begin
             temp1_seconds := avg_seconds div 10;
             average_time[7] :=chr(temp1_seconds+48)
            end;
           temp1_seconds := avg_seconds mod 10;
           average_time[8] := chr(temp1_seconds+48);
           average_time[9] := '.';
           if (avg_huns in [0..9])then 
             average_time[10] := '0'
           else
            begin
             temp1_huns := avg_huns div 10;
             average_time[10] :=chr(temp1_huns+48)
            end;
           temp1_huns := avg_huns mod 10;
           average_time[11] := chr(temp1_huns+48)
          end;

    { convert integer values for total time to character string }
           if (hours > 999) then
            begin
             temp2_hours := hours div 1000;
             access_time[1] := chr(temp2_hours + 48);
             temp2_hours := (hours mod 1000) div 100;
             access_time[2] := chr(temp2_hours +48);
             temp2_hours := ((hours mod 1000) mod 100) div 10;
             access_time[3] := chr(temp2_hours +48);
             temp2_hours := ((hours mod 1000) mod 100) mod 10;
             access_time[4] := chr(temp2_hours +48)
            end;
           if (hours > 99) then
            begin
             access_time[1] := ' ';
             temp2_hours := (hours div 100);
             access_time[2] := chr(temp2_hours + 48);
             temp2_hours := ((hours mod 100) div 10);
             access_time[3] := chr(temp2_hours + 48);
             temp2_hours := ((hours mod 100) mod 10);
             access_time[4] := chr(temp2_hours + 48)
            end;
           if (hours in [10..99]) then
            begin
             access_time[1] := ' ';
             access_time[2] := ' ';
             temp2_hours := hours div 10;
             access_time[3] := chr(temp2_hours + 48);
             temp2_hours := hours mod 10;
             access_time[4] := chr(temp2_hours + 48)
            end;
           if (hours in [0..9]) then
            begin
             access_time[1] := ' ';
             access_time[2] := ' ';
             access_time[3] := '0';
             access_time[4] := chr(hours +48)
            end;
           access_time[5] := ':';
           if (minutes in [0..9])then 
             access_time[6] := '0'
           else
            begin
             temp2_minutes := minutes div 10;
             access_time[6] :=chr(temp2_minutes+48)
            end;
           temp2_minutes := minutes mod 10;
           access_time[7] := chr(temp2_minutes+48);
           access_time[8] := ':';
           if (seconds in [0..9])then 
             access_time[9] := '0'
           else
            begin
             temp2_seconds := seconds div 10;
             access_time[9] :=chr(temp2_seconds+48)
            end;
           temp2_seconds := seconds mod 10;
           access_time[10] := chr(temp2_seconds+48);
           access_time[11] := '.';
           if (huns in [0..9])then 
             access_time[12] := '0'
           else
            begin
             temp2_huns := huns div 10;
             access_time[12] :=chr(temp2_huns+48)
            end;
           temp2_huns := huns mod 10;
           access_time[13] := chr(temp2_huns+48);
end;

function total_time(already,curr:string):string;

{ This function converts the the character values for time to integers using
  the VAX routines READV and WRITEV. }

var
   hours,min,second,hun,number_holder : integer;
   convert_time,convert_already,convert_curr : integer;
   substring : varying[2] of char;
   total,temp1,temp2 : string;
begin
  total := '';	
  if (length(already) > 0) then 
    begin
      substring := substr(already,1,2);
      readv(substring,number_holder);
      convert_already := number_holder*60;
      substring := substr(already,4,2);
      readv(substring,number_holder);
      convert_already := (convert_already + number_holder)*60;
      substring := substr(already,7,2);
      readv(substring,number_holder);
      convert_already := (convert_already + number_holder)*100;
      substring := substr(already,10,2);
      readv(substring,number_holder);
      convert_already := (convert_already + number_holder);
    end;
  if (length(curr) > 0) then 
    begin
      substring := substr(curr,1,2);
      readv(substring,number_holder);
      convert_curr := number_holder*60;
      substring := substr(curr,4,2);
      readv(substring,number_holder);
      convert_curr := (convert_curr + number_holder)*60;
      substring := substr(curr,7,2);
      readv(substring,number_holder);
      convert_curr := (convert_curr + number_holder)*100;
      substring := substr(curr,10,2);
      readv(substring,number_holder);
      convert_curr := (convert_curr + number_holder);
    end;
  convert_time := convert_already + convert_curr;
  hun := (convert_time mod 100);
  second := (convert_time div 100);
  min := (second div 60);
  hours := (min div 60);
  second := second - (min*60);
  min  := min - (hours*60);

  if (hours in [0..9]) then writev(temp1,'0',hours:1,':')
  else writev(temp1,hours:2,':');

  if (min in [0..9]) then writev(temp2,temp1,'0',min:1,':')
  else writev(temp2,temp1,min:2,':');

  if (second in [0..9]) then writev(temp1,temp2,'0',second:1,'.')
  else writev(temp1,temp2,second:2,'.');

  if (hun in [0..9]) then writev(total,temp1,'0',hun:1)
  else writev(total,temp1,hun:2);
  
  total_time := total;
end;

procedure insert(var root: user;current_in : user;sort_by:sort_type);

{ This procedure generates the link list for the four sort types
  (name, date_used, length_used and number_of_times). }

var 
  last,temp,current : user;
  found : boolean;
begin
  current := current_in;
  last := root;
  temp := root;
  found := false;

  while (not found) and (temp <> nil) do
    begin
      if (current^.name > temp^.name) then
        begin
	  last := temp;
	  temp := temp^.next;
	end	
      else
	found := true;
      if (temp = nil) then found := true;
    end;

  if (temp <> nil) then
    if (current^.name = temp^.name) then 
      begin
        temp^.length_used := total_time(temp^.length_used,current^.length_used);
        temp^.date_used := current^.date_used;
	temp^.number_of_uses := temp^.number_of_uses + 1;
      end
    else
      if (temp = root) then
	begin
	  root := current;
	  root^.next := temp;
	  temp^.last := root;
          root^.last := nil;
	end
      else
        begin
	  last^.next := current;
          current^.last := last;
	  current^.next:= temp;
          temp^.last := current;
	end
  else
    begin
      last^.next := current;
      current^.last := last;
      current^.next := temp;
    end;
end;

procedure insert_name(var root : user;current_in:user);

{ This procedure calls the procedure to generate the link list for
  the sort type name. }

var current : user;
begin
  current := current_in;
  sort_by := name;
  if (root = nil) then
    root := current
  else
    insert(root,current,sort_by);
end;

procedure insert_time(var root : user;current_in:user);

{ This procedure sorts each line by the field length_used and calls the
  procedure to generate the link list for sort type length_used. }

var current,temp,last : user;
    found : boolean;
begin
  current := current_in;
  sort_by := length_used;
  if (root = nil) then
    begin
      root := current;
      current := current^.next;
    end;
  while (current <> nil) do
    begin
      last := root;
      temp := root;
      found := false;
      while (not found) and (temp <> nil) do
	begin
	  if (current^.length_used > temp^.length_used) then
            begin
	      last := temp;
	      temp := temp^.length_used_next;
	    end	
	  else
	    found := true;
	  if (temp = nil) then found := true;
	end;

      if (temp <> nil) then
        if (current^.name = temp^.name) then 
          begin
            temp^.length_used := total_time(temp^.length_used,
                                            current^.length_used);
            temp^.date_used := current^.date_used;
	    temp^.number_of_uses := temp^.number_of_uses + 1;
          end
        else
	  if (temp = root) then
	    begin
	      root := current;
	      root^.length_used_next := temp;
	      temp^.length_used_last := root;
              root^.length_used_last := nil;
	    end
	  else
	    begin
	      last^.length_used_next := current;
	      current^.length_used_last := last;
              current^.length_used_next := temp;
              temp^.length_used_last := current;
	    end
      else
	begin
          last^.length_used_next := current;
	  current^.length_used_last := last;
          current^.length_used_next := temp;
         end;
      current := current^.next;
    end;{while root <> nil}
end;

procedure insert_number_of_uses(var root : user;current_in:user);

{ This procedure sorts each line by the field number_of_times and calls 
  the procedure to generate the link list for the sort type 
  number_of_times. }

var current,temp,last : user;
    found : boolean;
begin
  current := current_in;
  sort_by := number_of_times;
  if (root = nil) then
    begin
      root := current;
      current := current^.next;
    end;	
  while (current <> nil) do
    begin
      last := root;
      temp := root;
      found := false;   
      while (not found) and (temp <> nil) do
	begin
	  if (current^.number_of_uses > temp^.number_of_uses) then
            begin
	      last := temp;
	      temp := temp^.number_next;
	    end	
	  else
	    found := true;
	  if (temp = nil) then found := true;
	end;
      if (temp <> nil) then
        if (current^.name = temp^.name) then 
          begin
	    temp^.length_used := total_time(temp^.length_used,
                                            current^.length_used);
            temp^.date_used := current^.date_used;
	    temp^.number_of_uses := temp^.number_of_uses + 1;
          end
        else
	  if (temp = root) then
	    begin
	      root := current;
	      root^.number_next := temp;
	      temp^.number_last := root;
              root^.number_last := nil;
	    end
	  else
	    begin
	      last^.number_next := current;
	      current^.number_last := last;
              current^.number_next := temp;
              temp^.number_last := current;
	    end
    else
      begin
        last^.number_next := current;
        current^.number_last := last;
        current^.number_next := temp;
      end;
      current := current^.next;
    end;{while nroot <> nil}
end;

procedure insert_date(var root : user;current_in:user);

{ This procedure sorts each line by the field date_used and calls 
  the procedure to generate the link list for the sort type 
  date_used. }

var current,temp,last : user;
    found : boolean;
begin
  current := current_in;
  sort_by := date_used;
  if (root = nil) then
    begin
      root := current;
      current := current^.next;
    end;
  while (current <> nil) do
    begin
      last := root;
      temp := root;
      found := false; 
      while (not found) and (temp <> nil) do
	begin
	  if (current^.date_used > temp^.date_used) then
	    begin
	      last := temp;
	      temp := temp^.date_used_next;
	    end	
	  else
	    found := true;
	  if (temp = nil) then found := true;
	end;
      if (temp <> nil) then
        if (current^.name = temp^.name) then 
          begin
            temp^.length_used := total_time(temp^.length_used,
                                            current^.length_used);
            temp^.date_used := current^.date_used;
	    temp^.number_of_uses := temp^.number_of_uses + 1;
          end
        else
	  if (temp = root) then
	    begin
	      root := current;
	      root^.date_used_next := temp;
	      temp^.date_used_last := root;
              root^.date_used_last := nil;
	    end
	  else
	    begin
	      last^.date_used_next := current;
              current^.date_used_last := last;
	      current^.date_used_next := temp;
              temp^.date_used_last := current;
	    end
      else
        begin
          last^.date_used_next := current;
	  current^.date_used_last := last;
          current^.date_used_next := temp;
        end;
      current := current^.next;
    end;{while nroot <> nil}
end;


procedure process_report(var inline : string1; var histogram : numb;
                             var i : integer);
{ This procedure parses each line in the BB$TRACKER.LOG file. }

var 
    line : string1;
    temp,current : user;
    start_at,end_at,line_length : integer;
    start_time,finish_time: string;
    start,finish : integer;
begin
  line := inline;
  line_length := length(line);
  if (line[1] <> '') then
    begin
      node_maker(current);
      with current^ do  
        begin
          if (line[2] = '-') then
            begin
              date_used := '0' + substr(line,1,10);
              start_time := substr(line,12,8);
              finish_time := substr(line,24,8);
              length_used := substr(line,36,11);
              start_at := index(line,'[')+1;
              name := substr(line,start_at,index(line,']')
                                                -start_at);
            end
          else
            begin
              date_used := substr(line,1,11);
              start_time := substr(line,13,8);
              finish_time := substr(line,25,8);
              length_used := substr(line,37,11);
              start_at := index(line,'[')+1;
              name := substr(line,start_at,index(line,']')
                                                -start_at);
            end;
	end;{with}

       { extract the start and finish time from each line }
          if (line[2] = '-') then
            begin
              start_time := substr(line,12,8);
              finish_time := substr(line,24,8)
            end
           else
            begin
              start_time := substr(line,13,8);
              finish_time := substr(line,25,8)
            end;
        start_time := substr(start_time,1,2);
        finish_time := substr(finish_time,1,2);

        { convert start and finish time to integers }
        start := ((ord(start_time[1])-ord('0'))*10)+(ord(start_time[2])-ord('0'));
        finish := ((ord(finish_time[1])-ord('0'))*10)+(ord(finish_time[2])-ord('0'));
        for i := start to finish do

        { This loop calculates the number of accesses each hour.  The
          array histogram will be passed to the procedure histogram_time
          to produce a 24 hour access log. }

         begin
           histogram[i] := histogram[i] + 1;
         end;
	insert_name(name_root,current);
        temp := name_root;
        while (temp <> nil) do
          temp := temp^.next;
    end;
end;

procedure histogram_time(var first,second :string; histogram : numb;
                              i,access_count : integer; access_time,
                               average_time: string);

{ This procedure generates a 24 hour log of how many times the 
  Bulletin Board was accessed each hour. }

VAR
   temp1: varying[80] of char;
   temp : integer;
begin
  writeln(bbreport,'   Number of accesses this time period: ',
                      count:4);
  if (access_count <> 0) then
  writeln(bbreport,'   Number of users this time period:  ',
                           access_count:4);
  if (access_time <> '0000:00:00.00') then
  writeln(bbreport,'   Total time accessed:  ',access_time);
  if (average_time <> '00:00:00.00') then
  writeln(bbreport,'   Average time accessed:  ',average_time);
  writeln(bbreport,'   Number of times accessed each hour:  ');
  writeln(bbreport,' ');
  writeln(bbreport,' ');
  writeln(bbreport,'            Time            ','      Number of');
  writeln(bbreport,'       Start - Finish       ','     Times Used');
  writeln(bbreport,'       --------------       ','     ----------');
  first := '00:00';
  for i := 0 to 23 do
    begin
      temp1 := first;
      if first[2] = '9' then 
        begin
          first[2] := '0';
          temp := ord(first[1]) + 1;
          first[1] := chr(temp)
      end
      else
        begin
          temp := ord(first[2]) + 1;
          first[2] := chr(temp)
        end;
      second :=first;
      first := temp1;
      if second = '24:00' then
        second := '00:00';
      writeln(bbreport,'       ',first,'    ',second,'        ',
              histogram[i]);
      first := second
   end;
   page(bbreport)
end;


function uc(ch:string):string;

{ This function changes lower case alpha to upper case. }

begin
  for i:=1 to length(ch) do 
    if ch[i] in ['a'..'z'] then ch[i]:=chr(ord(ch[i])-32);
  uc:=ch;
end;

function change(str : string;
                index : integer) : integer;

{ This function changes a numeric string into an integer. }

var i : integer;
    number : integer;

begin
  number := ord(str[1]) - 48;
  for i := 2 to index do
    number := number * 10 + (ord(str[i]) - 48);
  change := number;
end;

procedure date_ok(var ending : c11;var ok: boolean);

{ This function will take the date entered and check it
 extensively for format and correctness.  These checks are necessary
 to insure that no run time errors occur when passing the beginning
 and ending date to the procedure that generates the monthly report
 title page. }

type characters = set of char;
var
  day,month,year : string;
  letters,numbers : characters;
  marker,check : integer;
begin
  letters := ['a'..'z','A'..'Z'];
  numbers := ['0'..'9'];
  ok := true;
  if ((substr(ending,3,1) = '-') and (substr(ending,7,1) = '-')) then
    begin      {hyphens are in right places}
      day := substr(ending,1,2);
      if ((day[1] in numbers) and (day[2] in numbers)) then
        begin   {day is numeric}
          month := substr(ending,4,3);
          if ((month[1] in letters) and (month[2] in letters)
               and (month[3] in letters)) then
            begin    {month is alphabetic}
              year := substr(ending,8,4);
              if ((year[1] in numbers) and (year[2] in numbers) and
                  (year[3] in numbers) and (year[4] in numbers)) then
                begin   {year is numeric}
                  check := change(year,4);
                  if ((check = 1987) or (check = 1988)) then
                    begin     {year is valid}
                      month := uc(month);
                      if ((month = 'SEP') or (month = 'APR')
                            or (month = 'JUN') or (month = 'NOV')) then
                        marker := 1
                      else if ((month = 'JAN') or (month = 'MAR')
                                or (month = 'JUL') or (month = 'MAY')
                                or (month = 'AUG') or (month = 'OCT')
                                or (month = 'DEC')) then
                             marker := 2
                           else if (month = 'FEB') then
                                  marker := 3
                                else marker := 4; 
                      case (marker) of
                        1: begin  {30 days in these months}
                             check := change(day,2);
                             if ((check > 30) or (check < 0)) then
                               begin
                                 ok := false;
                                 write('Invalid day: ',day,
                                         ' for: ',month,'.');
                               end;
                           end;
                        2: begin  {31 days in these months}
                             check := change(day,2);
                             if ((check > 31) or (check < 0)) then
                               begin
                                 ok := false;
                                 write('Invalid day: ',day,
                                         'for: ',month,'.');
                               end;
                           end;
                        3: begin
                             if ((check mod 4) = 0) then
                               begin   {leap year}
                                 check := change(day,2);
                                 if ((check > 29) or (check < 0)) then
                                   begin
                                     ok := false;
                                     write('Invalid day: ',day,
                                             'for: ',month,' in a leap year.');
                                   end;
                               end
                             else
                               begin     {not a leap year}
                                 check := change(day,2);
                                 if ((check > 28) or (check < 0)) then
                                   begin
                                     ok := false;
                                     write('Invalid day: ',day,
                                             'for: ',month,'.');
                                   end;
                               end;
                           end;
                        4: begin
                             ok := false;
                             write('Invalid month: ',month,' specified.');
                           end;
                      end;   {case}               
                    end
                  else 
                    begin
                      write('Invalid year: ',year,' specified.');
                      ok := false;
                    end;
                end
              else 
                begin
                  write('Year: ',year,' must be numeric.');
                  ok := false;
                end;
             end
           else 
             begin
               write('Month: ',month,' must be alphabetic.');
               ok := false;
             end;
         end
      else 
        begin
          write('Day: ',day,' must be numeric.');  
          ok := false;
        end;
    end
  else 
    begin
      write('Hyphens misplaced in date string: ',ending,'.');
      ok := false;
    end;
  if not(ok) then writeln('  Please try again.');
end; 

procedure write_first_page;

{ This procedure writes the title page for the monthly report. }

begin
  writeln(bbreport,'                 BULLETIN BOARD MONTHLY USAGE REPORT');
  writeln;
  writeln;
  writeln;
  writeln(bbreport,'           This report covers ',start_date,
                          ' through ',end_date);
  writeln(bbreport,' ');
  writeln(bbreport,' ');
  writeln;
end;

begin   {main}

{ This program generates the Bulletin Board monthly report. }

  { initialize gobal variables }
  count := 0;
  for i := 0 to 23 do
    histogram[i] := 0;
  access_count := 0;
  access_time := '0000:00:00.00';
  average_time := '00:00:00.00';
  ok := false;

  repeat
    writeln('Enter beginning date for report in form DD-MMM-YYYY:');
    readln(start_date);
    date_ok(start_date,ok);
  until (ok = true);
  ok := false;
  repeat
    writeln('Enter ending date for report in form DD-MMM-YYYY:');
    readln(end_date);
    date_ok(end_date,ok);
  until (ok = true);
  starter; 
  repeat
    readln(bblog,inline);
    process_report(inline,histogram,i);
    count := count + 1;
  until (eof(bblog));
  insert_time(time_root,name_root);
  insert_date(date_root,name_root);
  insert_number_of_uses(number_root,name_root);
  calculate(name_root,access_count,access_time,average_time);
  write_first_page;
  histogram_time(first,second,histogram,i,access_count,access_time,
                 average_time);
  sort_by := name;
  print_report(name_root,sort_by);
  sort_by := length_used;
  print_report(time_root,sort_by);
  sort_by := date_used;
  print_report(date_root,sort_by);
  sort_by := number_of_times;
  print_report(number_root,sort_by);
  close(bblog); 
  close(bbreport);
end.

