// IfsLeafOpen.bcpl - Leaf Open - SWAPPABLE
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 20, 1981  11:12 AM by Taft

get "IfsLeaf.decl";
get Sequin, fhQ, state, brokenLeaf, timedOutState from "IfsSequin.decl"
get "IfsDirs.decl";

external
[
//outgoing procedures
OpenLeaf; LeafLogin; LeafToIfsString;

//incoming procedures
SysAllocateZero; MoveBlock; Zero;
Enqueue; ExtractSubstring; InsertBefore; StringCompare;
OpenLeafVMem; CloseFH; FindLVMD; FreeLeafVMem; LeafError;
Login; Connect; CreateFD; LookupFD; OpenIFSFile;
AnswerSetOp; NextLockedSequin; SequinDestroy; UnlockSequin;

//incoming statics
CtxRunning; leafOpLim; system;
]

structure String: [ length byte; char↑1,1 byte ]

//----------------------------------------------------------------------------
let OpenLeaf(sequin, answerPBI, op) = valof
//----------------------------------------------------------------------------
[
let fh = SysAllocateZero(lenFH);
let queue = lv sequin>>Sequin.fhQ; let queueTop = @queue;
test queueTop ne 0
   ifso InsertBefore(queue, queueTop, fh)
   ifnot Enqueue(queue, fh)
let mode = op>>OpenRequest.mode; fh>>FH.mode = mode;
let leafstring = lv op>>OpenRequest.strings; let ui = lv fh>>FH.ui;
let ec = valof
   [
   let ec = LeafLogin(lv leafstring, ui, true); if ec ne 0 then resultis ec;
   let filename = LeafToIfsString(lv leafstring)
   let lc = selecton mode<<Mode.vDefault into
      [
      case vLowest: lcVLowest
      case vDontDefault:
      case vHighest: lcVHighest
      case vNext: lcVNext + lcCreate
      ]
   let openMode = (mode<<Mode.write eq 0? modeRead,
    (mode<<Mode.multipleWriters eq 0? modeReadWrite, modeReadWriteShared));
   if mode<<Mode.create then lc<<LC.create = true;
   if (openMode eq modeReadWrite) ne (mode<<Mode.extend ne 0) %
    (openMode ne modeReadWrite & lc<<LC.create ne 0) then
     resultis ecIllegalLookupControl;
   let fd = CreateFD(filename, lc, lv ec); if fd eq 0 then resultis ec;
   fh>>FH.fd = fd;
      [ // Repeat for ecFileBusy cleanup.
      ec = LookupFD(fd);
      if ec ne 0 %
       fd>>FD.lookupStatus eq lsNonexistent & not mode<<Mode.create %
       (fd>>FD.lc.vc ne lcVExplicit? mode<<Mode.vDefault eq vDontDefault,
       mode<<Mode.vExplicit ne anyExplicit) then
         resultis (ec ne 0? ec, ecIllegalLookupControl);
      fh>>FH.lvmd = OpenLeafVMem(fd, openMode, lv ec);
      ] repeatwhile ec eq ecFileBusy & UnlockFHs(fd)
   resultis ec;
   ]
CtxRunning>>RSCtx.userInfo = system;
if ec ne 0 then
   [ CloseFH(sequin, fh); resultis LeafError(answerPBI, op, ec); ]
let answer = AnswerSetOp(answerPBI, op, 2*lenOpenAnswer);
answer>>OpenAnswer.handle = fh
MoveBlock(lv answer>>OpenAnswer.length, lv fh>>FH.lvmd>>LVMD.lastAddress, 2)
resultis leafOpComplete;
]

//----------------------------------------------------------------------------
and LeafLogin(lvleafstring, ui, connect; numargs na) = valof
//----------------------------------------------------------------------------
[
if na ls 3 then connect = false;
Zero(ui, lenUserInfo); CtxRunning>>RSCtx.userInfo = ui;
let name = LeafToIfsString(lvleafstring);
let password = LeafToIfsString(lvleafstring)
let result = Login(name, password, ui)
if result eq 0 & connect then
   [
   let cName = LeafToIfsString(lvleafstring);
   let cPassword = LeafToIfsString(lvleafstring);
   if cName>>String.length ne 0 then
      result = Connect(cName, cPassword, ui);
   ]
resultis result
]

//----------------------------------------------------------------------------
and LeafToIfsString(lvstring) = valof
//----------------------------------------------------------------------------
[
let string = @lvstring;
// see if string is too long for op
test (leafOpLim - string)*2 gr @string
   ifnot @string = #777;  // Yes, insert a string of one 'delete' char.
   ifso
      [
      string>>String.length = @string;
      for i = 1 to string>>String.length do
       string>>String.char↑i = string>>String.char↑(i+1)
      ]
@lvstring = string + (string>>String.length + 3)/2; resultis string
]

//----------------------------------------------------------------------------
and UnlockFHs(fd) = valof
//----------------------------------------------------------------------------
[
// See if this file is locked under a timed out handle/sequin.
// If it is, unlock it.  Return true if the existing lvmd gets flushed.
let lvmd = FindLVMD(lv fd>>FD.dr>>DR.fp);
if lvmd eq 0 then resultis false;  // Return if locked by non-Leaf user. 
let sequin = 0;
   [  // loop through all sequins
   sequin = NextLockedSequin(sequin); if sequin eq 0 break;
   if sequin>>Sequin.state ne timedOutState then loop;
   let fh = @(lv sequin>>Sequin.fhQ);
   while fh ne 0 do
      [
      if lvmd eq fh>>FH.lvmd then
         [
         fh>>FH.lvmd = 0; sequin>>Sequin.brokenLeaf = true;
         if FreeLeafVMem(fh>>FH.fd, lvmd) then
            [ UnlockSequin(sequin); resultis true; ]
         ]
      fh = fh>>FH.link;
      ]
   ] repeat
resultis false;
]