-- 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.