-- File: IFSFileImplE.mesa
-- Last edited by:
-- Levin -  2-Jul-81 10:14:42

DIRECTORY
  IFSFile USING [AccessFailure, CantOpen, Error],
  IFSFilePrivate USING [CopyString, FileHandle, FileObject, FSInstance, zone],
  Inline USING [BITOR];

IFSFileImplE: MONITOR LOCKS fs.LOCK USING fs: IFSFilePrivate.FSInstance
  IMPORTS IFSFile, IFSFilePrivate, Inline
  EXPORTS IFSFilePrivate =

  BEGIN OPEN IFSFilePrivate;


  -- Miscellaneous --

  FileListSmashed: ERROR = CODE;
  FileNotInList: ERROR = CODE;
  IllegalDestroy: ERROR = CODE;
  InvalidFile: ERROR = CODE;


  -- Procedures and Signals Exported to IFSFilePrivate --

  InsertFile: PUBLIC PROCEDURE [fs: FSInstance, name: LONG STRING,
    openProc: PROCEDURE [FileHandle] RETURNS [IFSFile.AccessFailure]]
    RETURNS [file: FileHandle] =
    BEGIN

    NewlyOpened: ENTRY PROCEDURE [fs: FSInstance] RETURNS [BOOLEAN] = INLINE
      -- checks to see if the file identified by 'name' has already been entered in the
      -- file list.  If not, it enters it and indicates that the open operation is
      -- underway.  If so, it waits for any previous open attempt to be resolved, then
      -- reports the outcome.
      BEGIN
      file ← fs.fileList;
      UNTIL file = NIL DO
	IF EquivalentString[file.name, name] THEN
	  BEGIN
	  SELECT file.seal FROM
	    openSeal => {file.openCount ← file.openCount + 1; RETURN[FALSE]};
	    underwaySeal => {WAIT fs.changeInOpenState; file ← fs.fileList};
	    ENDCASE => GO TO bogusList
	  END
	ELSE -- ensure pointer validity
	  SELECT file.seal FROM
	    openSeal, underwaySeal => file ← file.link;
	    ENDCASE => GO TO bogusList;
	REPEAT
	  bogusList => ERROR FileListSmashed;
	ENDLOOP;
      file ← zone.NEW[FileObject ← FileObject[
	link: fs.fileList, name: CopyString[name], fs: fs]];
      fs.fileList ← file;
      RETURN[TRUE]
      END;

    AnnounceOutcome: ENTRY PROCEDURE [
      fs: FSInstance, outcome: IFSFile.AccessFailure] = -- INLINE --
      BEGIN
      IF outcome = ok THEN file.seal ← openSeal ELSE RemoveFile[fs, file];
      BROADCAST fs.changeInOpenState;
      SELECT outcome FROM
	ok => RETURN;
	io => RETURN WITH ERROR IFSFile.Error[io];
	ENDCASE => RETURN WITH ERROR IFSFile.CantOpen[outcome];
      END;

    IF NewlyOpened[fs] THEN AnnounceOutcome[fs, openProc[file]];
    END;

  ReleaseFile: PUBLIC PROCEDURE [
    file: FileHandle, closeProc: PROCEDURE [FileHandle]] =
    BEGIN
    IF LastReference[file.fs, file] THEN {closeProc[file]; FlushFile[file]};
    END;

  PurgeFile: PUBLIC PROCEDURE [
    file: FileHandle, destroyProc: PROCEDURE [FileHandle]] =
    BEGIN
    IF LastReference[file.fs, file] THEN {destroyProc[file]; FlushFile[file]}
    ELSE ERROR IllegalDestroy;
    END;


  -- Internal Procedures --

  LastReference: ENTRY PROCEDURE [fs: FSInstance, file: FileHandle]
    RETURNS [last: BOOLEAN] =
    BEGIN
    IF file.seal ~= openSeal OR file.openCount = 0 THEN ERROR InvalidFile;
    IF (last ← file.openCount = 1) THEN file.seal ← underwaySeal
    ELSE file.openCount ← file.openCount - 1;
    RETURN[last]
    END;

  FlushFile: PROCEDURE [file: FileHandle] =
    BEGIN
    DoRemoveFile: ENTRY PROCEDURE [fs: FSInstance] = INLINE
      {RemoveFile[fs, file]; BROADCAST fs.changeInOpenState};

    DoRemoveFile[file.fs];
    zone.FREE[@file];
    END;

  RemoveFile: INTERNAL PROCEDURE [fs: FSInstance, file: FileHandle] =
    -- removes the file from the list.
    BEGIN
    IF file = fs.fileList THEN fs.fileList ← file.link
    ELSE
      BEGIN
      IF fs.fileList = NIL THEN GO TO Trouble;
      FOR prev: FileHandle ← fs.fileList, prev.link UNTIL prev.link = NIL DO
	IF prev.link = file THEN {prev.link ← file.link; EXIT};
	REPEAT FINISHED => GO TO Trouble;
	ENDLOOP;
      EXITS
	Trouble => ERROR FileNotInList;
      END;
    file.seal ← closedSeal; -- try to catch dangling references
    END;

  EquivalentString: PROCEDURE [s1, s2: LONG STRING] RETURNS [BOOLEAN] =
    BEGIN
    casebit: WORD = 40B;
    IF s1 = NIL AND s2 = NIL THEN RETURN[TRUE];
    IF s1 = NIL OR s2 = NIL OR s1.length ~= s2.length THEN RETURN[FALSE];
    FOR i: CARDINAL IN [0..s1.length) DO
      IF Inline.BITOR[s1[i], casebit] ~= Inline.BITOR[s2[i], casebit] THEN RETURN[FALSE];
      ENDLOOP;
    RETURN[TRUE]
    END;
  
  END.