// IfsLeafSwap.bcpl - Leaf - SWAPPABLE
// Copyright Xerox Corporation 1979, 1980, 1982

// Last modified April 9, 1982  6:26 PM by Taft

get ecBadHandle from "IfsLeafErrors.decl";
get ecReturnFromReturned from "IfsDirs.decl";
get "IfsLeaf.decl"; get "IfsSequin.decl";

external
[
//outgoing procedures
AnswerSetOp; CheckHandle; LeafCtx;

//incoming procedures
Allocate; Block; BuddingLeaf; CallersFrame; CloseLeaf;
DeleteLeaf; Dequeue; DoNothingLeaf; Enqueue; FalsePredicate;
IFSError; InsertBefore; LeafError; LeafFinish; LeafStartup;
Min; MoveBlock; OpenLeaf; ParamsLeaf; ReadLeaf;
ResetLeaf; ReturnFrom; SequinAllocate; SequinBreak;
SequinReadPBI; SequinReleasePBI; SequinPut; SequinUnlockPBI;
SequinReaper; SetTimer; TimerHasExpired; TruncateLeaf;
UnlockSequin; Unqueue; WriteLeaf; Zero;

//incoming statics
scb; socLeaf; sysZone;

// outgoing statics
leafOpLim;
]

static leafOpLim = 0;

//----------------------------------------------------------------------------
let LeafCtx() be
//----------------------------------------------------------------------------
[
manifest linkOffset = offset Sequin.inputLink/16;

LeafStartup();
// SequinCtx will check for halt conditions and set this flag.
until scb>>SCB.haltFlag do
   [
   let link = scb>>SCB.inputSequinQ.head;
   while link ne 0 do
      [
      let sequin = link - linkOffset; link = @link;
      if sequin>>Sequin.state eq destroyedState then
         [
         Zero(lv sequin>>Sequin.inputVec, maxInputVPBIs);
         sequin>>Sequin.freeInputSlot = 0;
         ]
      let vpbiID = sequin>>Sequin.inputVec↑0;
      if vpbiID ne 0 then
         [ if Leaf(sequin, vpbiID) then Block(); loop; ]
      Unqueue(lv scb>>SCB.inputSequinQ, sequin + linkOffset)
      sequin>>Sequin.inputLink = -1; UnlockSequin(sequin);
      ]
   Block()
   if TimerHasExpired(lv scb>>SCB.reaperTimer) then
      [
      SequinReaper();  // ***Called from here for the stack space.
      SetTimer(lv scb>>SCB.reaperTimer, reaperInterval);
      ]
   ]
LeafFinish();   // doesn't return
]

//----------------------------------------------------------------------------
and Leaf(sequin, vpbiID) = valof
//----------------------------------------------------------------------------
[
let answerPBI = SequinAllocate(sequin); if answerPBI eq 0 resultis false;
let sequence = sequin>>Sequin.inputState.current;
let pbi = SequinReadPBI(vpbiID, true, false, sequence);
let op = pbi>>PBI.pup.dPort.socket↑1;  // This word holds op offset or 0
op = (op eq 0)? lv pbi>>PBI.pup.words, op + pbi;
 // Compute end of pup (word boundary) ... loop until data exhausted
let pupLim = lv pbi>>PBI.pup + (pbi>>PBI.pup.length + 1)/2 - checkOvBytes/2;
   [
   leafOpLim = op + (op>>Op.length + 1)/2;
   let Proc = FalsePredicate;
   unless op>>Op.length eq 0 % op>>Op.answer % leafOpLim ugr pupLim do
      [
      Proc = selecton (sequin>>Sequin.brokenLeaf? opReset, op>>Op.code) into
         [
         case opRead: ReadLeaf
         case opWrite: WriteLeaf
         case opOpen: OpenLeaf
         case opClose:
         case opCloseTransaction: CloseLeaf
         case opReset: ResetLeaf
         case opDelete: DeleteLeaf
         case opTruncate: TruncateLeaf
         case opNoop: DoNothingLeaf
         case opParams: ParamsLeaf
         case opTelnet: //TelnetLeaf
         default: BuddingLeaf
         ]
      ]
   // Break the sequin on a bad format Leaf request,
   //  send an error message if this sequin has a broken Leaf.
   switchon Proc(sequin, answerPBI, op) into
      [
      //case false:
      default:
         SequinBreak(sequin);
         answerPBI>>PBI.pup.length = 0;
      case leafOpError: break;
      case leafOpComplete:
         op = leafOpLim; if leafOpLim eq pupLim then break;
      case leafOpPupNotFull:
         if answerPBI>>PBI.pup.length + 2*lenLargestLeafAnswer le
          sequin>>Sequin.pupDataBytes + pupOvBytes then loop;
      case leafOpPupFull:
         SequinPut(sequin, answerPBI); answerPBI = SequinAllocate(sequin);
         if answerPBI ne 0 then loop;
         pbi>>PBI.pup.dPort.socket↑1 = op - pbi;
         SequinUnlockPBI(pbi); resultis true;
      ]
   ] repeat
sequin>>Sequin.inputState.current = sequence + 1; SequinReleasePBI(pbi);
MoveBlock(lv sequin>>Sequin.inputVec, lv sequin>>Sequin.inputVec↑1,
 maxInputVPBIs);
sequin>>Sequin.freeInputSlot = sequin>>Sequin.freeInputSlot - 1;
if answerPBI>>PBI.pup.length le pupOvBytes then
   [ SequinReleasePBI(answerPBI); resultis false; ]
SequinPut(sequin, answerPBI); resultis true;
]

//----------------------------------------------------------------------------
and AnswerSetOp(answerPBI, op, length) = valof
//----------------------------------------------------------------------------
[
let pbiEvenLength = (answerPBI>>PBI.pup.length - checkOvBytes + 1) & -2
let answer = lv answerPBI>>PBI.pup + pbiEvenLength/2;
MoveBlock(answer, op, 2);  // move in the request's op and handle
answer>>LeafAnswer.op = (@op & opCodeMask) + opAnswerBit + length;
answerPBI>>PBI.pup.length = pbiEvenLength + length + checkOvBytes
resultis answer;
]

//----------------------------------------------------------------------------
and CheckHandle(sequin, answerPBI, op, needLVMD; numargs na) = valof
//----------------------------------------------------------------------------
[
let queue = lv sequin>>Sequin.fhQ; let handle = op>>FileRequest.handle;
if (handle+#377 & #177400) eq 0 then
   [
   let fh = queue>>Queue.head;
   until handle eq 0 % fh eq 0 do [ handle = handle + 1; fh = @fh ];
   handle = fh; op>>FileRequest.handle = fh;
   ];
if handle eq 0 % ((na ls 4 % needLVMD) & handle>>FH.lvmd eq 0) %
 not Unqueue(queue, handle) then
   [
   ReturnFrom(CallersFrame(),
    LeafError(answerPBI, op, ecBadHandle));
   IFSError(ecReturnFromReturned);
   ]
InsertBefore(queue, queue>>Queue.head, handle);
if @handle eq 0 then queue>>Queue.tail = handle;
resultis handle;
]