SunNFSRemoteEnumImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Demers, November 8, 1987 10:59:58 am PST
DIRECTORY
BasicTime USING [GMT, nullGMT],
FS USING [Error],
FSBackdoor USING [noVersion, Version],
FSName USING [BangVersionFile],
FSRemoteFileBackdoor USING [EnumerateForInfoProc, EnumerateForNamesProc, InfoProc],
Rope USING [Compare, Concat, Equal, IsEmpty, IsPrefix, Match, ROPE, Substr],
SunNFS USING [DirOpRes],
SunNFSRemoteFile USING [caseFileNamePrefix, caseFileNamePrefixLen, CreateNameReader, CreateNameWriter, DecodeVersionFromNFSName, EachDirEntryProc, EnumerateDirectory, GetRemoteDirChild, GetRemoteDirRoot, GMTFromSunTime, LookupThruSymLinks, NameReaderObject, NameWriterObject, ReadBaseComponent, ReadDirComponent, RemoteDirObject, ReportFSError, ReportNFSError, RetractComponent, RopeFromNameWriter, ServerHandle, UnPinRemoteDir, UnPinRemoteDirPath, VersionInfo, WriteBaseComponent, WriteDirComponent]
;
SunNFSRemoteEnumImpl: CEDAR MONITOR
IMPORTS FS, FSName, Rope, SunNFSRemoteFile
EXPORTS SunNFSRemoteFile
~ {
OPEN SunNFSRemoteFile;
Copied Types
GMT: TYPE ~ BasicTime.GMT;
NameReader: TYPE ~ REF NameReaderObject;
NameWriter: TYPE ~ REF NameWriterObject;
RemoteDirHandle: TYPE ~ REF RemoteDirObject;
ROPE: TYPE ~ Rope.ROPE;
Version: TYPE ~ FSBackdoor.Version;
Parameters
initialInfoSetSize: CARDINAL ← 16;
Sets of Info
InfoSet: TYPE ~ REF InfoSetObject;
InfoSetObject: TYPE ~ RECORD [
size: CARDINAL,
info: SEQUENCE maxSize: CARDINAL OF Info
];
maxMaxSize: CARDINAL ← (CARDINAL.LAST - SIZE[InfoSetObject[0]] - 10) / SIZE[Info]; -- the '10' is just for luck
InfoKind: TYPE ~ { caseHint, file, subdirectory };
Info: TYPE ~ RECORD [
name: ROPE, -- not including version part
kind: InfoKind,
versionNum: CARDINAL,
created: GMT,
bytes: INT
];
AddInfoToSet: PROC [set: InfoSet, info: Info] RETURNS [newSet: InfoSet] ~ {
IF set = NIL THEN { set ← NEW[InfoSetObject[initialInfoSetSize]]; set.size ← 0 };
IF set.size < set.maxSize
THEN { newSet ← set }
ELSE {
newSize: CARDINAL ~ MIN[2*set.maxSize, maxMaxSize];
newSet ← NEW[InfoSetObject[newSize]];
FOR i: CARDINAL IN [0 .. set.size) DO
newSet.info[i] ← set.info[i];
ENDLOOP;
newSet.size ← set.size;
};
newSet.info[newSet.size] ← info;
newSet.size ← newSet.size + 1;
};
SortInfoSet: PROC [set: InfoSet] ~ {
Heapsort using <nameWithoutVersion, versionNum> as key ...
left, right, p, c, cRight: CARDINAL;
temp: Info;
Less: PROC [i, j: CARDINAL] RETURNS [BOOL] ~ INLINE {
RETURN [
SELECT Rope.Compare[set.info[i].name, set.info[j].name, FALSE] FROM
less => TRUE,
greater => FALSE,
ENDCASE => (set.info[i].versionNum < set.info[j].versionNum)
];
};
Swap: PROC [i, j: CARDINAL] ~ INLINE {
temp ← set.info[i];
set.info[i] ← set.info[j];
set.info[j] ← temp;
};
IF set.size < 2 THEN RETURN;
left ← set.size/2; right ← set.size - 1;
DO
SELECT TRUE FROM
(left > 0) => { left ← left - 1 };
(right > 0) => { Swap[0, right]; right ← right - 1 };
ENDCASE => EXIT;
FOR p ← left, c DO
IF (c ← 2*p+1) > right THEN EXIT;
IF ((cRight ← c+1) <= right) AND Less[c, cRight] THEN c ← cRight;
IF Less[c, p] THEN EXIT;
Swap[p, c];
ENDLOOP;
ENDLOOP;
};
Exported to SunNFSRemoteFile
EnumerationAborted: PRIVATE ERROR ~ CODE;
SunNFSEnumerateForInfo: PUBLIC FSRemoteFileBackdoor.EnumerateForInfoProc -- [h: ServerHandle, pattern: ROPE, proc: InfoProc] -- ~ {
dH: RemoteDirHandle;
nR: NameReader;
nWPath, nWSuffix: NameWriter;
patternPrefix, patternUpToVersionPart: ROPE;
isPattern: BOOL;
versionFromPattern: Version;
vI: VersionInfo;
callDepth: CARDINAL ← 0;
EnumerateInner: PROC ~ {
set: InfoSet ← NIL;
EachName: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL ← TRUE] -- ~ {
Select names to be included, add them to set.
dirOpRes: SunNFS.DirOpRes;
IF Rope.Equal[entryName, "."] OR Rope.Equal[entryName, ".."] THEN RETURN [TRUE];
IF Rope.IsPrefix[caseFileNamePrefix, entryName, FALSE] THEN {
casedComponent, nameSuffix: ROPE;
casedComponent ← Rope.Substr[entryName, caseFileNamePrefixLen];
WriteDirComponent[nWSuffix, casedComponent]; -- Ugh! Logically, this ought to be WriteBaseComponent[...], but I don't want leading '> characters for the pattern match!
nameSuffix ← RopeFromNameWriter[nWSuffix];
IF Rope.Match[patternUpToVersionPart, nameSuffix, FALSE] THEN {
set ← AddInfoToSet[set, [name~casedComponent, kind~caseHint, versionNum~0, created~BasicTime.nullGMT, bytes~0]];
};
RetractComponent[nWSuffix];
RETURN[TRUE];
};
dirOpRes ← LookupThruSymLinks[h, dH, entryName];
SELECT dirOpRes.status FROM
ok => NULL;
perm, acces, noent => RETURN [TRUE];
ENDCASE => ReportNFSError[dirOpRes.status, h];
SELECT dirOpRes.attributes.type FROM
reg => {
nameWithoutVersion, nameSuffix: ROPE;
thisVersion: Version;
caseOK: BOOL;
[nameWithoutVersion, thisVersion, caseOK] ← DecodeVersionFromNFSName[entryName, TRUE];
IF (thisVersion = FSBackdoor.noVersion) OR (NOT caseOK) THEN RETURN[TRUE];
IF (vI # bangNumber) OR (thisVersion = versionFromPattern) THEN {
WriteDirComponent[nWSuffix, nameWithoutVersion]; -- Ugh! Logically, this ought to be WriteBaseComponent[...], but I don't want leading '> characters for the pattern match!
nameSuffix ← RopeFromNameWriter[nWSuffix];
IF Rope.Match[patternUpToVersionPart, nameSuffix, FALSE] THEN {
set ← AddInfoToSet[set, [name~nameWithoutVersion, kind~file, versionNum~thisVersion, created~GMTFromSunTime[dirOpRes.attributes.mtime], bytes~dirOpRes.attributes.size]];
};
RetractComponent[nWSuffix];
};
};
dir => {
addIt: BOOL;
thisVersion: Version;
caseOK: BOOL;
[, thisVersion, caseOK] ← DecodeVersionFromNFSName[entryName, TRUE];
SELECT TRUE FROM
((thisVersion # FSBackdoor.noVersion) OR (NOT caseOK)) => addIt ← FALSE;
(callDepth > 0) => addIt ← TRUE;
(NOT isPattern) => addIt ← FALSE;
ENDCASE => addIt ← Rope.IsPrefix[patternPrefix, entryName, FALSE];
IF addIt THEN {
set ← AddInfoToSet[set, [name~entryName, kind~subdirectory, versionNum~CARDINAL.LAST, created~BasicTime.nullGMT, bytes~0]];
};
};
ENDCASE;
};
EnumerateDirectory[h, dH, EachName, FALSE
! FS.Error => {
IF error.code = $accessDenied THEN { set ← NIL; CONTINUE } ELSE REJECT
}
];
IF set # NIL THEN {
i: CARDINAL ← 0;
latestCaseHint, thisName, casedName: ROPENIL;
SortInfoSet[set];
WHILE i < set.size DO
thisName ← set.info[i].name;
casedName ← thisName;
SELECT set.info[i].kind FROM
caseHint => {
latestCaseHint ← thisName;
i ← i + 1;
};
subdirectory => {
callDepth ← callDepth + 1;
IF latestCaseHint # NIL THEN {
SELECT Rope.Compare[latestCaseHint, thisName, FALSE] FROM
less => latestCaseHint ← NIL;
equal => casedName ← latestCaseHint;
ENDCASE;
};
WriteDirComponent[nWPath, casedName];
WriteDirComponent[nWSuffix, casedName];
[dHChild~dH] ← GetRemoteDirChild[h, dH, thisName, FALSE];
EnumerateInner[];
dH ← UnPinRemoteDir[dH];
RetractComponent[nWSuffix];
RetractComponent[nWPath];
callDepth ← callDepth - 1;
i ← i + 1;
};
file => {
it: CARDINAL;
temp: ROPE;
IF latestCaseHint # NIL THEN {
SELECT Rope.Compare[latestCaseHint, thisName, FALSE] FROM
less => latestCaseHint ← NIL;
equal => casedName ← latestCaseHint;
ENDCASE;
};
SELECT vI FROM
bangStar, bangNumber => {
If vI = number the selection by version number was done before sorting.
it ← i; i ← i + 1;
};
bangH, bangL => {
start: CARDINAL ← i;
i ← i + 1;
WHILE (i < set.size) AND (set.info[i].kind = file) AND Rope.Equal[thisName, set.info[i].name, FALSE] DO
i ← i + 1;
ENDLOOP;
it ← (IF vI = bangH THEN i-1 ELSE start);
};
ENDCASE;
temp ← FSName.BangVersionFile[casedName, [set.info[it].versionNum]];
WriteBaseComponent[nWPath, temp];
temp ← Rope.Concat["<", RopeFromNameWriter[nWPath]];
IF NOT proc[temp, set.info[it].bytes, set.info[it].created]
THEN ERROR EnumerationAborted[];
RetractComponent[nWPath];
};
ENDCASE;
ENDLOOP;
};
};
Main line code of EnumerateForInfo ...
{
ENABLE {
UNWIND => {
IF dH # NIL THEN UnPinRemoteDirPath[dH];
};
EnumerationAborted => {
CONTINUE;
};
};
nR ← CreateNameReader[pattern];
nWPath ← CreateNameWriter[];
dH ← FollowDirPathForEnumeration[h, nR, nWPath];
[base~patternUpToVersionPart, vI~vI, version~versionFromPattern, isPattern~isPattern, patternHead~patternPrefix] ← ReadBaseComponent[nR~nR, case~FALSE, stripVersion~TRUE];
SELECT vI FROM
missing => ReportFSError[illegalName, h, patternUpToVersionPart, "Enumerate, version part required"];
ENDCASE;
nWSuffix ← CreateNameWriter[];
EnumerateInner[];
};
IF dH # NIL THEN UnPinRemoteDirPath[dH];
};
FollowDirPathForEnumeration: PROC [sH: ServerHandle, nR: NameReader, nW: NameWriter] RETURNS [dH: RemoteDirHandle] ~ {
ENABLE UNWIND => {
IF dH # NIL THEN UnPinRemoteDirPath[dH];
};
caseFileName: ROPE;
component, casedComponent: ROPE;
EachDirEntry: EachDirEntryProc -- [entryName: ROPE] RETURNS [continue: BOOL ← TRUE] -- ~ {
IF Rope.Equal[entryName, caseFileName, FALSE] THEN {
casedComponent ← Rope.Substr[entryName, caseFileNamePrefixLen];
continue ← FALSE;
};
};
dH ← GetRemoteDirRoot[sH];
DO
dHParent: RemoteDirHandle ← dH;
component ← casedComponent ← ReadDirComponent[nR, FALSE];
IF Rope.IsEmpty[component] THEN EXIT;
[dHChild~dH] ← GetRemoteDirChild[sH~sH, dH~dH, childName~component, create~FALSE];
caseFileName ← Rope.Concat[caseFileNamePrefix, component];
EnumerateDirectory[sH, dHParent, EachDirEntry];
WriteDirComponent[nW, casedComponent];
ENDLOOP;
};
SunNFSEnumerateForNames: PUBLIC FSRemoteFileBackdoor.EnumerateForNamesProc -- [h: ServerHandle, pattern: ROPE, proc: NameProc] -- ~ {
This looks inefficient, but for NFS I have to Stat every file to find out whether it's a directory anyway, so ...
EachFile: FSRemoteFileBackdoor.InfoProc -- [file: ROPE, ... ] RETURNS [continue: BOOL] -- ~ {
RETURN [proc[file]];
};
SunNFSEnumerateForInfo[h, pattern, EachFile];
};
}...