// IfsLeafRead.bcpl - Leaf Read - SWAPPABLE
// Copyright Xerox Corporation 1979, 1981, 1982

// Last modified April 9, 1982  12:49 PM by Taft

get FD, mode, modeReadWrite, modeWrite, modeReadWriteShared
 from "IfsDirs.decl"
get ecIllegalLeafRead, ecLeafFileTooLong from "IfsLeafErrors.decl";
get "IfsLeaf.decl";
get "IfsSequin.decl";

external
[
//outgoing procedures
SetModeLength; ReadLeaf;

//incoming procedures
AnswerSetOp; ByteBlt; CheckHandle; DoubleAdd; DoubleDifference;
DoubleIncrement; DoubleSubtract; DoubleUsc; LeafError; LeafGetPage;
Umin; MoveBlock; Zero; 
]

//----------------------------------------------------------------------------
let ReadLeaf(sequin, answerPBI, op) = valof
//----------------------------------------------------------------------------
[
let fh = CheckHandle(sequin, answerPBI, op);
let address = lv op>>FileRequest.address;
let contentEvenLength = ((answerPBI>>PBI.pup.length + 1) & -2) - pupOvBytes;
let answer = lv answerPBI>>PBI.pup.words + contentEvenLength/2;
let pupDataMax = sequin>>Sequin.pupDataBytes - readAnswerOv;
let readLength = Umin(Umin(bytesPerUserPage, pupDataMax), op>>FileRequest.length)
if readLength ugr (pupDataMax-contentEvenLength) then resultis leafOpPupFull;
unless SetModeLength(op, fh, false, lv readLength) do
 resultis LeafError(answerPBI, op, ecIllegalLeafRead);
let readByte = address>>LeafAddress.low & bytesPerPage-1
let readPage = address>>LeafAddress.highAddr lshift logPagesPerWord +
 address>>LeafAddress.low rshift logBytesPerPage
let firstLength = (readByte + readLength ule bytesPerPage ?
 readLength, bytesPerPage - readByte)
let pageAddress = LeafGetPage(fh>>FH.lvmd, readPage + 1, cleanPage);
if pageAddress eq 0 then resultis LeafError(answerPBI, op, ecLeafFileTooLong);
ByteBlt(lv answer>>FileAnswer.words, 0, pageAddress, readByte, firstLength)
if readLength ugr firstLength then
   [
   pageAddress = LeafGetPage(fh>>FH.lvmd, readPage + 2, cleanPage);
   if pageAddress eq 0 then resultis LeafError(answerPBI, op,
    ecLeafFileTooLong);
   ByteBlt(lv answer>>FileAnswer.words, firstLength, pageAddress, 0,
    readLength - firstLength)
   ]
//no thirds allowed (since bytesPerPage >= bytesPerPup)

AnswerSetOp(answerPBI, op, readAnswerOv + readLength);
MoveBlock(lv answer>>FileAnswer.address, address,
 size FileAnswer.address/bitsPerWord + size FileAnswer.length/bitsPerWord);
DoubleIncrement(address, readLength); address>>LeafAddress.signExtend = 0;
op>>FileRequest.length = op>>FileRequest.length - readLength;
resultis (op>>FileRequest.length eq 0? leafOpComplete, leafOpPupNotFull);
]

//----------------------------------------------------------------------------
and SetModeLength(op, fh, writeFlag, lvLength; numargs na) = valof
//----------------------------------------------------------------------------
[
if na ls 4 then lvLength = lv op>>FileRequest.length;
let lastAddress = lv fh>>FH.lvmd>>LVMD.lastAddress
let maxAddress = table [ #3777; #174000; ] // 2**27 - 2048 words of address
let length = op>>FileRequest.length; let address = lv op>>FileRequest.address; 
let mode = address>>LeafAddress.mode;
let readOnly = true; let noNewEOFs = true;
switchon fh>>FH.fd>>FD.mode into
   [
   case modeWrite: case modeReadWrite:
      noNewEOFs = false;
   case modeReadWriteShared:
      readOnly = false;
   default: endcase;
   ]
let newEOF = address>>LeafAddress.newEOF;
address>>LeafAddress.signExtend = 0;
let funnyAddress = DoubleUsc(address, maxAddress) ge 0;
if funnyAddress then
   [ //** writes cannot occur on the first 3/4 of the IFS leader page unless
     //**  the special change create date and type hack is being invoked...
   if writeFlag & address!1 uls -512 then
      [
      let leaderPagePos = address!1 + bytesPerPage;
      unless (leaderPagePos eq offset ILD.created/8 & length eq 2*lTIME) %
       (leaderPagePos eq offset ILD.type/8 & length eq 4) do resultis false;
      ]
   address>>LeafAddress.signExtend = -1;
   ]
let nextAddress = vec 1; nextAddress!0 = 0; nextAddress!1 = length;
DoubleAdd(nextAddress, address);
let extendEOF = (DoubleDifference(lastAddress, nextAddress) & signBit) ne 0;

// Check here to see:
//  1) if trying to write onto anywhere but the leaderpage in readOnly mode.
//  2) if trying to set newEOF in readOnly or on leaderpage.
// Make 'writes' mean writes to file data itself.
if funnyAddress & nextAddress!1 le 0 then writeFlag = false;
if (readOnly & writeFlag) %
 (newEOF & (noNewEOFs % nextAddress!0 ls 0)) then resultis false;

switchon mode into
   [
   case checkExtend:
      if extendEOF then resultis false; endcase;
   case dontExtend:
      if extendEOF then
         [
         let difference = vec 1; nextAddress = lastAddress;
         MoveBlock(difference, lastAddress, 2);
         DoubleSubtract(difference, address);
         test difference!0 eq 0
          ifso length = difference!1;
          ifnot [ MoveBlock(address, lastAddress, 2); length = 0; ]
         if @lvLength ugr length then @lvLength = length;
         op>>FileRequest.length = length;
         ]
      endcase;
   case noHoles:
      if (DoubleDifference(lastAddress, address) & signBit) ne 0 then
       resultis false;
   case anywhere:
      unless extendEOF do endcase;
      if noNewEOFs resultis false;  // Only allow extends in write mode.
      if (DoubleDifference(maxAddress, nextAddress) & signBit) ne 0
       then resultis false;
   ]

if writeFlag then fh>>FH.lvmd>>LVMD.written = true;
if newEOF % extendEOF then MoveBlock(lastAddress, nextAddress, 2);
address>>LeafAddress.signExtend = 0;
address>>LeafAddress.newEOF = DoubleUsc(lastAddress, nextAddress) eq 0;
 // use newEOF to report whether address plus length equals last address
resultis true;
]