-- XDFileCache.Mesa
-- Edited by:
-- Sandman on July 21, 1980 11:17 AM
-- Barbara on November 2, 1978 9:24 AM
-- Bruce on October 21, 1980 3:50 PM
-- Evans on Jun 26, 1980 1:54 PM
DIRECTORY
AltoFileDefs USING [eofDA, FP, NullFP],
DebugOps USING [],
DirectoryDefs USING [DirectoryLookup, EnumerateDirectory],
DSyms USING [SymbolLock],
Inline USING [BITAND, BITOR, COPY],
MachineDefs USING [],
SegmentDefs USING [
AccessOptions, Append, DefaultAccess, DefaultVersion, EnumerateFiles,
FileAccessError, FileHandle, FileNameError, FileObject, FindFile, InsertFile,
InvalidFP, LockFile, NewFileOnly, Object, OldFileOnly, OpenFile, Read,
ReleaseFile, SetFileAccess, UnlockFile, VersionOptions],
Segments USING [],
Storage USING [CopyString, FreeString, Node],
String USING [AppendString, EquivalentSubStrings, SubStringDescriptor],
SwapperOps USING [AllocateObject];
XDFileCache: MONITOR LOCKS DSyms.SymbolLock
IMPORTS DirectoryDefs, DSyms, Inline, SegmentDefs, Storage, String, SwapperOps
EXPORTS DebugOps, MachineDefs, Segments
SHARES SegmentDefs =
BEGIN
NullFP: AltoFileDefs.FP = AltoFileDefs.NullFP;
FileHandle: TYPE = SegmentDefs.FileHandle;
NullFileObject: SegmentDefs.FileObject = [
busy: FALSE, body: file[open: FALSE, length: FALSE, lengthvalid: FALSE,
read: FALSE, write: FALSE, append: FALSE, lengthchanged: FALSE, lock: 0,
swapcount: 0, segcount: 0, fp: NullFP]];
FCRecord: TYPE = RECORD [
name: STRING,
fp: AltoFileDefs.FP];
FCSize: CARDINAL = 20;
FCLimit: CARDINAL = FCSize-1;
FCArray: DESCRIPTOR FOR ARRAY [0..FCSize) OF FCRecord;
InitFileCache: PROCEDURE =
BEGIN
i: CARDINAL;
FCArray ← DESCRIPTOR[Storage.Node[FCSize*SIZE[FCRecord]],FCSize];
FOR i IN [0..FCSize) DO
FCArray[i] ← FCRecord[NIL,NullFP];
ENDLOOP;
cacheInvalid ← FALSE;
RETURN
END;
PromoteFCRecord: PROCEDURE [i: CARDINAL] =
BEGIN
ithFCR: FCRecord;
IF i = FCLimit THEN RETURN;
ithFCR ← FCArray[i];
Inline.COPY[to: @FCArray[i], from: @FCArray[i+1], nwords: SIZE[FCRecord]*(FCLimit-i)];
FCArray[FCLimit] ← ithFCR;
RETURN
END;
CopyFileName: PROCEDURE [name: STRING] RETURNS [copy: STRING] =
BEGIN
IF name = NIL THEN RETURN[NIL];
copy ← Storage.CopyString[name];
RETURN
END;
AddFCRecord: PROCEDURE [name: STRING, fh: FileHandle] =
BEGIN
IF FCArray[0].name # NIL THEN
Storage.FreeString[FCArray[0].name];
Inline.COPY[to: @FCArray[0], from: @FCArray[1], nwords: SIZE[FCRecord]*(FCSize-1)];
FCArray[FCLimit].name ← CopyFileName[name];
IF fh # NIL THEN FCArray[FCLimit].fp ← fh.fp
ELSE FCArray[FCLimit].fp ← NullFP;
cacheInvalid ← TRUE;
RETURN
END;
Lookup: PROC [fp: POINTER TO AltoFileDefs.FP, name: STRING] RETURNS [found: BOOLEAN] =
BEGIN
FOR i: CARDINAL DECREASING IN [0..FCSize) DO
IF FCArray[i].name = NIL THEN EXIT;
IF EquivalentFileNames[name, FCArray[i].name] THEN {
fp↑ ← FCArray[i].fp; RETURN[TRUE]};
ENDLOOP;
found ← DirectoryDefs.DirectoryLookup[fp,name,FALSE];
END;
CacheNewFile: PUBLIC PROCEDURE [
name: STRING, access: SegmentDefs.AccessOptions ← SegmentDefs.DefaultAccess,
version: SegmentDefs.VersionOptions ← SegmentDefs.DefaultVersion]
RETURNS [file: FileHandle] =
BEGIN OPEN AltoFileDefs;
i: CARDINAL;
create: BOOLEAN;
[access,version] ← ValidateOptions[access,version];
create ← Inline.BITAND[version,SegmentDefs.OldFileOnly]=0;
file ← NIL;
FOR i DECREASING IN [0..FCSize) DO
IF FCArray[i].name = NIL THEN
BEGIN AddFCRecord[name,NIL]; EXIT END;
IF EquivalentFileNames[name, FCArray[i].name] THEN
BEGIN PromoteFCRecord[i]; EXIT END;
REPEAT FINISHED => AddFCRecord[name,NIL];
ENDLOOP;
IF FCArray[FCLimit].fp.leaderDA # eofDA AND ~cacheInvalid THEN
BEGIN OPEN SegmentDefs;
file ← InsertFile[@FCArray[FCLimit].fp, access];
OpenFile[file ! InvalidFP => GOTO BadCache];
RETURN;
EXITS BadCache =>
BEGIN
IF file.segcount = 0 THEN ReleaseFile[file];
cacheInvalid ← TRUE;
END;
END;
ValidateCache[];
SELECT TRUE FROM
FCArray[FCLimit].fp.leaderDA # eofDA =>
file ← SegmentDefs.InsertFile[@FCArray[FCLimit].fp, access];
create => {file ← RealNewFile[name,access]; FCArray[FCLimit].fp ← file.fp};
ENDCASE => ERROR SegmentDefs.FileNameError[name];
RETURN
END;
FileName, NameForFile: PUBLIC PROCEDURE [name: STRING, file: FileHandle] =
BEGIN
localname: STRING ← [40];
i: CARDINAL;
BEGIN
IF cacheInvalid THEN GO TO notincache
ELSE FOR i DECREASING IN [0..FCSize) DO
IF FCArray[i].name = NIL THEN GO TO notincache;
IF FCArray[i].fp = file.fp THEN
BEGIN
String.AppendString[name,FCArray[i].name];
PromoteFCRecord[i];
RETURN
END;
REPEAT FINISHED => GO TO notincache;
ENDLOOP;
EXITS notincache => AddFCRecord[NIL,file];
END;
ValidateCache[];
IF FCArray[FCLimit].name = NIL THEN
BEGIN
FOR i DECREASING IN [1..FCSize) DO
FCArray[i] ← FCArray[i-1];
ENDLOOP;
FCArray[0] ← [NIL,NullFP];
SIGNAL SegmentDefs.InvalidFP[@file.fp]
END
ELSE String.AppendString[name,FCArray[FCLimit].name];
RETURN
END;
EquivalentFileNames: PROCEDURE [n1, n2: STRING] RETURNS [BOOLEAN] =
BEGIN
s1,s2: String.SubStringDescriptor;
s1 ← [base: n1, offset: 0,
length: n1.length - (IF n1[n1.length-1] = '. THEN 1 ELSE 0)];
s2 ← [base: n2, offset: 0,
length: n2.length - (IF n2[n2.length-1] = '. THEN 1 ELSE 0)];
RETURN[String.EquivalentSubStrings[@s1,@s2]]
END;
ModifyFile: PUBLIC ENTRY PROCEDURE [name: STRING] RETURNS [BOOLEAN] =
BEGIN ENABLE UNWIND => NULL;
cap: AltoFileDefs.FP;
okay: BOOLEAN ← TRUE;
p: POINTER TO StatItem;
CheckFHandle: PROCEDURE [file: SegmentDefs.FileHandle] RETURNS [BOOLEAN] =
BEGIN
IF (file.lock # 0 OR file.segcount # 0) AND file.fp = cap THEN
BEGIN
SegmentDefs.LockFile[file];
FOR p ← statHead, p.link UNTIL p = NIL DO
okay ← okay AND p.proc[name, file]
ENDLOOP;
SegmentDefs.UnlockFile[file];
okay ← okay AND file.lock = 0 AND file.segcount = 0;
IF okay THEN SegmentDefs.ReleaseFile[file];
RETURN[okay];
END;
RETURN[FALSE];
END;
InvalidateFileCache[];
FOR p ← statHead, p.link UNTIL p = NIL DO
okay ← okay AND p.proc[name, NIL]
ENDLOOP;
IF ~Lookup[@cap, name] THEN RETURN[TRUE];
[] ← SegmentDefs.EnumerateFiles[CheckFHandle];
RETURN[okay];
END;
StatItem: TYPE = RECORD [
link: POINTER TO StatItem, proc: PROC[STRING,FileHandle] RETURNS [BOOLEAN]];
statHead: POINTER TO StatItem ← NIL;
AddModifyProc: PUBLIC PROC [proc: PROC[STRING,FileHandle] RETURNS [BOOLEAN]] = {
p: POINTER TO StatItem = Storage.Node[SIZE[StatItem]];
p↑ ← [link: statHead, proc: proc];
statHead ← p};
cacheInvalid: BOOLEAN;
InvalidateFileCache: PUBLIC PROCEDURE = BEGIN cacheInvalid ← TRUE END;
ValidateCache: PROCEDURE =
BEGIN
i: CARDINAL;
num, found: CARDINAL ← 0;
caseBit: WORD = 40B;
CheckEntry: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, dirname: STRING]
RETURNS[BOOLEAN] =
BEGIN
fcr: POINTER TO FCRecord ← @FCArray[FCLimit];
THROUGH [0..FCSize) DO
IF fcr.name = NIL THEN
BEGIN
IF fcr.fp.leaderDA = AltoFileDefs.eofDA THEN EXIT;
IF fcr.fp = fp↑ THEN
{fcr.name ← CopyFileName[dirname]; found ← found+1};
END
ELSE
IF Inline.BITOR[fcr.name[0],caseBit] = Inline.BITOR[dirname[0],caseBit] AND EquivalentFileNames[fcr.name, dirname]
THEN
{fcr.fp ← fp↑; found ← found+1};
fcr ← LOOPHOLE[fcr - SIZE[FCRecord]];
ENDLOOP;
RETURN[found = num];
END;
IF ~cacheInvalid THEN RETURN;
FOR i IN [0..FCSize) DO
IF FCArray[i].name # NIL THEN {num ← num+1; FCArray[i].fp ← NullFP; LOOP};
IF FCArray[i].fp.leaderDA # AltoFileDefs.eofDA THEN num ← num+1;
ENDLOOP;
DirectoryDefs.EnumerateDirectory[CheckEntry];
cacheInvalid ← FALSE;
END;
RealNewFile: PROCEDURE [name: STRING, access: SegmentDefs.AccessOptions]
RETURNS [file: FileHandle] =
BEGIN
fp: AltoFileDefs.FP;
[] ← DirectoryDefs.DirectoryLookup[@fp,name,TRUE];
IF (file ← SegmentDefs.FindFile[@fp]) = NIL THEN
BEGIN
file ← LOOPHOLE[
SwapperOps.AllocateObject[SIZE[file SegmentDefs.Object]]];
file↑ ← NullFileObject;
file.fp ← fp;
END;
SegmentDefs.SetFileAccess[file,access];
RETURN
END;
ValidateOptions: PROCEDURE [
access: SegmentDefs.AccessOptions, version: SegmentDefs.VersionOptions]
RETURNS [SegmentDefs.AccessOptions, SegmentDefs.VersionOptions] =
BEGIN OPEN SegmentDefs, Inline;
IF access = SegmentDefs.DefaultAccess THEN access ← SegmentDefs.Read;
-- IF version = DefaultVersion THEN version ← 0;
IF BITAND[version,NewFileOnly+OldFileOnly] = NewFileOnly+OldFileOnly
OR (BITAND[version,NewFileOnly]#0 AND BITAND[access,Append]=0)
THEN ERROR FileAccessError[NIL];
IF BITAND[access,Append]=0 THEN
version ← BITOR[version,OldFileOnly];
RETURN[access,version]
END;
InitFileCache[];
END...