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