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:
Russ Atkinson (RRA) May 13, 1985 8:38:24 pm PDT
Bob Hagmann March 15, 1985 4:31:45 pm PST
Schroeder, December 14, 1983 9:14 am
Levin, September 22, 1983 1:03 pm
Russ Atkinson, February 27, 1985 11:21:31 pm PST
DIRECTORY
Ascii USING [Upper],
BasicTime USING [GMT, nullGMT],
File USING [FP, Handle],
FS USING [Error, InfoProc, NameProc],
FSBackdoor USING [EntryPtr, EntryType, GuestProcs, 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],
Process USING [CheckForAbort],
Rope USING [Cat, Flatten, Index, ROPE, Substr, Text];
FSMainImpl2: CEDAR PROGRAM
IMPORTS Ascii, FS, FSBackdoor, FSDir, FSFileOps, FSLock, FSName, FSRemoteFile, FSReport, Process, 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;
IF FSBackdoor.GuestProcs.IsGuestProcess[] THEN {
FSBackdoor.GuestProcs.EnumerateForInfo[pattern, proc, wDir];
RETURN;
};
[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;
IF FSBackdoor.GuestProcs.IsGuestProcess[] THEN {
FSBackdoor.GuestProcs.EnumerateForNames[pattern, proc, wDir];
RETURN;
};
[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 FSBackdoor.GuestProcs.IsGuestProcess[] THEN RETURN FSBackdoor.GuestProcs.FileInfo[name, wantedCreatedTime, remoteCheck, wDir];
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;
IF FSBackdoor.GuestProcs.IsGuestProcess[] THEN {
FSBackdoor.GuestProcs.SetKeep[name, keep, wDir];
}
ELSE {
[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, "No keeps for 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
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];
Process.CheckForAbort[];
RRA: this allows us to abort during an enumeration, which is non-trivial
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.
Bob Hagmann March 15, 1985 4:19:21 pm PST
changes to: DIRECTORY
Bob Hagmann March 15, 1985 4:23:07 pm PST
changes to: DIRECTORY, EnumerateForInfo, EnumerateForNames, FileInfo, END, SetKeep