%include "moose:mouse.inc"
%option "-low-nocheck-nodiag-noline-nostack"
%include "sys:fsconsts.inc"
%include "sys:ether.inc"

%recordformat fsreqfm(%record(messagefm) msg,
                      %Integerarray i(1:4))

!                         _^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@
%constinteger allowed = 2_01111111101111111111111111111111,
              simple  = 2_01001000011110111011110100111111,
              writer  = 2_00000010100000000000000000000000,
              reader  = 2_00110001000001000100001011000000,
              z       = 2_00000100000000000000000000000000
%conststring(31)bad = "Bad filestore command"

%owninteger command letter=0, command bit=0
%Owninteger uno=0, rdte, lsap

%Recordformat Connfm (%Integer rdte,
                      %Integer lsap, rsap,
                      %Record(connfm)%name next)

%Ownrecord(connfm)%name conns

%ownrecord(mailboxfm)      mbox
%ownrecord(semaphorefm)    sem

%ownbytearray   pbuffer (0:1023)
%ownbyteinteger pbytes

%Routine initialise
   %Record(dictfm)%name m
   %integer i

   putstring ("Filestore process. Version of 17/7/86");putsym(10)

   setup semaphore (sem)
   setup mailbox (mbox, sem)

   ether start
   conns == NIL

   m == poa_logdict
   m == m_alt %while m_alt ## Nil
   i = Make Entry ("FS_REQUESTS",m)
   %If i = 0 %start
      putstring ("Filestore process failed to create mailbox");putsym(10)
      %Stop
   %Finish
   integer (i) = addr(mbox)
%End

%routine error(%string(255)%name s)
%bytename b
  %string(255) p
  p = substring(s,3,length(s))
  %if charno(p,length(p))= nl %then length(p)=length(p)-1
  %signal 3,3,charno(s,2)-'0',p
%end

%predicate okok(%integer mask)
  %falseif commandbit&mask=0
  %true
%end

%predicate ok(%integer cl)
  %falseunless cl>='@'
  commandletter = cl&95
  commandbit = 1<<(cl&31)
  %trueif okok(allowed)
  %false
%end

%Routine fc(%string(255)%name command,response,
                    %integer port,%integername bytes,%bytename buffer)

!Perform a filestore interchange, i.e. send the command line in COMMAND,
!followed by the BYTES-byte data at BUFFER (if the command is of the
!write-kind), then read a response, putting the response line part of it
!into RESPONSE, and the rest (if the command is of the read-kind) into
!the BUFFER, setting BYTES accordingly.
!In the read case, BYTES is both an input (which tells us how big the
!buffer is) and an output (which tells the caller how big the data-part
!of the packet was).
!The Z-command (read whole file) is a special case, and here we send the
!command and receive the response and as many packets as the filestore
!sends us.

%bytearray buf(0:531)
%integer linelength,size

  %routine append(%integer ch)
    command = command.tostring(ch)
  %end

  %routine phex(%integer n)
!Append HD-hex number to command line
    phex(n>>4) %and n = n&15 %if n>>6#0
    append(n+'0')
  %end

  %routine move(%integer bytes,%bytename from,to)
  %label x,y
    *subq #1,d0
    *ble y
  x:*move.b (a0)+,(a1)+
    *dbra d0,x
  y:
  %end

  %routine scan
    linelength = 0
    linelength = linelength+1 %until linelength=80 %or buf(linelength-1)=nl
  %end

  !putstring("FC")
  %signal 3,4,0,bad %unless ok(charno(command,1))
  length(command) = length(command)-1 %if charno(command,length(command))=nl
  append(',') %and phex(bytes) %if commandbit&writer#0
  append(nl)
  %if commandbit&writer#0 %start
    bytes = 0 %if bytes<0
    bytes = 512 %if bytes>512
    move(length(command),charno(command,1),buf(0))
    move(bytes,buffer,buf(length(command)))
    ethertransmitblock(port,length(command)+bytes,buf(0))
    etherreceiveblock(port,532,size,buf(0))
    scan
    length(response) = linelength
    move(linelength,buf(0),charno(response,1))
  %else
    ethertransmitblock(port,length(command),charno(command,1))
    etherreceiveblock(port,532,size,buf(0))
    scan
    length(response) = linelength
    move(length(response),buf(0),charno(response,1))
    !putstring("Response=".response);putsym(10)
    %if commandbit&reader#0 %start
      size = size-linelength
      size = 0 %if size<0
      size = bytes %if size>bytes
      bytes = size
      move(size,buf(linelength),buffer)
    %elseif commandbit&z#0
      %returnif charno(response,1)='-'; !Error response
      size = 0
      %cycle
        etherreceiveblock(port,bytes,linelength,buffer)
        bytes = bytes-linelength; size = size+linelength
        buffer == byteinteger(addr(buffer)+linelength)
      %repeatuntil linelength#512;      !Keep going till we get an
      bytes = size;                     !incomplete (possibly empty) block.
    %finish
  %finish
%end

%externalintegerfn fcomm(%Integer lsap, %integer cn,%string(255)s)
!Backward compatible.  Perform a filestore interchange with a non-read,
!non-write command.  CN contains the command letter (<<8) and the
!reference number digit (user number of transaction number), S contains
!the parameters if any.  The result is the numeric filestore response,
!unless there is an error response, in which case we bomb out.
  cn = cn+uno+'0' %if cn&255=0;  !Frig: substitute logged-on user number
  %signal 3,4,0,bad %unless ok(cn>>8) %and okok(simple)
  uno = 0 %if commandletter='M'; !Forget user number if logging off
  s = tostring(cn>>8).tostring(cn).s
  fc(s,s,lsap,integer(0),byteinteger(0))
  error (s) %if charno(s,1)='-'
!  %signal 3,4,charno(s,2)-'0',substring(s,3,length(s)) %if charno(s,1)='-'
  cn = charno(s,1)-'0'
  uno = cn %if commandletter = 'L'; !Remember user number if logging on
  %result = cn
%end

%externalroutine fcommw(%Integer lsap,%integer cn,%string(255)s,
                        %bytename buffer,%integer size)
!Backward compatible filestore interchange, write-command.
  %signal 3,4,0,bad %unless ok(cn>>8) %and okok(writer)
  s = tostring(cn>>8).tostring(cn).s
  fc(s,s,lsap,size,buffer)
!  %signal 3,4,charno(s,2)-'0',substring(s,3,length(s)) %if charno(s,1)='-'
   error(s) %if charno (s,1) = '-'
%end

%externalintegerfn fcommr(%integer lsap,%integer cn,%string(255)s,
                          %bytename buffer,%integer max)
!Backward compatible filestore interchange, read-command.
  cn = cn+uno+'0' %if cn&255=0
  %signal 3,4,0,bad %unless ok(cn>>8) %and okok(reader)
  s = tostring(cn>>8).tostring(cn).s
!  PUTSTRING("LSAP=");putlong(lsap);putsym(10)
  !putstring("<<".s);putsym(10)
  fc(s,s,lsap,max,buffer)
  !putstring(">>".s);putsym(10)
!  %signal 3,4,charno(s,2)-'0',substring(s,3,length(s)) %if charno(s,1)='-'
  error(s) %if charno(s,1) = '-'
  %result = max
%end

%Integerfn Get Lsap (%Integer rdte)
   %Record(connfm)%name c, p==NIL, nc
   %Integer bytes, lsap, rsap

   c == conns
   %While c ## NIL %cycle
      %result = c_lsap %if c_rdte = rdte
      p == c; c==c_next
   %Repeat

   lsap = ether allocate port
   ether open port (lsap, rdte, 0)
   pbuffer(0) = 2
   ether transmit block (lsap, 1, pbuffer(0))
   pbuffer(0) = 12
   ether transmit block (lsap, 1, pbuffer(0))
   ether receive block (lsap, 512, bytes, pbuffer(0))
   %If pbuffer(0) = '-' %start
      %signal 3, 4, 0, "Filestore refused to give a port"
   %Finish
   rsap = pbuffer(0)-'0'
   ether close port (lsap)
   ether open port (lsap, rdte, rsap)
   nc == new (nc); nc = 0
   nc_next == NIL
   nc_lsap = lsap
   nc_rsap = rsap
   nc_rdte = rdte
   %if conns == NIL %start
      conns == nc
   %Else
      p_next == nc
   %finish
   %Result = lsap
%End

%string(9)%fn itohmd(%Integer x)
%string(9) s=""
   %routine put(%Integer n)
      put(n>>4) %and n=n&15 %if n>>6#0
      s=s.tostring(n+'0')
   %end
   put(x)
   %result = s
%End

%Routine logon  (%Record(fsreqfm)%name r)
   %Record(passfm)%name pass
   %integer pos=1
   %String(255) fn

   %integerfn hex
      %integer n=0,k
      %cycle
         %result = n %if pos >= length(fn); pos=pos+1
         k = charno (fn,pos-1)-'0'
         %result = n %if k < 0
         n = n<<4+k
      %Repeat
   %End
         
   pass == record(r_i(4))
   rdte = r_i(3)
   lsap = get lsap (rdte)
   fn = "L0".pass_owner.",".pass_password
   fc (fn,fn,lsap,nil,nil)
   error(fn) %if charno(fn,1)='-'
   uno = hex
   r_i(1) = 1
   r_i(2) = uno+rdte<<8
%End

%Routine logoff (%Record(fsreqfm)%name r)
   putstring("Logoff request")
%End

%Routine open file (%Record(fsreqfm)%name r)
   %integer pos=1,xno,blocks,pad
   %String(255) fn 

   %integerfn hex
      %integer n=0,k
      %cycle
         %result = n %if pos >= length(fn); pos=pos+1
         k = charno (fn,pos-1)-'0'
         %result = n %if k < 0
         n = n<<4+k
      %Repeat
   %End

   uno = r_i(3)
   rdte = uno >> 8
   lsap = get lsap (rdte)
   uno = uno & 255
   fn = "S".itohmd(uno).string(r_i(4))
   fc (fn,fn,lsap,nil,nil)
   error(fn) %if charno(fn,1)='-'
   xno = hex; blocks=hex; pad=hex
   r_i(1) = 1
   r_i(2) = xno+rdte<<8
   r_i(3) = blocks<<9-pad
%End

%Routine create file (%Record(fsreqfm)%name r)
   %Integer p

   !putstring("create file request")
   uno = r_i(3)
   rdte = uno >> 8
   lsap = get lsap (rdte)
   uno = uno & 255
   p = fcomm (lsap,'T0'+uno,string(r_i(4)))
   r_i(1) = 1
   r_i(2) = p+rdte<<8
%End

%Routine read file (%Record(fsreqfm)%name r)
   %Record(blockfm)%name bl
   %bytename buff
   %Integer xno
   %Integer max=0, total=0, this

   bl == record(r_i(3))
   xno = bl_token
   lsap = get lsap (xno>>8)   
   xno = xno & 255
   buff == byteinteger(bl_buffer)
   !putstring("RF");putsym(10)
   %cycle
!      putstring("Pos=");putlong(bl_position>>9);putsym(10)
      this = fcommr (lsap,'R0'+xno,itohmd(bl_position>>9),buff,512)
      %exitif this = 0
      max = this %if this > max
      %exitif this < max
      buff == buff[this]
      total=total+this
      bl_position = bl_position + this
 !     putstring("tot=");putlong(total);putstring(" amo=");putlong(bl_amount)
 !     putsym(10)
   %Repeatuntil total=bl_amount %or this < 512 {Catch VAX}
   r_I(1) = 1
   r_i(2) = total
%End

%Routine write file (%Record(fsreqfm)%name r)
   %record(blockfm)%name bl
   %bytename buff
   %Integer xno, this, total=0

   !putstring("write file request")
   bl == record(r_i(3))
   xno = bl_token
   lsap = get lsap (xno>>8)
   xno = xno&255
   buff == byteinteger(bl_buffer)
   %Cycle
      this = bl_amount - total
      this = 512 %if this > 512
      fcommw (lsap,'W0'+xno,itohmd(bl_position>>9),buff,this)
      bl_position = bl_position + this
      buff == buff[this]
      total = total+this
   %Repeatuntil total=bl_amount
   r_i(1) = 1
   r_i(2) = total
%End

%Routine close file (%Record(fsreqfm)%name r)
   %integer xno,p

   xno = r_i(3)
   lsap = get lsap (xno>>8)
   xno = xno & 255
   p = fcomm (lsap,'K0'+xno,"") %unless xno=0
   r_i(1) = 1
%End

%Routine delete file (%Record(fsreqfm)%name r)
%End

%Routine copy file (%Record(fsreqfm)%name r)
%End

%Routine rename file (%Record(fsreqfm)%name r)
%End

%Routine Handle Request (%Record(fsreqfm)%name r)
   %Constinteger last = 10
   %Switch sw(1:last)

   %on 3 %start
      r_i(1) = 0
      string(r_i(2))=event_message
      %Return
   %Finish
            
   -> bum %unless 1 <= r_i(1) <= last
   -> sw(r_i(1))

sw(fs logon):        logon(r)       ; %Return
sw(fs logoff):       logoff(r)      ; %Return
sw(fs open file):    open file(r)   ; %Return
sw(fs create file):  create file(r) ; %Return
sw(fs read file):    read file(r)   ; %Return
sw(fs write file):   write file(r)  ; %Return
sw(fs close file):   close file(r)  ; %Return
sw(fs delete file):  delete file(r) ; %Return
sw(fs copy file):    copy file(r)   ; %Return
sw(fs rename file):  rename file(r) ; %Return

bum:  r_i(1) = 0
      string(r_i(2)) = "Bad function code"
      %Return
%End

%Begin
   %Record(fsreqfm)%name req

   initialise

   %cycle
!!      putstring("FP Snore");putsym(10)
      req == receive message (mbox)
!!      putstring("FP Alarm");putsym(10)
      handle request (req)
      send message (req,req_msg_reply,nil)
   %repeat

%Endofprogram
