FSMainImpl2.mesa
Exports FS.EnumerateForInfo, FS.EnumerateForNames, FS.FileInfo and FS.SetKeep
Exports FSBackdoor.Enumerate, FSBackdoor.EnumerateCacheForInfo, FSBackdoor.EnumerateCacheForNames and FSBackdoor.Flush
See FSMainImpl1 for FS.Open, FS.Create, FS.Copy, FS.Delete, and FS.Rename
Last Edited by: Schroeder, December 14, 1983 9:14 am
Last Edited by: Levin, September 22, 1983 1:03 pm
DIRECTORY
Ascii USING [Upper],
BasicTime USING [GMT, nullGMT],
File USING [FP, Handle],
FS USING [Error, InfoProc, NameProc],
FSBackdoor USING [EntryPtr, EntryType, highestVersion, InfoProc, lowestVersion, MakeFName, NameProc, ProduceError, TextFromTextRep, TextRep, Version],
FSDir USING [AcquireOldFName, DeleteEntry, DoKeeps, EnumerateEntries, UpdateAttachedEntry, UpdateLocalEntry, VersionMatching],
FSFileOps USING [DeleteFile, GetProps, GetVolumeDesc, OpenFile, SetKeep, VolumeDesc],
FSLock USING [ActiveFile, ReleaseRecord],
FSName USING [ConvertNamebodyPattern, IsLocal, ParseCacheName, ParseClientName, ParsedFName, ServerAndFileRopes, VersionInfo],
FSRemoteFile USING [EnumerateForInfo, EnumerateForNames, Info],
FSReport USING [LockConflict, ReportRemote, UnknownFile, UnknownVolume, UnknownVolumeLName, VersionSpecified],
Rope USING [Cat, Flatten, Index, ROPE, Substr, Text];
FSMainImpl2: CEDAR PROGRAM
IMPORTS Ascii, FS, FSBackdoor, FSDir, FSFileOps, FSLock, FSName, FSRemoteFile, FSReport, Rope
EXPORTS FS, FSBackdoor
= BEGIN
Exported to FS
EnumerateForInfo: PUBLIC PROC [pattern: Rope.ROPE, proc: FS.InfoProc, wDir: Rope.ROPE] =
BEGIN
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseClientName[pattern, wDir, TRUE, TRUE];
IF FSName.IsLocal[pp.nameBody]
THEN BEGIN
fullFName, attachedTo: Rope.ROPE;
fp: File.FP;
created: BasicTime.GMT;
bytes: INT;
keep: CARDINAL;
Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED
BEGIN
fullFName ← FSBackdoor.MakeFName [FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ];
WITH e: entry^ SELECT FROM
local => BEGIN
keep ← e.keep;
fp ← e.fp;
attachedTo ← NIL;
END;
attached => BEGIN
keep ← e.keep;
created ← e.created;
attachedTo ← FSBackdoor.TextFromTextRep[@entry[e.attachedTo]];
END;
ENDCASE => ERROR;
RETURN [accept: TRUE, stop: FALSE];
END;
Accept: PROC RETURNS [stop: BOOLEAN] =
BEGIN
IF attachedTo = NIL
THEN BEGIN
ENABLE FS.Error => IF error.code = $badFP THEN GOTO skip;
[bytes: bytes, created: created] ← FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ];
END
ELSE BEGIN
[fullFName: attachedTo, bytes: bytes] ← FileInfo[attachedTo, created, TRUE, NIL
! FS.Error => {bytes ← -1; CONTINUE} ];
END;
RETURN [ NOT proc[fullFName, attachedTo, created, bytes, keep] ];
EXITS skip => RETURN[FALSE];
END;
IF pp.volDesc = NIL THEN UnknownVolumePattern[pp.fullName];
InnerEnumerate[pp.volDesc, pp.nameBody, TRUE, (vI = missing OR vI = bangStar), pp.version, Match, Accept];
END
ELSE BEGIN
server, rp: Rope.ROPE;
[server, rp] ← FSName.ServerAndFileRopes[pp.fullName];
FSRemoteFile.EnumerateForInfo[server, rp, proc];
END;
END;
EnumerateForNames: PUBLIC PROC [pattern: Rope.ROPE, proc: FS.NameProc, wDir: Rope.ROPE] =
BEGIN
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseClientName[pattern, wDir, TRUE, TRUE];
IF FSName.IsLocal[pp.nameBody]
THEN BEGIN
fullFName: Rope.ROPE;
Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED
BEGIN
fullFName ← FSBackdoor.MakeFName [ FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ];
RETURN [accept: TRUE, stop: FALSE];
END;
Accept: PROC RETURNS [stop: BOOLEAN] =
{ RETURN [ NOT proc[fullFName] ] };
IF pp.volDesc = NIL THEN UnknownVolumePattern[pp.fullName];
InnerEnumerate[pp.volDesc, pp.nameBody, TRUE, (vI = missing OR vI = bangStar), pp.version, Match, Accept];
END
ELSE BEGIN
server, rp: Rope.ROPE;
[server, rp] ← FSName.ServerAndFileRopes[pp.fullName];
FSRemoteFile.EnumerateForNames[server, rp, proc];
END;
END;
FileInfo: PUBLIC PROC [name: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, remoteCheck: BOOLEAN, wDir: Rope.ROPE] RETURNS [fullFName, attachedTo: Rope.ROPE, keep: CARDINAL, bytes: INT, created: BasicTime.GMT] =
BEGIN
pn: FSName.ParsedFName;
localName, localCheck: BOOLEAN;
[pn, ] ← FSName.ParseClientName[name, wDir, TRUE];
localName ← FSName.IsLocal[pn.nameBody];
IF pn.volDesc = NIL
THEN BEGIN -- is no system volume
IF localName
THEN FSReport.UnknownVolumeLName[pn.fullName]
ELSE localCheck ← FALSE;
END
ELSE localCheck ← (localName OR NOT remoteCheck OR wantedCreatedTime # BasicTime.nullGMT OR pn.version IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion));
IF localCheck
THEN BEGIN -- check directory/cache
type: FSBackdoor.EntryType;
fp: File.FP;
a: FSLock.ActiveFile;
[type, pn.version, keep, fp, created, attachedTo, a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, wantedCreatedTime];
SELECT type FROM
notFound =>
IF localName
THEN FSReport.UnknownFile[pn.fullName, wantedCreatedTime]
ELSE localCheck ← FALSE; -- cause remote check to occur below
local, cached => BEGIN
ENABLE FS.Error => FSLock.ReleaseRecord[a];
[bytes: bytes, created: created] ← FSFileOps.GetProps[FSFileOps.OpenFile[pn.volDesc.vol, fp]];
FSLock.ReleaseRecord[a];
END;
attached => BEGIN
FSLock.ReleaseRecord[a];
bytes ← -1;
IF remoteCheck
THEN [fullFName: attachedTo, bytes: bytes] ← FileInfo[attachedTo, created, TRUE, NIL
! FS.Error => CONTINUE ];
END;
ENDCASE => ERROR;
END;
IF NOT localCheck
THEN BEGIN
server, file: Rope.ROPE;
[server, file] ← FSName.ServerAndFileRopes[pn.fullName];
[pn.version, bytes, created] ← FSRemoteFile.Info[server, file, wantedCreatedTime];
keep ← 0;
END;
fullFName ← FSBackdoor.MakeFName[pn.nameBody, pn.version, IF pn.volDesc = NIL THEN NIL ELSE pn.volDesc.prefix];
END;
SetKeep: PUBLIC PROC [name: Rope.ROPE, keep: CARDINAL, wDir: Rope.ROPE] =
BEGIN
type: FSBackdoor.EntryType;
entryKeep: CARDINAL;
fp: File.FP;
time: BasicTime.GMT;
attachedTo: Rope.Text;
a: FSLock.ActiveFile;
pn: FSName.ParsedFName;
vI: FSName.VersionInfo;
isLocal: BOOLEAN;
[pn, vI] ← FSName.ParseClientName[name, wDir, TRUE];
isLocal ← FSName.IsLocal[pn.nameBody];
IF isLocal AND pn.volDesc = NIL
THEN FSReport.UnknownVolumeLName[pn.fullName];
IF NOT isLocal
THEN FSBackdoor.ProduceError[noKeeps, "Can't set the keep on a GName."];
IF vI # missing
THEN FSReport.VersionSpecified[pn.fullName];
[type, pn.version, entryKeep, fp, time, attachedTo, a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, BasicTime.nullGMT];
IF type = notFound THEN FSReport.UnknownFile[pn.fullName, BasicTime.nullGMT];
IF keep # 0 AND keep # entryKeep
THEN BEGIN -- change the keep
IF type = local
THEN BEGIN
ENABLE FS.Error => FSLock.ReleaseRecord[a];
FSFileOps.SetKeep[FSFileOps.OpenFile[pn.volDesc.vol, fp], keep];
FSDir.UpdateLocalEntry[pn.volDesc, pn.nameBody, pn.version, keep, fp, replace];
END
ELSE FSDir.UpdateAttachedEntry[pn.volDesc, pn.nameBody, pn.version, keep, time, attachedTo, replace];
END;
FSLock.ReleaseRecord[a];
FSDir.DoKeeps[pn.volDesc, pn.nameBody];
END;
Exported to FSBackdoor
Enumerate: PUBLIC PROC [ volName: Rope.ROPE, nameBodyPattern: Rope.Text, localOnly, allVersions: BOOLEAN, version: FSBackdoor.Version, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN], acceptProc: PROC RETURNS [stop: BOOLEAN] ] =
BEGIN
vDesc: FSFileOps.VolumeDesc = FSFileOps.GetVolumeDesc[volName];
IF vDesc = NIL THEN FSReport.UnknownVolume[volName];
InnerEnumerate[vDesc, FSName.ConvertNamebodyPattern[nameBodyPattern], localOnly, allVersions, version, matchProc, acceptProc];
END;
EnumerateCacheForInfo: PUBLIC PROC [proc: FSBackdoor.InfoProc, volName, pattern: Rope.ROPE] =
BEGIN
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED
BEGIN
fullGName ← FSBackdoor.MakeFName[FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version];
WITH e: entry^ SELECT FROM
cached => fp ← e.fp;
ENDCASE => ERROR;
RETURN [accept: TRUE, stop: FALSE];
END;
Accept: PROC RETURNS [stop: BOOLEAN] =
BEGIN
[bytes, keep, created] ← FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ];
RETURN [ NOT proc[fullGName, created, bytes, keep] ];
END;
fp: File.FP;
fullGName: Rope.ROPE;
created: BasicTime.GMT;
bytes: INT;
keep: CARDINAL;
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseCacheName[volName, pattern, TRUE];
IF pp.volDesc = NIL THEN FSReport.UnknownVolume[volName];
InnerEnumerate[pp.volDesc, pp.nameBody, FALSE, (vI = bangStar OR vI = missing), pp.version, Matcher, Accept];
END;
EnumerateCacheForNames: PUBLIC PROC [proc: FSBackdoor.NameProc, volName, pattern: Rope.ROPE] =
BEGIN
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED
BEGIN
fullGName ← FSBackdoor.MakeFName[FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version];
RETURN [accept: TRUE, stop: FALSE];
END;
Accept: PROC RETURNS [stop: BOOLEAN] =
{ RETURN [ NOT proc[fullGName] ] };
fullGName: Rope.ROPE;
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseCacheName[volName, pattern, TRUE];
IF pp.volDesc = NIL THEN FSReport.UnknownVolume[volName];
InnerEnumerate[pp.volDesc, pp.nameBody, FALSE, (vI = bangStar OR vI = missing), pp.version, Matcher, Accept];
END;
Flush: PUBLIC PROC [fullGName, volName: Rope.ROPE] =
BEGIN
fp: File.FP;
a: FSLock.ActiveFile;
type: FSBackdoor.EntryType;
pn: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pn, vI] ← FSName.ParseCacheName[volName, fullGName, FALSE];
IF pn.volDesc = NIL THEN FSReport.UnknownVolume[volName];
[type, pn.version, , fp, , , a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, BasicTime.nullGMT];
IF type # notFound
THEN BEGIN
IF pn.volDesc.prefix = NIL AND a.fileLock # none
THEN BEGIN -- lock conflict
FSLock.ReleaseRecord[a];
FSReport.LockConflict[NIL, pn.nameBody, pn.version];
END;
FSReport.ReportRemote[startFlushing, fullGName];
FSDir.DeleteEntry[pn.volDesc, pn.nameBody, pn.version];
FSLock.ReleaseRecord[a]; -- not in directory any more, so ok to release lock
FSFileOps.DeleteFile[ FSFileOps.OpenFile[pn.volDesc.vol, fp] ];
FSReport.ReportRemote[endFlushing, fullGName];
END;
END;
Internal procedures
UnknownVolumePattern: PROC [pattern: Rope.ROPE] =
{ FSBackdoor.ProduceError[unknownVolume, Rope.Cat ["No system volume so can't match \"", pattern, "\"."] ] };
InnerEnumerate: PROC [vDesc: FSFileOps.VolumeDesc, nameBodyPattern: Rope.Text, localOnly, allVersions: BOOLEAN, version: FSBackdoor.Version, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN], acceptProc: PROC RETURNS [stop: BOOLEAN] ] =
BEGIN
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED
BEGIN
name: LONG POINTER TO FSBackdoor.TextRep = @entry[entry.nameBody];
IF localOnly AND name[0] = '[ THEN RETURN [accept: FALSE, stop: TRUE]; -- skip GNames
IF numberMatch AND (version # entry.version)
THEN RETURN[accept: FALSE, stop: FALSE] -- wrong version
ELSE SELECT Match[name, nameBodyPattern] FROM
fit => {[accept, stop] ← matchProc[entry]; RETURN};
compatible => RETURN[accept: FALSE, stop: FALSE];
clash => RETURN[accept: FALSE, stop: TRUE];
ENDCASE => ERROR;
END;
numberMatch: BOOLEAN = NOT allVersions AND
version IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion);
matching: FSDir.VersionMatching =
IF allVersions
THEN all
ELSE SELECT version FROM
FSBackdoor.lowestVersion => bangLOnly,
FSBackdoor.highestVersion => bangHOnly,
ENDCASE => all;
start: Rope.Text = Rope.Flatten[
Rope.Substr[nameBodyPattern, 0, Rope.Index[nameBodyPattern, 0, "*"]]
];
FSDir.EnumerateEntries[vDesc, start, matching, Matcher, acceptProc];
END;
MatchResult: TYPE = {fit, compatible, clash};
The match result is computed on the assumtion that name is always GE the pattern prefix (characters up to the first star). In this case, "compatible" means that it is sensible to present another name GE the current one, and "clash" means that such a name cannot "fit".
Match: UNSAFE PROC [name: LONG POINTER TO FSBackdoor.TextRep, pattern: Rope.Text] RETURNS [MatchResult] = UNCHECKED
BEGIN
SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = TRUSTED
BEGIN
"1" is the pattern, "2" is the name
WHILE len1 > 0 DO
c1: CHAR = pText[i1];
IF c1 = '*
THEN BEGIN -- quick kill for * at end of pattern
IF len1 = 1 THEN RETURN [fit];
else must take all combinations
BEGIN -- first, accept the *
j1: INT = i1 + 1;
nlen1: INT = len1 - 1;
j2: INT ← i2;
nlen2: INT ← len2;
WHILE nlen2 >= 0 DO
IF SubMatch[j1, nlen1, j2, nlen2] = fit THEN RETURN [fit];
j2 ← j2 + 1;
nlen2 ← nlen2 - 1;
ENDLOOP;
END;
RETURN [compatible];
END;
IF len2 = 0 THEN RETURN [compatible];
at this point demand an exact match in both strings
IF Ascii.Upper[c1] # Ascii.Upper[name[i2]] THEN RETURN [clash];
i1 ← i1 + 1;
len1 ← len1 - 1;
i2 ← i2 + 1;
len2 ← len2 - 1;
ENDLOOP;
RETURN [IF len2 = 0 THEN fit ELSE clash];
END;
pText: REF TEXT = LOOPHOLE[pattern];
RETURN [SubMatch [0, pText.length, 0, name.length]];
END;
END.