{ News utilities }

%include "inc:util.imp"
%include "news_s:news.inc"

%external %string (80) Copyright %alias "NEWS_INDEX_(C)_FDC_85" = -
   "Copyright (C) 1985 Frank D. Cringle"

%external %integer Last Message
%external %integer File Length
%external %integer Home Host
%external %integer News Host
%external %string (12) News Server = "BRAVO::"

%own %record (Index fm) %array Index(0:Entries - 1)
%own %integer Index Position
%own %integer Start of Message
%own %integer News Mode = -1

{---------- GET INDEX ----------}
%routine Get Index(%integer pos)    { Get index block which starts at pos }
   %integer i

   %return %if pos = Index Position
   Select Host(News Host)
   Set Input(pos) %unless Pos = 0 %and Index Position = -1
   %for i = 0, 1, Blocksize - 1 %cycle
      Read Symbol(byteinteger(addr(Index) + i))
   %repeat
   Index Position = pos
%end

{---------- PREV INDEX ----------}
%predicate Prev Index(%integer %name i)
   %integer p

   %cycle
      i = i - 1
      %if i < 0 %start
         %false %if Index Position = 0
         p = Index Position
         Get Index(0)
         %while 0 < Index(Entries - 1)_Position < p %cycle
            Get Index(Index(Entries - 1)_Position)
         %repeat
         i = Entries - 2
      %finish
      %true %if Index(i)_Length # 0
      %false %if Index(i)_Position = 0
   %repeat
%end

{---------- NEXT INDEX ----------}
%predicate Next Index(%integer %name i)

   %cycle
      i = i + 1
      %if i > Entries - 2 %start
         %false %if Index(Entries - 1)_Position = 0
         Get Index(Index(Entries - 1)_Position)
         i = 0
      %finish
      %true %if Index(i)_Length # 0
      %false %if Index(i)_Position = 0
   %repeat
%end

{---------- PLAUSIBLE ----------}
%predicate Plausible
{ Check that the first index block of the file is plausible }
   %integer i, n

   Get Index(0)
   n = 0
   %for i = 0, 1, Entries - 1 %cycle      { Check used entries }
      %exit %if Index(i)_Position = 0
      %false %if (Index(i)_Number <= n) %or (Index(i)_Position < Blocksize)
      n = Index(i)_Number
   %repeat
   %while i < Entries - 1 %cycle          { Check any remaining unused entries }
      i = i + 1
      %false %if Index(i)_Position # 0
   %repeat
   %true
%end

{---------- OPEN NEWS FILE ----------}
%external %predicate Open News File(%string(255) name,             { topic } %c
                                    %integer mode)    { 0: read, 1: update }
{ Returns false if file is in use, else true and }
{  sets Last Message and File Length             }

   %integer count, l, t
   %integer First Time = 1
   %constant %integer seconds = 1000

   {OPEN IT}
   %predicate Open It

      %on %event 3 %start
         %false %if event_sub = 3 %and event_extra = '#' { conflicting access }
         Select Host(Home Host)
         %signal %event 3, event_sub, event_extra, event_message
      %finish
   
      %if mode = 0 %start
         Open Input(News Stream, News Directory.name)
         File Length = 0
      %else
         File Length = Open Update(News Stream, News Directory.name)
      %finish
      %true
   %end {Open It}

   %on %event 10 %start
      Select Host(Home Host)
      Select Output(0)
      Newline
      Printstring(Event_Message)
      Newline
      %stop
   %finish

   Select Host(News Host)

   %if %not Open It %start
      count = 10
      %cycle
         t = cputime + 6 * seconds
         %while cputime < t %cycle
         %repeat
         %exit %if Open It
         Count = Count - 1
         %false %if Count < 1
         Select Host(Home Host)
         Select Output(0)
         Newline %if First Time # 0
         First Time = 0
         Print String("The topic ".name." is being used at the moment, ")
         Print String("retrying....")
         Newline
         Select Host(News Host)
      %repeat
   %finish

   News Mode = Mode
   Index Position = -1
   %false %if %not Plausible
   Last Message = Index(Entries - 1)_Number
{--------------- to avoid filestore problem (length isn't right) ------------}
   %if mode # 0 %start
      l = Move to Message(Last Message, 0)
      File Length = Start of Message + l
      File Length = Index Position + Blocksize %if File Length <= Index Position
   %finish
{----------------------------------------------------------------------------}
   %true
%end {Open News File}

{---------- CREATE NEWS FILE ----------}
%external %predicate Create News File(%string(255) name)
{ returns true if ok, else false (file exists) }
{ only operates on home filestore }

   %integer i
   %string(255) fn

   fn = News Directory.name
   %false %if exists(fn)
   open output(News Stream, fn)
   select output(News Stream)
   %for i = 0, 1, Blocksize - 1 %cycle
      print symbol (0)
   %repeat
   close output
   permit(fn, "ffa")
   %true
%end

{---------- MOVE TO MESSAGE ----------}
%external %integer %function Move to Message (%integer %name n, %integer dir)
{ positions News Stream at start of message n (if dir = 0),       }
{ or the predecessor of message (if dir = backwards),             }
{ or the successor of message n (if dir = forwards), and returns  }
{ the length of the message (0 if it doesn't exist)               }

   %integer i

   Select Host(News Host)
   Select Input(News Stream)
   Get Index(0) %if Index(0)_Number > n
   %while 0 < Index(Entries - 2)_Number < n %and -
          Index(Entries - 1)_Position # 0 %cycle
      Get Index(Index(Entries - 1)_Position)
   %repeat
   %for i = 0, 1, Entries - 2 %cycle
      %exit %if Index(i)_Number >= n %or Index(i)_Number = 0
   %repeat
   %if dir = 0 %start
      %result = 0 %if Index(i)_Number # n
      set input(Index(i)_Position)
      Start of Message = Index(i)_Position
      %result = Index(i)_Length
   %else %if dir = backwards %and (Index(i)_Number >= n %or Index(i)_Number = 0)
      %result = 0 %unless Prev Index(i)
   %else %if Index(i)_Number = n %or Index(i)_Length = 0
      %result = 0 %unless Next Index(i)
   %finish
   n = Index(i)_Number
   set input(Index(i)_Position)
   Start of Message = Index(i)_Position
   %result = Index(i)_Length
%end

{---------- LOG MESSAGE ----------}
%external %routine Log Message(%integer length)
{ Update index to account for new message (length bytes long) }
{ which has been added to the end of the file.                }
{ Updates File Length and Last Message.                       }


   %integer i, pos, ipos

   Select Host(News Host)
   Select Output(News Stream)
   Select Input(News Stream)
   Flush Output
   %signal 10, 9, 99, "Message too long" %if length > 16_ffff
   pos = File Length
   File Length = File Length + length
   Last Message = Last Message + 1
   %while Index(Entries - 1)_Position # 0 %cycle
      Get Index (Index(Entries - 1)_Position)
   %repeat
   i = Entries - 1
   %while (i > 0) %and (Index(i - 1)_Position = 0) %cycle
      i = i - 1
   %repeat
   %if i = Entries - 1 %start
      ipos = Index Position
      Index Position = (File Length + Blocksize - 1) & \(Blocksize - 1)
      Index(Entries - 1)_Position = Index Position
      Index(Entries - 1)_Number = Last Message
!
      File Length = Index Position + Blocksize
! Unnecessary because of hacked `flush output'
!     set output(File Length)
!     %while File Length < Index Position + Blocksize %cycle
!        Print Symbol(0)
!        File Length = File Length + 1
!     %repeat
      set output(ipos)
      %for i = 0, 1, Blocksize - 1 %cycle
         Print Symbol(byteinteger(addr(Index) + i))
      %repeat
      flush output
      %for i = 0, 1, Entries - 1 %cycle
         Index(i)_Number = 0
         Index(i)_Length = 0
         Index(i)_Position = 0
      %repeat
      i = 0
   %finish
   Index(i)_Number = Last Message
   Index(Entries-1)_Number = Last Message
   Index(i)_Length = length
   Index(i)_Position = pos
%end

{---------- CANCEL MESSAGE ----------}
%external %routine Cancel Message(%string (255) %name Topic, %integer n)
   %integer i

   %if %not Open News File (Topic, 1) %start
      Select Host (Home Host)
      Select Input (3)
      Close Input
      Select Output (0)
      Newline
      Print String ("It's hopeless. Try to cancel the message later on. ")
      Newline
      %return
   %finish

   Select Host(News Host)
   Select Input(News Stream)
   Get Index(0) %if Index(0)_Number > n
   %while 0 < Index(Entries - 2)_Number < n %cycle
      Get Index(Index(Entries - 1)_Position)
   %repeat
   %for i = 0, 1, Entries - 2 %cycle
      %exit %if Index(i)_Number >= n %or Index(i)_Number = 0
   %repeat
   %return %if Index(i)_Number # n
   Index(i)_Length = 0
   Close News File
%end


{---------- CLOSE NEWS FILE ----------}
%external %routine Close News File
   %integer i

   Select Host(News Host)
   Select Input(News Stream)
   %if News Mode = 0 %start
      News Mode = -1
      Close Input
   %else %if News Mode = 1
      News Mode = -1
      Select Output(News Stream)
      Set Output(Index Position)
      %for i = 0, 1, Blocksize - 1 %cycle
         Print Symbol(byteinteger(addr(Index) + i))
      %repeat
      %if Index Position # 0 %start
         Flush Output
         Get Index(0)
         %if Index(Entries - 1)_Number # Last Message %start
            Index(Entries - 1)_Number = Last Message
            Set Output(0)
            %for i = 0, 1, Blocksize - 1 %cycle
               Print Symbol(byteinteger(addr(Index) + i))
            %repeat
         %finish
      %finish
      Close Update
   %finish
   Select Host(Home Host)
%end
