{$T-}
program ACCOUNTER;
const NL=CHR(10);
      CR=CHR(13);
      FF=CHR(12);
      RAD50HEL=13012;  {RAD50 OF 'HEL'}
      RAD50BYE=4205;   {RAD50 OF 'BYE'}
      RAD50TON=32614;  {RAD50 OF 'TON'}
      RAD50UIC=-31573; {RAD50 OF 'UIC'}
type
PACKET = record
  TSK1    : INTEGER;
  TASK    : INTEGER;
  PROG    : CHAR;
  GROUP   : CHAR;
  DEVICE  : array[0..1] of CHAR;
  UNIT    : INTEGER;
  YEAR    : INTEGER;
  MONTH   : INTEGER;
  DAY     : INTEGER;
  HOUR    : INTEGER;
  MINUTE  : INTEGER;
  SECOND  : INTEGER;
  OLDPROG : CHAR;
  OLDGROUP: CHAR;
  FILL1   : INTEGER;
end;
STATE = (ON,OFF,CHANGE,BOOT);
TIME = record
  HOUR    : INTEGER;
  MINUTE  : INTEGER;
  SECOND  : INTEGER;
end;
NODEPTR = @NODE;
NODE = record
  FWRD    : NODEPTR;
  BACK    : NODEPTR;
  GROUP   : CHAR;
  PROG    : CHAR;
  TOTAL   : TIME;
  STATUS  : STATE;
  CURTIME : TIME;
  NUMTERM : INTEGER;
end;
SEARCHKEY = (NONE,ADD,FOUND,END);
PRINTKEY = (YES,NO);
var
  PACKETFILE : file of PACKET;
  INREC : PACKET;
  LIST  : TEXT;
  LISTHEAD : NODEPTR;
  CURNODE  : NODEPTR;
  THISTIME : TIME;
  THISSTATUS : STATE;
  NEWNODE : NODEPTR;
  SEARCH : SEARCHKEY;
  PACKETGROUP : INTEGER;
  PACKETPROG : INTEGER;
  PRINT : PRINTKEY;
  CH : CHAR;
  LIST_DEVICE : array [0..3] of CHAR;

  function OCTDEC( OCTVAL:INTEGER ) : INTEGER;
    var DECIMAL    : INTEGER;
        REMAINDER  : INTEGER;
        NEWVAL     : INTEGER;
    begin
      DECIMAL := OCTVAL mod 8;
      REMAINDER := OCTVAL div 8;
      if REMAINDER#0 then
       begin
        NEWVAL := OCTDEC(REMAINDER);
        NEWVAL := 10 * NEWVAL;
        DECIMAL := DECIMAL + NEWVAL
       end;
      OCTDEC := DECIMAL;
    end;
  procedure WRITEUNIT( UNIT:INTEGER );
    var DEC : INTEGER;
    begin
      DEC := OCTDEC(UNIT);
      if DEC<10 then WRITE(LIST,'0');
      WRITE(LIST,DEC,':')
    end;
  procedure WRITEUIC( GROUP:INTEGER ; PROG:INTEGER );
    var DEC:INTEGER;
    begin
      DEC := OCTDEC(GROUP);
      WRITE(LIST,'[',DEC:3,',');
      DEC := OCTDEC(PROG);
      WRITE(LIST,DEC:3,']')
    end;
  procedure ADDTIME( var LAST:TIME;var NOW:TIME;var SUM:TIME;FACTOR:INTEGER );
  var DELTA : TIME;
  begin
    DELTA.HOUR := NOW.HOUR - LAST.HOUR;
    if DELTA.HOUR < 0 then DELTA.HOUR := DELTA.HOUR + 24;
    DELTA.MINUTE := NOW.MINUTE - LAST.MINUTE;
    DELTA.SECOND := NOW.SECOND - LAST.SECOND;
    if DELTA.SECOND < 0 then begin
      DELTA.SECOND := DELTA.SECOND + 60;
      DELTA.MINUTE := DELTA.MINUTE - 1
    end;
    if DELTA.MINUTE < 0 then begin
      DELTA.MINUTE := DELTA.MINUTE + 60;
      DELTA.HOUR := DELTA.HOUR - 1
    end;
    DELTA.HOUR := FACTOR * DELTA.HOUR;
    DELTA.MINUTE := FACTOR * DELTA.MINUTE;
    DELTA.SECOND := FACTOR * DELTA.SECOND;
    SUM.HOUR := SUM.HOUR + DELTA.HOUR;
    SUM.MINUTE := SUM.MINUTE + DELTA.MINUTE;
    SUM.SECOND := SUM.SECOND + DELTA.SECOND;
    SUM.MINUTE := SUM.MINUTE +  SUM.SECOND div 60;
    SUM.SECOND := SUM.SECOND mod 60;
    SUM.HOUR := SUM.HOUR + SUM.MINUTE div 60;
    SUM.MINUTE := SUM.MINUTE mod 60;
  end;
  procedure INSERT( LOC:NODEPTR; NEW:NODEPTR );
  begin
    NEW@.FWRD := LOC;       {Forward pointer of NEW points to LOC}
    NEW@.BACK := LOC@.BACK; {Backward pointer of NEW points to LOC's backward}
    LOC@.BACK := NEW;       {Backward pointer of LOC points to NEW}
    NEW@.BACK@.FWRD := NEW; {Forward pointer of node preceeding NEW points to NEW}
  end;

  procedure SETTIME( var SOURCE:TIME; var DEST:TIME );
  begin
    DEST.HOUR := SOURCE.HOUR;
    DEST.MINUTE := SOURCE.MINUTE;
    DEST.SECOND := SOURCE.SECOND
  end;
  procedure SUMMARIZE;
  begin
      CURNODE := LISTHEAD@.FWRD;
      SEARCH := NONE;
      while (CURNODE#LISTHEAD) & (PACKETGROUP>ORD(CURNODE@.GROUP)) do
        CURNODE := CURNODE@.FWRD;
      if CURNODE=LISTHEAD then SEARCH := END
        else if PACKETGROUP=ORD(CURNODE@.GROUP) then
          begin
          while (CURNODE#LISTHEAD) & (PACKETGROUP=ORD(CURNODE@.GROUP))
                & (PACKETPROG>ORD(CURNODE@.PROG)) do
            CURNODE := CURNODE@.FWRD;
          if CURNODE=LISTHEAD then SEARCH := END
            else if PACKETGROUP#ORD(CURNODE@.GROUP) then SEARCH := ADD
            else if PACKETPROG#ORD(CURNODE@.PROG) then SEARCH := ADD
            else SEARCH := FOUND
          end
        else SEARCH := ADD;

      case SEARCH of
        NONE:WRITE(OUT,NL,CR,'Bad error....');
        ADD,END:if THISSTATUS=OFF then
          begin if PRINT=YES then WRITE(LIST,NL,CR,'No logon...') end
            else begin
              NEW(NEWNODE);  INSERT( CURNODE , NEWNODE );
              with NEWNODE@ do begin
                TOTAL.HOUR := 0;
                TOTAL.MINUTE := 0;
                TOTAL.SECOND := 0;
                STATUS := ON;
                SETTIME( THISTIME ,   CURTIME );
                GROUP := INREC.GROUP;
                PROG := INREC.PROG;
                NUMTERM := 1
              end
            end;
        FOUND:if (CURNODE@.STATUS=OFF) & (THISSTATUS=OFF) then
          with CURNODE@ do begin
            if PRINT=YES then WRITE(LIST,NL,CR,'No logon....') end
            else if STATUS=OFF then
              begin
                STATUS:=ON;
                NUMTERM := 1;
                SETTIME( THISTIME , CURTIME )
              end
            else if THISSTATUS=ON then
              begin
                if PRINT=YES then WRITE(LIST,NL,CR,'Double logon...');
                NUMTERM := NUMTERM + 1
              end
            else begin
              ADDTIME(CURTIME,THISTIME,TOTAL,NUMTERM );
              SETTIME( CURTIME , THISTIME );
              NUMTERM := NUMTERM - 1;
              if NUMTERM=0 then STATUS := OFF
            end
          end
  end;

begin
  RESET(PACKETFILE,'[1,1]LOGGER.DAT ');
  LIST_DEVICE[2] := ':'; LIST_DEVICE[3] := ' ';
  WRITE(OUT,NL,CR,'Do you want to print logon summary? (Y/N):');
  BREAK(OUT);  READ(INP,CH);
  WRITE(OUT,NL,CR,'Indicate listing device..  "TI" or "CL">');
  BREAK(OUT);  READ(INP,LIST_DEVICE[0],LIST_DEVICE[1]);
  WRITE(OUT,NL,CR,'Listing device is ',LIST_DEVICE); BREAK(OUT);
  if (CH='Y') ! (CH='y') then PRINT := YES else PRINT := NO;
  if PRINT=YES then REWRITE(LIST,LIST_DEVICE);
  NEW(LISTHEAD);
  with LISTHEAD@ do begin
    FWRD := LISTHEAD;
    BACK := LISTHEAD;
    GROUP := CHR(0);
    PROG := CHR(0);
  end;
  while ~EOF(PACKETFILE) do
    begin
      INREC := PACKETFILE@; GET(PACKETFILE);
      if INREC.TASK=RAD50HEL then THISSTATUS := ON;
      if INREC.TASK=RAD50BYE then THISSTATUS := OFF;
      if INREC.TASK=RAD50TON then THISSTATUS := BOOT;
      if INREC.TASK=RAD50UIC then THISSTATUS := CHANGE;
      PACKETGROUP := ORD(INREC.GROUP);
      PACKETPROG := ORD(INREC.PROG);
      THISTIME.HOUR := INREC.HOUR;
      THISTIME.MINUTE := INREC.MINUTE;
      THISTIME.SECOND := INREC.SECOND;
      if PRINT=YES then begin
        WRITE(LIST,NL,CR,INREC.DEVICE);
        WRITEUNIT( INREC.UNIT );
        WRITEUIC( PACKETGROUP , PACKETPROG );
        if THISSTATUS=ON then WRITE(LIST,' Logged on at   ');
        if THISSTATUS=OFF then WRITE(LIST,' Logged off at  ');
        if THISSTATUS=BOOT then WRITE(LIST,' Booted at      ');
        if THISSTATUS=CHANGE then WRITE(LIST,' Changed UIC at ');
        WRITE(LIST,INREC.HOUR:2,':',INREC.MINUTE:2,':');
        WRITE(LIST,INREC.SECOND:2,' on ',INREC.MONTH:2);
        WRITE(LIST,'/',INREC.DAY:2,'/',INREC.YEAR:2);
      end;
      if THISSTATUS=BOOT then begin
        CURNODE := LISTHEAD@.FWRD;
        while CURNODE#LISTHEAD do begin
          CURNODE@.STATUS := OFF;
          ADDTIME( CURNODE@.CURTIME,THISTIME,CURNODE@.TOTAL,CURNODE@.NUMTERM);
          CURNODE@.NUMTERM := 0;
          CURNODE := CURNODE@.FWRD
        end;
        THISSTATUS := ON
      end;
      if THISSTATUS=CHANGE then begin
        THISSTATUS := ON;
        SUMMARIZE;
        THISSTATUS := OFF;
        PACKETGROUP := ORD(INREC.OLDGROUP);
        PACKETPROG := ORD(INREC.OLDPROG);
        INREC.GROUP := INREC.OLDGROUP;
        INREC.PROG := INREC.OLDPROG
      end;
      SUMMARIZE;
    end;

  REWRITE(LIST,LIST_DEVICE);
  CURNODE := LISTHEAD@.FWRD;
  while CURNODE#LISTHEAD do
    begin
      WRITE(LIST,NL,CR,'Logon time for ');
      WRITEUIC( ORD(CURNODE@.GROUP) , ORD(CURNODE@.PROG) );
      WRITE(LIST,' is ',CURNODE@.TOTAL.HOUR:6,':');
      WRITE(LIST,CURNODE@.TOTAL.MINUTE:2,':');
      WRITE(LIST,CURNODE@.TOTAL.SECOND:2,', and status is ');
      if CURNODE@.STATUS=ON then WRITE(LIST,'logged on.')
        else WRITE(LIST,'logged off.');
      CURNODE := CURNODE@.FWRD
    end;
  BREAK(LIST);
  WRITE(OUT,NL,CR,'I AM DONE....',NL,CR);
  BREAK(OUT)
end.
