!
! >>> LOCAL <<<
!
! This module of the IMP RTS handles input/output to objects known
! as "local files". These have some similarity to the old idea of
! a "tank" in that they are read/write stream-like objects residing in
! store, but they have several telling differences.
!
! 1) Tanks were independent of streams, local files are fully integrated.
!    Thus select/print/read etc can all be used as usual.
! 2) Tanks were fixed-size; local files are self-sizing.
! 3) Tanks were numbered; local files are named.
!
! Use of SCB for LOCAL files
!
! _FAB will point to the LOCALFM block for this local file,
! _RAB will point to the SEGMENTFM block currently active (if any)
!
! Revision history:
!
! 1.0.0    antiquity   IAY  creation of package and debug to functionality
! 1.0.1    24-OCT-1984 IAY  close on zero-length output does not update RAB
!                           as none has been created (result was an access
!                           violation as location 0 was used as the RAB).
! 1.0.2    24-OCT-1984 IAY  remove clearing of SIZE field to prevent
!                           deallocation of allocated buffers, as this is now
!                           done in an entirely different way.
! 1.0.3    15-APR-1985 IAY  change to generic I/O system
!
%from IMP %include Formats, Streams, Buffers, HandCall, Depio, Mcode

%record %format Segment Fm ( -
   %record(Buff Fm) Buffer,
   %integer Base Addr, Limit Addr,         {for positioning operations}
   %record(Segment Fm) %name Next, Prev )

%record %format Local Fm ( -
   %byte Readers, Writers,
   %record(Segment Fm) %name Segment List,
   %record(Local Fm)    %name File Chain,
   %record(SCB Fm)     %name SCB,
   %integer Eof Addr,                      {for positioning operations}
   %string(15) File Name )

%own %record(Local Fm) %name Files == 0

!
! > Find File <
!
%record(Local Fm) %map Find File ( %string(15) S )
   %record(Local Fm) %name F == Files
   %while F ## Nil %cycle
      %result == F %if F_File Name = S
      F == F_File Chain
   %repeat
   F == New(F);  F = 0 {no segment list, eof addr=0}
   F_File Chain == Files;  Files == F
   F_File Name = S
   %result == F
%end

!
! >> Step Input Buffer <<
!
! Called when the current input stream has hit the end of its buffer
!
%externalroutine Step Input Buffer  %alias "3L___sib"
   {In:   R1 = addr(SCB)}
   {Out:  R1 : unchanged}
   {      R2 = address of first character position}
   %record(SCB Fm) %name S
   %integer P
   *STR _ 1, S
   %record(Local   Fm) %name F == S_Fab
   %record(Segment Fm) %name R == S_Rab
   %if R == Nil %start {not any active as yet}
      R == F_Segment List
   %else
      R == R_Next
   %finish
   Eof %if R == Nil {at end of the list now}
   S_Rab     == R
   S_ThisB    = R_Buffer
   S_Next     = S_ThisB_Base
   S_Limit    = S_ThisB_Base + S_ThisB_Size
   S_Position = R_Base Addr
   P = S_Next
   *LDR _ 2, P
   *LDR _ 1, S
%end

!
! > Close Input <
!
%externalroutine Close Input %alias "3L___local_close_in"( %integer SS, Abandon )
   %record(SCB Fm) %name S == Record(SS)
   %record(Local Fm) %name F == S_Fab
   F_Readers = F_Readers - 1
%end

%externalroutine Reset Input %alias "3L___local_reset_in"(%integer SS)
   %record(SCB Fm) %name S == Record(SS)
   S_Rab == Nil
   S_Limit = S_Next                         {nothing in it for now}
%end

!
! >> OPEN LOCAL INPUT <<
!
%external %routine Open Local Input %alias "3L_IMP_OPEN_LOCAL_INPUT" -
                                  ( %integer Stream, %string(15) File )
   %record(Local Fm) %name F == Find File(File)
   %if F_Writers # 0 %start
      Event_Message = File." currently being written"
      %signal 9, 3
   %finish
   F_Readers = F_Readers + 1
   %record(SCB Fm) %name S == New SCB(0)
   S_Close    Handler = Addr 2(Close Input)
   S_Reset    Handler = Addr 1(Reset Input)
   S_Buffer   Handler = Addr 0(Step Input Buffer)
   S_Rab == Nil;  S_Fab == F
   S_Limit = S_Next                           {nothing in it for now}
   Push Input Stream(Stream,S)
%end

!
! >> Step Output Buffer <<
!
%externalroutine Step Output Buffer %alias "3L___sob"
   {R1 = addr(SCB)}
   {R0 = Char}
   %record(SCB Fm) %name S
   %integer Char
   *STR _ 1, S
   *STR _ 0, Char
   %record(Local Fm) %name F == S_Fab
   %record(Segment Fm) %name R == S_Rab, N == Nil
   %integer Size = 1024, Base = 0
   %if R ## Nil %start
      Size = R_Buffer_Size
      Base = R_Base Addr + Size
   %finish
   Size = Size + Size>>1 {50% growth factor}
   N == New(N);  N_Buffer = Create Buffer(Size);  N_Next == Nil
   N_Base Addr = Base;  N_Limit Addr = Base + Size - 1
   {link onto end of list}
   %if R == Nil %then F_Segment List == N -
                %else R_Next         == N
   N_Prev     == R
   S_Rab      == N
   S_ThisB    = N_Buffer
   S_Limit    = S_ThisB_Base + S_ThisB_Size
   S_Position = Base
   S_Next     = S_ThisB_Base
   *LDR _ R0, Char
   *LDR _ R2, S
   *LDR _ R3, [R2]      {buffer pointer}
   *STRB_ R0, [R3], #1
   *STR _ R3, [R2]
%end

!
! > Close Output <
!
%externalroutine Close Output %alias "3L___local_close_out"( %integer SS, Abandon )
   %record(SCB Fm) %name S == Record(SS)
   %record(Local Fm) %name F == S_Fab
   F_Writers = F_Writers-1
   %record(Segment Fm) %name R == S_Rab
   %if R ## Nil %start
      R_Buffer_Size = S_Next - R_Buffer_Base
      R_Limit Addr =  R_Base Addr + R_Buffer_Size - 1
      F_Eof Addr = R_Limit Addr
   %finish
%end

!
! >> OPEN LOCAL OUTPUT <<
!
%external %routine Open Local Output %alias "3L_IMP_OPEN_LOCAL_OUTPUT" -
                                   ( %integer Stream, %string(15) File )
   %record(Local Fm) %name F == Find File(File)
   %if F_Readers # 0 %start
      Event_Message = FIle." currently being read"
      %signal 9, 3
   %finish
   %if F_Writers = 0 %start {delete space associated with the file}
      %record(Segment Fm) %name R, N == F_Segment List
      F_Segment List == Nil
      %while N ## Nil %cycle
         Delete Buffer(N_Buffer)
         R == N_Next;  Dispose(N);  N == R
      %repeat
   %else {already are some writers}
      %signal 15 %if F_Writers # 0 {cant cope with this so far...}
   %finish
   F_Writers = F_Writers+1
   %record(SCB Fm) %name S == New SCB(0)
   S_Close Handler = Addr 2 ( Close Output )
   S_Buffer  Handler = Addr 0(Step Output Buffer)
   S_Rab == Nil;  S_Fab == F
   S_Next = 0;  S_Limit = 0       {FULL}
   Push Output Stream(Stream,S)
%end
