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]];
};