FSMainImpl2.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
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
Levin, September 22, 1983 1:03 pm
Russ Atkinson, October 19, 1984 11:35:39 am PDT
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
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
Exported to FS
EnumerateForInfo: PUBLIC PROC [pattern: ROPE, proc: FS.InfoProc, wDir: ROPE] = {
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseClientName[pattern, wDir, TRUE, TRUE];
IF FSName.IsLocal[pp.nameBody]
THEN {
fullFName, attachedTo: ROPE;
fp: File.FP;
created: GMT;
bytes: INT;
keep: CARDINAL;
Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
fullFName ← FSBackdoor.MakeFName [FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ];
WITH e: entry^ SELECT FROM
local => {
keep ← e.keep;
fp ← e.fp;
attachedTo ← NIL;
};
attached => {
keep ← e.keep;
created ← e.created;
attachedTo ← FSBackdoor.TextFromTextRep[@entry[e.attachedTo]];
};
ENDCASE => ERROR;
RETURN [accept: TRUE, stop: FALSE];
};
Accept: PROC RETURNS [stop: BOOL] = {
IF attachedTo = NIL
THEN {
ENABLE FS.Error => IF error.code = $badFP THEN GOTO skip;
[bytes: bytes, created: created] ← FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ];
}
ELSE {
[fullFName: attachedTo, bytes: bytes] ← FileInfo[attachedTo, created, TRUE, NIL
! FS.Error => {bytes ← -1; CONTINUE} ];
};
RETURN [ NOT proc[fullFName, attachedTo, created, bytes, keep] ];
EXITS skip => RETURN[FALSE];
};
IF pp.volDesc = NIL THEN UnknownVolumePattern[pp.fullName];
InnerEnumerate[pp.volDesc, pp.nameBody, TRUE, (vI = missing OR vI = bangStar), pp.version, Match, Accept];
}
ELSE {
server, rp: ROPE;
[server, rp] ← FSName.ServerAndFileRopes[pp.fullName];
FSRemoteFile.EnumerateForInfo[server, rp, proc];
};
};
EnumerateForNames: PUBLIC PROC [pattern: ROPE, proc: FS.NameProc, wDir: ROPE] = {
pp: FSName.ParsedFName;
vI: FSName.VersionInfo;
[pp, vI] ← FSName.ParseClientName[pattern, wDir, TRUE, TRUE];
IF FSName.IsLocal[pp.nameBody]
THEN {
fullFName: ROPE;
Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
fullFName ← FSBackdoor.MakeFName [ FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ];
RETURN [accept: TRUE, stop: FALSE];
};
Accept: PROC RETURNS [stop: BOOL] =
{ 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];
}
ELSE {
server, rp: ROPE;
[server, rp] ← FSName.ServerAndFileRopes[pp.fullName];
FSRemoteFile.EnumerateForNames[server, rp, proc];
};
};
FileInfo: PUBLIC PROC [name: ROPE, wantedCreatedTime: GMT, remoteCheck: BOOL, wDir: ROPE] RETURNS [fullFName, attachedTo: ROPE, keep: CARDINAL, bytes: INT, created: GMT] = {
pn: FSName.ParsedFName;
localName, localCheck: BOOL;
[pn, ] ← FSName.ParseClientName[name, wDir, TRUE];
localName ← FSName.IsLocal[pn.nameBody];
IF pn.volDesc = NIL
THEN { -- is no system volume
IF localName
THEN FSReport.UnknownVolumeLName[pn.fullName]
ELSE localCheck ← FALSE;
}
ELSE localCheck ← (localName OR NOT remoteCheck OR wantedCreatedTime # BasicTime.nullGMT OR pn.version IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion));
IF localCheck THEN { -- 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 => {
ENABLE FS.Error => FSLock.ReleaseRecord[a];
[bytes: bytes, created: created] ← FSFileOps.GetProps[FSFileOps.OpenFile[pn.volDesc.vol, fp]];
FSLock.ReleaseRecord[a];
};
attached => {
FSLock.ReleaseRecord[a];
bytes ← -1;
IF remoteCheck
THEN [fullFName: attachedTo, bytes: bytes] ← FileInfo[attachedTo, created, TRUE, NIL
! FS.Error => CONTINUE ];
};
ENDCASE => ERROR;
};
IF NOT localCheck THEN {
server, file: ROPE;
[server, file] ← FSName.ServerAndFileRopes[pn.fullName];
[pn.version, bytes, created] ← FSRemoteFile.Info[server, file, wantedCreatedTime];
keep ← 0;
};
fullFName ← FSBackdoor.MakeFName[pn.nameBody, pn.version, IF pn.volDesc = NIL THEN NIL ELSE pn.volDesc.prefix];
};
SetKeep: PUBLIC PROC [name: ROPE, keep: CARDINAL, wDir: ROPE] = {
type: FSBackdoor.EntryType;
entryKeep: CARDINAL;
fp: File.FP;
time: GMT;
attachedTo: Rope.Text;
a: FSLock.ActiveFile;
pn: FSName.ParsedFName;
vI: FSName.VersionInfo;
isLocal: BOOL;
[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 { -- change the keep
IF type = local
THEN {
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];
}
ELSE FSDir.UpdateAttachedEntry[pn.volDesc, pn.nameBody, pn.version, keep, time, attachedTo, replace];
};
FSLock.ReleaseRecord[a];
FSDir.DoKeeps[pn.volDesc, pn.nameBody];
};
Exported to FSBackdoor
Enumerate: PUBLIC PROC [ volName: ROPE, nameBodyPattern: Rope.Text, localOnly, allVersions: BOOL, version: FSBackdoor.Version, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL], acceptProc: PROC RETURNS [stop: BOOL] ] = {
vDesc: FSFileOps.VolumeDesc = FSFileOps.GetVolumeDesc[volName];
IF vDesc = NIL THEN FSReport.UnknownVolume[volName];
InnerEnumerate[vDesc, FSName.ConvertNamebodyPattern[nameBodyPattern], localOnly, allVersions, version, matchProc, acceptProc];
};
EnumerateCacheForInfo: PUBLIC PROC [proc: FSBackdoor.InfoProc, volName, pattern: ROPE] = {
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
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];
};
Accept: PROC RETURNS [stop: BOOL] = {
[bytes, keep, created] ← FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ];
RETURN [ NOT proc[fullGName, created, bytes, keep] ];
};
fp: File.FP;
fullGName: ROPE;
created: 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];
};
EnumerateCacheForNames: PUBLIC PROC [proc: FSBackdoor.NameProc, volName, pattern: ROPE] = {
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
fullGName ← FSBackdoor.MakeFName[FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version];
RETURN [accept: TRUE, stop: FALSE];
};
Accept: PROC RETURNS [stop: BOOL] =
{ RETURN [ NOT proc[fullGName] ] };
fullGName: 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];
};
Flush: PUBLIC PROC [fullGName, volName: ROPE] = {
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 {
IF pn.volDesc.prefix = NIL AND a.fileLock # none
THEN { -- lock conflict
FSLock.ReleaseRecord[a];
FSReport.LockConflict[NIL, pn.nameBody, pn.version];
};
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];
};
};
Internal procedures
TranslateServer: PROC [pattern: ROPE] = {
FSBackdoor.ProduceError[unknownVolume, Rope.Cat ["No system volume so can't match \"", pattern, "\"."] ] };
UnknownVolumePattern: PROC [pattern: ROPE] = {
FSBackdoor.ProduceError[unknownVolume, Rope.Cat ["No system volume so can't match \"", pattern, "\"."] ] };
InnerEnumerate: PROC [vDesc: FSFileOps.VolumeDesc, nameBodyPattern: Rope.Text, localOnly, allVersions: BOOL, version: FSBackdoor.Version, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL], acceptProc: PROC RETURNS [stop: BOOL] ] = {
Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL] = UNCHECKED {
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;
};
numberMatch: BOOL = 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];
};
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 {
SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = TRUSTED {
"1" is the pattern, "2" is the name
WHILE len1 > 0 DO
c1: CHAR = pText[i1];
IF c1 = '*
THEN { -- quick kill for * at end of pattern
IF len1 = 1 THEN RETURN [fit];
else must take all combinations
{ -- 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;
};
RETURN [compatible];
};
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];
};
pText: REF TEXT = LOOPHOLE[pattern];
RETURN [SubMatch [0, pText.length, 0, name.length]];
};
END.