!REGION: procedures for accessing parts (regions) of files

%include "inc:util.imp"
%include "inc:fs.imp"

%bytemap put(%integer k,%bytename b)
  b = k; %result == b[1]
%end

%byteintegermap putnum(%integer n,%bytename b)
  b == putnum(n>>4,b) %and n = n&15 %if n>=64
  %result == put(n+'0',b)
%end

%routine add(%integer x,%string(*)%name s)
  s = s.tostring(x)
%end

%routine addnum(%integer x,%string(*)%name s)
  addnum(x>>4,s) %and x = x&15 %if x>=64
  add(x+'0',s)
%end

%byteintegermap num(%bytename b,%integername x)
%integer k
  x = 0
  %cycle
    k = b-'0'; b == b[1]
    %result == b %if k<0
    x = x<<4+k
  %repeat
%end

%routine move(%integer bytes,%bytename from,to)
  *sub.l #1,d0
  *swap d0
a:*swap d0
b:*move.b (a0)+,(a1)+
  *dbra d0,b
  *swap d0
  *dbra d0,a
%end

%routine funny or(%integer bytes,%half rshift,lshift,%bytename from,%halfname to)
  *sub.l #1,d0
  *swap d0
a:*swap d0
b:*clr.w d3
  *move.b (a0)+,d3
  *lsr.w d1,d3
  *andi.w #16_1F,d3
  *lsl.w d2,d3
  *or.w d3,(a1)+
  *dbra d0,b
  *swap d0
  *dbra d0,a
%end

! ACCESS FILE
! Opens the file and returns in SIZE the number of bytes in the file,
! and in REF a token which must be passed to DEACCESS FILE, READ REGION,
! and WRITE REGION.  The token is in fact the address of a record (NEWed
! by ACCESS FILE and DISPOSEd by DEACCESS FILE) which contains the
! transaction number and the file size.
! MODE=0: Read-only, MODE=1: Read-modify.

%recordformat token f (%integer size,last block,xno)

%externalroutine access file(%string(255)filename,%integer mode,
                             %integername ref,size)
%string(255)s=""
%integer blocks,xno
%bytename b
%record(tokenf)%name file
  mode = 'A'-'S' %unless mode=0
  add(mode+'S',s); add(userno+'0',s); s = s.filename; add(nl,s)
  b == charno(s,1)
  etherwrite(lsap,b,length(s))
  length(s) = etherread(lsap,b,255)
  %signal 3,3,b[1]-'0',substring(s,3,length(s)-1) %if b='-'
  b == num(b,xno)
  b == num(b,blocks)
  b == num(b,size)
  size = blocks<<9-size
  file == new(file)
  file_size = size
  file_last block = (size-1)>>9
  file_xno = xno
  ref = addr(file)
%end

! DEACCESS FILE
! Closes the file and throws away the token

%externalroutine deaccess file(%integer ref)
%string(255)s=""
%record(tokenf)%name file
%bytename b
  file == record(ref)
  add('K',s); add(file_xno+'0',s); add(nl,s)
  b == charno(s,1)
  etherwrite(lsap,b,length(s))
  length(s) = etherread(lsap,b,255)
  dispose(file)
%end

! READ REGION
! Reads BYTES bytes from the file specified by REF into store at BUFFER,
! starting with byte BYTE of the file (BYTE=0 means beginning of file).

%externalroutine read region(%integer ref,byte,bytes,%bytename buffer)
%string(31)s=""
%bytearray temp(1:516)
%bytename b,p
%record(tokenf)%name file
%integer lo,hi,i,count
  %returnif bytes<=0 %or byte<0
  file == record(ref)
  %signal 9 %if byte+bytes>file_size
  b == temp(1)
  lo = byte>>9; hi = (byte+bytes-1)>>9
  add('U',s); add(file_xno+'0',s); addnum(lo,s); add(nl,s)
  etherwrite(lsap,charno(s,1),length(s))
  i = etherread(lsap,b,515)
  ->shit %if b='-'
  s = ""
  add('X',s); add(file_xno+'0',s)
  addnum(hi-lo+1,s);   !Multi-block read!
  add(nl,s)
  etherwrite(lsap,charno(s,1),length(s))
  count = 512-byte&511
  p == b[byte&511+3]   {ASSUME filestore always sends size as 2 digits
  %cycle
    i = etherread(lsap,b,515)
    %if b='-' %start
shit: lo = b[1]-'0'; b[1] = i-3
      %signal 3,4,lo,string(addr(b)+1)
    %finish
    count = bytes %if bytes<count
    move(count,p,buffer)
    buffer == buffer[count]
    bytes = bytes-count
    count = 512; p == b[3]
    %exitunless i=515
    lo = lo+1
  %repeatuntil lo>hi
  %signal 3,4,0,"Read region: internal error" %unless lo>=hi %and bytes<=0
%end

%externalroutine funny or region(%integer ref,byte,bytes,shifts,%halfname buffer)
%half lshift,rshift
%string(31)s=""
%bytearray temp(1:516)
%bytename b,p
%record(tokenf)%name file
%integer lo,hi,i,count
  lshift = shifts>>16; rshift = shifts&16_FFFF
  %returnif bytes<=0 %or byte<0
  file == record(ref)
  %signal 9 %if byte+bytes>file_size
  b == temp(1)
  lo = byte>>9; hi = (byte+bytes-1)>>9
  add('U',s); add(file_xno+'0',s); addnum(lo,s); add(nl,s)
  etherwrite(lsap,charno(s,1),length(s))
  i = etherread(lsap,b,515)
  ->shit %if b='-'
  s = ""
  add('X',s); add(file_xno+'0',s)
  addnum(hi-lo+1,s);   !Multi-block read!
  add(nl,s)
  etherwrite(lsap,charno(s,1),length(s))
  count = 512-byte&511
  p == b[byte&511+3]   {ASSUME filestore always sends size as 2 digits
  %cycle
    i = etherread(lsap,b,515)
    %if b='-' %start
shit: lo = b[1]-'0'; b[1] = i-3
      %signal 3,4,lo,string(addr(b)+1)
    %finish
    count = bytes %if bytes<count
    funny or(count,rshift,lshift,p,buffer)
    buffer == buffer[count]
    bytes = bytes-count
    count = 512; p == b[3]
    %exitunless i=515
    lo = lo+1
  %repeatuntil lo>hi
  %signal 3,4,0,"Or region: internal error" %unless lo>=hi %and bytes<=0
%end

%externalroutine write region(%integer ref,byte,bytes,%bytename buffer)
%bytearray temp(1:532)
%integer count,i,lo,hi,offset,oldsize
%record(tokenf)%name file
%bytename b,b0
  %returnif bytes<=0 %or byte<0
  file == record(ref)
  lo = byte>>9; hi = (byte+bytes-1)>>9
  oldsize = file_size
  %signal 3,4,byte-file_size,"Write region: off end of file" %if byte>oldsize
  file_size = byte+bytes %if byte+bytes>file_size
  file_last block = hi %if hi>file_last block
  offset = byte&511
  b0 == put('W',temp(1))
  b0 == put(file_xno+'0',b0)
  %cycle
    b == putnum(lo,b0)
    b == put(',',b)
    %if offset#0 %start;    {first block not aligned
      count = offset+bytes
      count = 512 %if count>512 %or lo<hi
      count = file_size %if lo=hi %and count<file_size
      b == putnum(count,b)
      b == put(nl,b)
      i = 512; i = oldsize %if oldsize<512
      readregion(ref,byte-offset,i,b)
      i = 512-offset; i = bytes %if bytes<i
      move(i,buffer,b[offset])
      byte = byte-offset
      buffer == buffer[-offset]
      bytes = bytes+offset
      offset = 0
    %elseif lo=hi %and bytes<512  {last block incomplete
      count = bytes
      count = 512 %if lo<file_lastblock
      count = file_size-lo<<9 %if lo=file_last block
      b == putnum(count,b)
      b == put(nl,b)
      %if lo<hi %or byte+bytes<oldsize %start
        i = 512; i = oldsize-hi<<9 %if hi=file_last block
        readregion(ref,byte,i,b)
      %finish
      move(bytes,buffer,b)
    %else                         {complete block
      count = 512
      b == putnum(count,b)
      b == put(nl,b)
      move(512,buffer,b)
    %finish
    etherwrite(lsap,temp(1),addr(b)-addr(temp(1))+count)
    i = etherread(lsap,b0,255)
    %if b0='-' %start
      count = b0[1]-'0'; b0[1] = i-3
      %signal 3,4,count,string(addr(b0)+1)
    %finish
    byte = byte+count
    buffer == buffer[count]
    bytes = bytes-count
    lo = lo+1
  %repeatuntil lo>hi
%end

%endoffile
