%begin                                   {*Swoppable ACCOUNTANT process*}
   %external %routine %spec OPEN DA   (%integer chan, flags, %string(31) file)
   %external %routine %spec READ DA   (%integer chan, block num, %name buffer)
   %external %routine %spec WRITE DA  (%integer chan, block num, %name buffer)
   %external %routine %spec CLOSE DA  (%integer chan)
   %external %routine %spec UNPACK    (%record(file fm)%name f,
                                       %string(*)%name s)
   %external %string(72) %fn %spec ITOS (%integer N, places)
   %const %string(31) user account file  = "ACCNT:USERFILE"
   %const %integer file = 1              {DA channel for account file}

   {*System interface: supervisor call numbers*}

   %const %integer Become Accountant = 128,
                                Poff = 16,
                                 Pon = 115,
                           Tell user = 120

   %const %record(*)%name null == 0

   %routine report (%string(63) s)
      %record(parm fm) P
      P_text = s
      P_dsno = 0                         {OPER}
      SVC (Tell user, P)
   %end

   {Format of user account block}

   %const %integer resources = 6         {Room for expansion}
   %record %format account fm (          %c
           %integer unit, owner,         {Packed username}
           %string(31) name,             {Real name}
           %integer when,                {Time of last login/logout}
           (%integer CPU, logons, connect  %or
            %integer %array Usage(1:resources)),
           %integer %array Quota(1:resources),
           %string(15) Aegis,            {Dept/charge code for user}
           %string(63) Address)          {Or any other useful information}

   {Hash table entry indexing account file}
   %const %integer last = 4095           {Size of hash table}

   %record %format user fm  (            %c
           %integer index,               {Pointer to his account block}
                    Packed username)

   %record %format block fm (            %c
           %record(account fm) account   %or
           %record(user fm)%array u(0:63))


   {********** Mapping of virtual arrays onto account file ***********}

   %record(block fm) %map block (%integer N)
      %own %record(block fm) buffer
      %own %integer last block = -1, actual
      %on * %start
         Report (event_message)
         last block = -1
         %result == buffer
      %finish
      %if N # last block %start
         Write DA(file, last block, buffer) %if last block >= 0
         Read DA (file, N, buffer) %if N >= 0
         last block = N
      %finish
      %result == buffer
   %end

   %record(user fm) %map user (%integer N)
      !User list is 64 user-blocks, starting at block zero
      %integer block no = N>>6, offset = N&63
      %result == block(block no)_u(offset)
   %end

   %record(account fm) %map account (%integer N)
      %result == block(N+63)_account
   %end

   %integer %fn find (%integer who, %integer %name exists)
      %integer probe = rem(who, last)    {Primitive hash function}
      %record(user fm) %name u
      exists = 0
      %cycle
         u == user(probe)
         %if u_packed username = 0 %start            {Empty slot}
            %result = probe
         %finish
         exists = 1 %and %result = probe %if u_packed username = who
         probe = rem(last, probe+1)
      %repeat
   %end

   %record(account fm) %map find account(%integer unit, owner)
      %record(file fm) f
      %string(63)      s
      %record(account fm)%name A
      %integer         exists
      %integer         index = find(owner, exists)

      %if exists = 0 %start
         f_unit = unit;  f_owner = owner;  f_N1 = 0;  f_N2 = 0
         Unpack (f, s)
         Report ("Who is ".s."??")
         %result == null
      %finish

      A == account(user(index)_index)
      %if A_owner # owner %start
         Report ("Corrupt file!")
         %result == null
      %finish
      %result == A
   %end

   %routine Logon (%integer unit, owner, when)
      %record(account fm)%name A == find account(unit, owner)
      A_when = when %if A ## null
   %end

   %routine Logoff(%integer unit, owner, when, CPU used)
      %record(account fm)%name A == find account(unit, owner)
      %if A ## null %start
         A_CPU     = A_CPU     + CPU used
         A_Logons  = A_Logons  + 1
         A_Connect = A_Connect + (when-A_when)
         Block(-1) = 0
      %finish
   %end

   %record(parm fm) P, Q
   %switch A(1:4)
   %label REPLY

   SVC (Become Accountant, P)
   Open DA (file, 1, user account file)

   %cycle
      P_sact = 0;  SVC(Poff,P)
      %unless 0 < P_dact <= 4 %start
         Report ("DACT".itos(P_dact,1))
A(3):    P_p1 = -1
REPLY:   P_dsno = P_ssno
         P_dact = P_sact
         SVC (Pon, P)
         %continue
      %finish
      -> A(P_dact)

A(1): {LOGON P1=Unit, P2=Owner, P3=Time of day}
      Logon (P_p1,    P_p2,     P_p3)
      -> REPLY

A(2): {LOGOUT P1=Unit, P2=Owner, P3=Time of day, P4=CPU used}
      Logoff (P_p1,    P_p2,     P_p3,           P_p4)
      -> REPLY

A(4): {Relay message}
      Q = P;  Q_dsno <- P_p6
      SVC (Tell user, P)
      -> REPLY
   %repeat

%end %of %program
