-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- File: IFSFileControl.mesa

-- Last edited by HGM:  6-Feb-85 20:42:27
-- Last edited by Gobbel: 27-Jul-81 19:55:35
-- Last edited by Levin:   1-Jul-81 16:43:27
-- Hankins	25-Jul-84 13:42:58	Klamath update (LOOHOLEs)

DIRECTORY
  FileDefs USING [Operations, OperationsRecord],
  Runtime USING [IsBound],
  IFSFilePrivate USING [
    Abandon, Close, CopyString, Destroy, Extend, FinalizeFreeList, FSInstance,
    FSObject, GetLength, GetTimes, InitializeFreeList, Open, SetCreationTime,
    SetLength, StartRead, StartWrite, Truncate],
  Leaf USING [
    Answer, AnswerObject, LeafOp, leafSocket, paramsOp, ptLeaf, Request,
    RequestObject],
  LogDefs USING [DisplayNumber],
  PupDefs USING [
    GetPupAddress, PupAddress, PupNameTrouble, PupPackageDestroy, PupPackageMake],
  Sequin USING [
    Broken, Buffer, Create, Destroy, Get, GetEmptyBuffer, Handle, Put,
    ReleaseBuffer, SetZone],
  Storage USING [StringLength],
  String USING [WordsForString],
  VMDefs USING [Problem, UnableToLogin],
  VMStorage USING [longTerm, shortTerm];

IFSFileControl: MONITOR
  IMPORTS
    IFSFilePrivate, LogDefs, PupDefs, Runtime, Sequin, Storage, String, VMDefs,
    VMStorage
  EXPORTS FileDefs, IFSFilePrivate =

  BEGIN OPEN IFSFilePrivate;


  -- Global Variables --

  loginCount: CARDINAL;

  ifsOps: FileDefs.Operations;

  oldZone: MDSZone;


  -- Miscellaneous --

  someWordsForFilename: CARDINAL = 15;

  CantCommunicateByTelepathy: ERROR = CODE;
  FilesInUse: ERROR = CODE;
  InsufficientLogouts: ERROR = CODE;
  PupBuffersTooSmall: ERROR = CODE;
  ServerNameMissing: ERROR = CODE;
  TooManyLogouts: ERROR = CODE;


  -- Types Exported to FileDefs --

  -- Unfortunately, the compiler doesn't support this just yet
  -- FSInstance: PUBLIC TYPE = IFSFilePrivate.FSInstance;

  -- Procedures Exported to FileDefs --

  InitializeIFS: PUBLIC PROCEDURE RETURNS [ops: FileDefs.Operations] =
    BEGIN
    IF (loggingEnabled ← Runtime.IsBound[LOOPHOLE[LogDefs.DisplayNumber]]) THEN {
      ifsFiles ← 0; LogDefs.DisplayNumber["Open IFS Files"L, [short[@ifsFiles]]]};
    loginCount ← 0;
    InitializeFreeList[];
    -- the LOOPHOLEs below get around non-support for multiple implementors of an
    -- exported type.
    ifsOps ← VMStorage.longTerm.NEW[
      FileDefs.OperationsRecord ← [
      login: LOOPHOLE[Login], logout: LOOPHOLE[Logout],
      checkpoint: LOOPHOLE[Checkpoint], abort: LOOPHOLE[Abort],
      open: LOOPHOLE[Open], close: LOOPHOLE[Close], abandon: LOOPHOLE[Abandon],
      destroy: LOOPHOLE[Destroy], getLength: LOOPHOLE[GetLength],
      setLength: LOOPHOLE[SetLength], extend: LOOPHOLE[Extend],
      truncate: LOOPHOLE[Truncate], startRead: LOOPHOLE[StartRead],
      startWrite: LOOPHOLE[StartWrite], getTimes: LOOPHOLE[GetTimes],
      setCreation: LOOPHOLE[SetCreationTime]]];
    oldZone ← Sequin.SetZone[VMStorage.shortTerm];
    RETURN[ifsOps]
    END;

  FinalizeIFS: PUBLIC PROCEDURE =
    BEGIN
    IF loginCount # 0 THEN ERROR InsufficientLogouts;
    VMStorage.longTerm.FREE[@ifsOps];
    FinalizeFreeList[];
    [] ← Sequin.SetZone[oldZone];
    END;


  -- Variables Exported to IFSFilePrivate --

  loggingEnabled: PUBLIC BOOLEAN;

  ifsFiles: PUBLIC CARDINAL;


  -- Internal Procedures --

  Login: PROCEDURE [
    server, userName, password, secondaryName, secondaryPassword: STRING ← NIL]
    RETURNS [fs: FSInstance] =
    BEGIN
    serverAddr: PupDefs.PupAddress ← [net:, host:, socket: Leaf.leafSocket];
    TryForConnection: PROCEDURE =
      BEGIN
      LeafStringWords: PROCEDURE [s: STRING] RETURNS [CARDINAL] = {
        RETURN[String.WordsForString[Storage.StringLength[s]] - 1]};
      sequin: Sequin.Handle;
      buffer: Sequin.Buffer ← Sequin.GetEmptyBuffer[];
      adequate: CARDINAL =
        2 *
          (MAX[SIZE[Leaf.RequestObject], SIZE[Leaf.AnswerObject]] + 1 +
             LeafStringWords[userName] + LeafStringWords[password] +
             LeafStringWords[secondaryName] + LeafStringWords[secondaryPassword] +
             someWordsForFilename);
      problem: VMDefs.Problem;
      IF buffer.maxBytes < adequate THEN ERROR PupBuffersTooSmall;
      sequin ← Sequin.Create[dest: serverAddr, pupType: Leaf.ptLeaf];
      LOOPHOLE[buffer.data, Leaf.Request]↑ ← [
        Leaf.paramsOp, params[
        packetDataBytes: buffer.maxBytes, fileLockTimeout: 2 * 60 / 5,
        connectionTimeout: 10 * 60 / 5]];
      buffer.nBytes ← Leaf.paramsOp.length;
      BEGIN
      ENABLE Sequin.Broken => {problem ← io; GO TO serverDead};
      answerOp: Leaf.LeafOp;
      Sequin.Put[sequin, buffer];
      buffer ← Sequin.Get[sequin];
      answerOp ← LOOPHOLE[buffer.data, Leaf.Answer].op;
      Sequin.ReleaseBuffer[buffer];
      IF answerOp.type # params OR answerOp.sense # reply THEN {
        problem ← other; GO TO serverDead};
      EXITS
        serverDead => {
          Sequin.Destroy[sequin]; ERROR VMDefs.UnableToLogin[problem]};
      END;
      Sequin.Destroy[sequin];
      END;
    NoteLogin: ENTRY PROCEDURE = INLINE {loginCount ← loginCount + 1};
    IF server = NIL OR server.length = 0 THEN ERROR ServerNameMissing;
    IF Runtime.IsBound[LOOPHOLE[PupDefs.PupPackageMake]] THEN
      PupDefs.PupPackageMake[]
    ELSE ERROR CantCommunicateByTelepathy;
    PupDefs.GetPupAddress[
      @serverAddr, server !
      PupDefs.PupNameTrouble =>
        ERROR VMDefs.UnableToLogin[
          SELECT code FROM noRoute, noResponse => io, ENDCASE => other]];
    TryForConnection[];
    fs ← VMStorage.shortTerm.NEW[
      FSObject ← [
      primaryName: CopyString[userName], primaryPassword: CopyString[password],
      secondaryName: CopyString[secondaryName],
      secondaryPassword: CopyString[secondaryPassword], serverAddr: serverAddr]];
    NoteLogin[];
    END;

  Logout: PROCEDURE [fs: FSInstance] =
    BEGIN
    NoteLogout: ENTRY PROCEDURE = INLINE {loginCount ← loginCount - 1};
    IF loginCount = 0 THEN ERROR TooManyLogouts;
    IF fs.fileList # NIL THEN ERROR FilesInUse;
    IF fs.primaryName # NIL THEN VMStorage.shortTerm.FREE[@fs.primaryName];
    IF fs.primaryPassword # NIL THEN VMStorage.shortTerm.FREE[@fs.primaryPassword];
    IF fs.secondaryName # NIL THEN VMStorage.shortTerm.FREE[@fs.secondaryName];
    IF fs.secondaryPassword # NIL THEN VMStorage.shortTerm.FREE[@fs.secondaryPassword];
    VMStorage.shortTerm.FREE[@fs];
    PupDefs.PupPackageDestroy[];
    NoteLogout[];
    END;

  Checkpoint: PROCEDURE [fs: FSInstance] = {NULL};

  Abort: PROCEDURE [fs: FSInstance] = {NULL};


  END.