SearchPathViews.mesa
Copyright Ó 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer May 28, 1992 6:04 pm PDT
Implementation of search path views.
DIRECTORY Atom, Basics, BasicTime, Convert, IO, PFS, PFSBackdoor, PFSClass, PFSExtras, PFSNames, PFSNarrow, PFSPrefixMap, RedBlackTree, Rope, SearchPaths;
SearchPathViews: CEDAR MONITOR
IMPORTS Atom, BasicTime, Convert, IO, PFS, PFSBackdoor, PFSClass, PFSExtras, PFSNames, PFSNarrow, PFSPrefixMap, RedBlackTree, Rope
EXPORTS SearchPaths
= {OPEN PFSClass, SearchPaths;
OpenFileObject: PUBLIC TYPE ~ PFSClass.OpenFileObject;
FileType: TYPE ~ PFS.FileType;
Mutability: TYPE ~ PFS.Mutability;
flavor: ATOM ~ $SearchPath;
flavorName: ROPE ~ Rope.Concat["-", Atom.GetPName[flavor]];
c0: PUBLIC PFSNames.Component ¬ [name: [flavorName, 0, flavorName.Length] ];
versionKey: ATOM ~ Atom.MakeAtom["SearchPathViews uses this to test whether a later version has been loaded"];
myVal: REF UniqueID ~ NEW[UniqueID ¬ [egmt: [BasicTime.Now[], 0]]];
maintenanceProcs: MaintenanceProcs ~ NEW [MaintenanceProcsObject ¬ [
Sweep, Validate]];
procs: FileManipulationProcs ~ NEW [FileManipulationProcsObject ¬ [
delete: Delete,
enumerateForInfo: EnumerateForInfo,
enumerateForNames: EnumerateForNames,
fileInfo: FileInfo,
lookupName: LookupName,
rename: Rename,
copy: Copy,
setAttributes: SetAttributes,
setByteCountAndUniqueID: SetByteCountAndUniqueID,
setClientProperty: SetClientProperty,
getClientProperty: GetClientProperty,
enumerateClientProperties: EnumerateClientProperties,
read: Read,
write: Write,
open: Open,
close: Close,
store: Store,
retrieve: Retrieve,
attach: Attach,
getInfo: GetInfo,
pfsNameToUnixName: PFSNameToUnixName,
caseSensitive: CaseSensitive]];
fsh: FSHandle ~ NEW [FSObject ¬ [
flavor: flavor,
name: flavorName,
maintenanceProcs: maintenanceProcs,
procs: procs,
data: NIL]];
DefNode: TYPE ~ REF DefNodePrivate;
DefNodePrivate: TYPE ~ RECORD [server: ROPE, def: Def];
Def: TYPE ~ REF DefPrivate;
DefPrivate: TYPE ~ RECORD [
search: SearchPath,
defined: BasicTime.GMT,
case: BOOL];
defs: RedBlackTree.Table ~ RedBlackTree.Create[GetDefKey, CompareDefs];
GetDefKey: PROC [data: REF ANY] RETURNS [REF ANY] ~ {RETURN [data]};
CompareDefs: PROC [k, data: REF ANY] RETURNS [Basics.Comparison] ~ {
k1: ROPE ~ WITH k SELECT FROM
x: ROPE => x, x: DefNode => x.server, ENDCASE => ERROR;
k2: ROPE ~ WITH data SELECT FROM
x: ROPE => x, x: DefNode => x.server, ENDCASE => ERROR;
RETURN Rope.Compare[k1, k2, FALSE]};
SetPath: PUBLIC PROC [name: ROPE, search: SearchPath] ~ {
def: Def ~ NEW [DefPrivate ¬ [search, BasicTime.Now[], FALSE]];
FOR sp: SearchPath ¬ search, sp.rest WHILE sp#NIL DO
IF PFS.CaseSensitive[sp.first] THEN {def.case ¬ TRUE; EXIT};
ENDLOOP;
EntrySetPath[name, def];
RETURN};
EntrySetPath: ENTRY PROC [name: ROPE, def: Def] ~ {
ENABLE UNWIND => NULL;
dn: DefNode ¬ NARROW[defs.Lookup[name]];
IF dn=NIL THEN {
dn ¬ NEW [DefNodePrivate ¬ [name, def]];
defs.Insert[dn, name];
RETURN}
ELSE {dn.def ¬ def; RETURN}};
GetScratchPath: PUBLIC PROC [base: ROPE, search: SearchPath] RETURNS [name: ROPE] ~ {
def: Def ~ NEW [DefPrivate ¬ [search, BasicTime.Now[], FALSE]];
FOR sp: SearchPath ¬ search, sp.rest WHILE sp#NIL DO
IF PFS.CaseSensitive[sp.first] THEN {def.case ¬ TRUE; EXIT};
ENDLOOP;
RETURN EntryGetScratchPath[base, def]};
EntryGetScratchPath: ENTRY PROC [base: ROPE, def: Def] RETURNS [name: ROPE] ~ {
ENABLE UNWIND => NULL;
DO
i: INT ~ scratchCount ¬ scratchCount+1;
name ¬ IO.PutFR["%g-%g", [rope[base]], [integer[i]] ];
IF defs.Lookup[name]=NIL THEN {
dn: DefNode ~ NEW [DefNodePrivate ¬ [name, def]];
defs.Insert[dn, name];
RETURN};
ENDLOOP};
scratchCount: INT ¬ 0;
GetPath: PUBLIC PROC [name: ROPE] RETURNS [search: SearchPath] ~ {
dn: DefNode ~ NARROW[defs.Lookup[name]];
IF dn=NIL THEN PFS.Error[[environment, $SearchPathNotFound, IO.PutFR1["search path %g not found", [rope[name]] ], name]];
RETURN [dn.def.search]};
DeletePath: PUBLIC ENTRY PROC [name: ROPE] RETURNS [had: BOOL]
~ {RETURN [defs.Delete[name]#NIL]};
ConsSearchDir: PUBLIC PROC [pathName: ROPE] RETURNS [--absolute directory--PATH] ~ {
c1: PFSNames.Component ~ [name: [pathName, 0, pathName.Length]];
RETURN [PFSNames.ConstructName[LIST[c0, c1], TRUE, TRUE]]};
ConsFileSearch: PUBLIC PROC [pathName: ROPE, short: PFSNames.Component] RETURNS [PATH] ~ {
c1: PFSNames.Component ~ [name: [pathName, 0, pathName.Length]];
RETURN [PFSNames.ConstructName[LIST[c0, c1, short], TRUE, FALSE]]};
GetHandle: PROC [fs: ROPE, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE] ~ {RETURN [fsh, NIL]};
Delete: PROCEDURE [h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.NameConfirmProc] ~ {
path: PATH;
uid: UniqueID;
[underName: path, uniqueID: uid] ¬ Find[h, file, wantedUniqueID, [lowest], TRUE, FALSE];
IF proc=NIL OR proc[path, uid] THEN PFS.Delete[path, uid, NIL];
RETURN};
EnumerateForInfo: PROC [h: FSHandle, pattern: PATH, proc: PFS.InfoProc, lbound: PATH, hbound: PATH] ~ {
XfmInfo: PROC [fullFName, underDir, underName: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType] RETURNS [continue: BOOL ¬ TRUE]
~ {RETURN proc[fullFName, NIL, uniqueID, bytes, mutability, fileType]};
Enumerate[h, pattern, XfmInfo, lbound, hbound];
RETURN};
Enumerate: PROC [h: FSHandle, pattern: PATH, proc: PROC [PATH, PATH, PATH, UniqueID, INT, Mutability, FileType] RETURNS [continue: BOOL ¬ TRUE], lbound: PATH, hbound: PATH] ~ {
patFirst, patRest, lbRest, hbRest: PATH ¬ NIL;
d: Def;
server, spatpre: ROPE;
serverIsPat, xfmLB, xfmHB, stop: BOOL ¬ FALSE;
[server, d, serverIsPat, spatpre] ¬ FindDef[h, pattern, TRUE, TRUE];
SELECT TRUE FROM
pattern.ComponentCount=2 => {
prelen: INT ~ spatpre.Length;
dn: DefNode ¬ NARROW[defs.Lookup[spatpre]];
patDir: BOOL ~ pattern.IsADirectory[];
IF dn=NIL THEN dn ¬ NARROW[defs.LookupNextLarger[spatpre]];
WHILE dn#NIL AND Rope.EqualSubstrs[dn.server, 0, prelen, spatpre, 0, prelen, FALSE] DO
c1: PFSNames.Component ~ [name: [dn.server, 0, dn.server.Length]];
sn: PATH ~ PFSNames.ConstructName[LIST[c0, c1], TRUE, patDir];
IF Rope.Match[server, dn.server, FALSE] AND NOT proc[sn, NIL, NIL, [egmt: [dn.def.defined, 0]], 0, mutable, PFS.tDirectory] THEN RETURN;
dn ¬ NARROW[defs.LookupNextLarger[dn.server]];
ENDLOOP;
RETURN};
pattern.ComponentCount[] > 2 => {
patRest ¬ pattern.SubName[start: 2, directory: pattern.IsADirectory[] ];
patFirst ¬ pattern.SubName[count: 2, absolute: pattern.IsAbsolute[] ];
IF lbound#NIL THEN {
[xfmLB, lbRest] ¬ patFirst.IsAPrefix[lbound];
IF NOT xfmLB THEN SELECT lbound.Compare[patFirst, d.case] FROM
less, equal => NULL;
greater => RETURN;
ENDCASE => ERROR};
IF hbound#NIL THEN {
[xfmHB, hbRest] ¬ patFirst.IsAPrefix[hbound];
IF NOT xfmHB THEN SELECT patFirst.Compare[hbound, d.case] FROM
less => NULL;
equal, greater => RETURN;
ENDCASE => ERROR};
patFirst ¬ patFirst.ReplaceComponent[0, c0];
FOR sp: SearchPath ¬ d.search, sp.rest WHILE sp#NIL AND NOT stop DO
nThis: INT ~ sp.first.ComponentCount[];
patThis: PATH ~ PFSNames.Cat[sp.first, patRest];
lbThis: PATH ~ IF xfmLB THEN PFSNames.Cat[sp.first, lbRest] ELSE NIL;
hbThis: PATH ~ IF xfmHB THEN PFSNames.Cat[sp.first, hbRest] ELSE NIL;
PassInfo: PROC [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType] RETURNS [continue: BOOL] ~ {
attachedTo ¬ fullFName;
fullFName ¬ ReplacePrefix[fullFName, nThis, patFirst].SetVersionNumber[[none]];
stop ¬ (NOT proc[fullFName, sp.first, attachedTo, uniqueID, bytes, mutability, fileType]) OR stop;
RETURN [NOT stop]};
PFS.EnumerateForInfo[patThis, PassInfo, lbThis, hbThis];
ENDLOOP;
RETURN};
ENDCASE => ERROR};
EnumerateForNames: PROC [h: FSHandle, pattern: PATH, proc: PFS.NameProc, lbound: PATH, hbound: PATH] ~ {
StripInfo: PROC [fullFName, underDir, underName: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType] RETURNS [continue: BOOL] ~ {
RETURN proc[fullFName]};
Enumerate[h, pattern, StripInfo, lbound, hbound];
RETURN};
Find: PROC [h: FSHandle, file: PATH, wantedUniqueID: UniqueID, defaultVersion: Version, err, dirOK: BOOL] RETURNS [fullFName, underDir, underName: PATH ¬ NIL, uniqueID: UniqueID, bytes: INT ¬ 0, mutability: Mutability ¬ mutable, fileType: FileType ¬ PFS.tUnspecified] ~ {
ncl: PFSNames.Component ¬ file.ShortName[];
seenOne: BOOL ¬ FALSE;
anyUid: BOOL ~ wantedUniqueID=PFS.nullUniqueID;
CatchInfo: PROC [fullFNameX, underDirX, underNameX: PATH, uniqueIDX: UniqueID, bytesX: INT, mutabilityX: Mutability, fileTypeX: FileType] RETURNS [continue: BOOL] ~ {
IF anyUid OR wantedUniqueID=uniqueIDX THEN {
seenOne ¬ TRUE;
fullFName ¬ fullFNameX;
underDir ¬ underDirX;
underName ¬ underNameX;
uniqueID ¬ uniqueIDX;
bytes ¬ bytesX;
mutability ¬ mutabilityX;
fileType ¬ fileTypeX;
};
RETURN [NOT seenOne]};
IF file.ComponentCount[] < (IF dirOK THEN 2 ELSE 3) THEN PFSBackdoor.ProduceError[illegalName, IO.PutFR1["not enough components in search path file name %g", [rope[FmtName[h, file]]] ]];
SELECT ncl.version.versionKind FROM
numeric, lowest, highest, next => NULL;
none => file ¬ file.SetVersionNumber[defaultVersion];
all => PFSBackdoor.ProduceError[patternNotAllowed, Rope.Cat["version pattern not allowed (", FmtName[h, file], ")"]];
ENDCASE => ERROR;
Enumerate[h, file, CatchInfo, NIL, NIL];
IF err AND NOT seenOne THEN PFSBackdoor.ProduceError[unknownFile, Rope.Cat["no match for ", FmtName[h, file], IF NOT anyUid THEN Rope.Concat[" of ", FmtTime[wantedUniqueID.egmt.gmt]] ELSE NIL]];
RETURN};
FileInfo: PROC [h: FSHandle, file: PATH, wantedUniqueID: UniqueID] RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: Mutability, fileType: FileType] ~ {
full: PATH;
attachedTo ¬ NIL;
[full,,, uniqueID, bytes, mutability, fileType] ¬ Find[h, file, wantedUniqueID, [highest], TRUE, TRUE];
version ¬ full.ShortName[].version;
RETURN};
LookupName: PROC [h: FSHandle, file: PATH] RETURNS[PATH]
~ {RETURN [Find[h, file, PFS.nullUniqueID, [highest], TRUE, TRUE].fullFName]};
Rename: PROC [h: FSHandle, fromFile: PATH, wantedUniqueID: UniqueID, toFile: PATH, createOptions: PFS.CreateOptions, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] ~ { --PFSImpl of March 20, 1992 confims that toFile is relative to this FS
full, realDir, realFromPath, realToPath: PATH;
realFromUid: UniqueID;
[fullFName: full, underDir: realDir, underName: realFromPath, uniqueID: realFromUid] ¬ Find[h, fromFile, wantedUniqueID, [highest], TRUE, FALSE];
IF proc=NIL OR proc[full, realFromUid] THEN {
realToPath ¬ toFile.Replace[0, 2, realDir];
PFS.Rename[realFromPath, realToPath, realFromUid, createOptions, NIL];
};
RETURN [TRUE]};
Copy: PROC [h: FSHandle, fromFile: PATH, wantedUniqueID: UniqueID, toFile: PATH, createOptions: PFS.CreateOptions, proc: PFS.NameConfirmProc] RETURNS [done: BOOL ¬ FALSE] ~ { --PFSImpl of March 20, 1992 confims that toFile is relative to this FS
realDir, realFromPath, realToPath: PATH;
realFromUid: UniqueID;
XfmConfirm: PROC [fullName: PATH, uniqueID: UniqueID] RETURNS [continue: BOOL ¬ FALSE] ~ {RETURN proc[realToPath.SetVersionNumber[[none]], uniqueID]};
[underDir: realDir, underName: realFromPath, uniqueID: realFromUid] ¬ Find[h, fromFile, wantedUniqueID, [highest], TRUE, FALSE];
realToPath ¬ toFile.Replace[0, 2, realDir];
PFS.Copy[realFromPath, realToPath, realFromUid, createOptions, XfmConfirm];
RETURN [TRUE]};
Open: PROC [h: FSHandle, file: PATH, wantedUniqueID: UniqueID, access: PFS.AccessOptions, checkFileType: BOOL, fileType: FileType, createOptions: PFS.CreateOptions] RETURNS [of: OpenFile] ~ {
realPath: PATH;
realUid: UniqueID ¬ wantedUniqueID;
of ¬ NEW [OpenFileObject];
SELECT access FROM
read, write, append => {
realPath: PATH;
of.fs ¬ h;
[fullFName: of.fullFName, underName: realPath, uniqueID: of.uniqueID, bytes: of.bytes, mutability: of.mutability, fileType: of.fileType] ¬ Find[h, file, wantedUniqueID, [highest], TRUE, FALSE];
of.attachedTo ¬ NIL;
of.state ¬ open;
of.data ¬ PFS.Open[realPath, access, of.uniqueID, checkFileType, fileType, createOptions];
};
create => {
realPath: PATH ~ ThisMapForWrite[h, file];
osub: REF ANY ~ PFS.Open[realPath, access, wantedUniqueID, checkFileType, fileType, createOptions];
sub: OpenFile ~ NARROW[osub];
of^ ¬ [fs: h, fullFName: file.ReplaceComponent[0, c0].SetVersionNumber[sub.fullFName.ShortName[].version], attachedTo: sub.fullFName, uniqueID: sub.uniqueID, bytes: sub.bytes, mutability: sub.mutability, fileType: sub.fileType, access: sub.access, state: sub.state, data: sub];
};
ENDCASE => ERROR;
RETURN};
SetAttributes: PROC [h: FSHandle, file: OpenFile, attributes: PFS.CreateOptions] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
PFS.SetAttributes[sub, attributes];
RETURN};
SetByteCountAndUniqueID: PROC [h: FSHandle, file: OpenFile, bytes: INT, uniqueID: PFS.UniqueID] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
PFS.SetByteCountAndUniqueID[sub, bytes, uniqueID];
RETURN};
SetClientProperty: PROC [h: FSHandle, file: OpenFile, propertyName: ROPE, propertyValue: ROPE] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
PFS.SetClientProperty[sub, propertyName, propertyValue];
RETURN};
GetClientProperty: PROC [h: FSHandle, file: OpenFile, propertyName: ROPE] RETURNS [propertyValue: ROPE] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
RETURN PFS.GetClientProperty[sub, propertyName]};
EnumerateClientProperties: PROC [h: FSHandle, file: OpenFile, proc: PFS.PropProc] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
PFS.EnumerateClientProperties[sub, proc];
RETURN};
Read: UNSAFE PROC [h: FSHandle, file: OpenFile, filePosition, nBytes: CARD, toPtr: POINTER, toStart: CARD] RETURNS [bytesRead: INT] ~ UNCHECKED {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
RETURN PFS.Read[sub, filePosition, nBytes, toPtr, toStart]};
Write: PROC [h: FSHandle, file: OpenFile, filePosition, nBytes: CARD, fromPtr: POINTER, fromStart: CARD] RETURNS [bytesWritten: INT] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
tsub: OpenFile ~ NARROW[file.data];
bytesWritten ¬ PFS.Write[sub, filePosition, nBytes, LOOPHOLE[fromPtr], fromStart];
file.bytes ¬ tsub.bytes;
RETURN};
Close: PROC [h: FSHandle, file: OpenFile, abort: BOOL] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
file.state ¬ closed;
PFSExtras.NewClose[sub, abort];
RETURN};
Store: PROC [h: FSHandle, file: PATH, wantedUniqueID: UniqueID, str: IO.STREAM, proc: PFS.StoreConfirmProc, createOptions: PFS.CreateOptions] ~ {
underName: PATH ~ ThisMapForWrite[h, file];
fullName: PATH ~ file.ReplaceComponent[0, c0].SetVersionNumber[[none]];
[--per discussion with Carl Hauser on May 28, 1992--] ¬ proc[fullName];
PFS.Store[underName, wantedUniqueID, str, NIL, createOptions];
RETURN};
Retrieve: PROC [h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.RetrieveConfirmProc, checkFileType: BOOL ¬ FALSE, fileType: FileType] ~ {
full, realName: PATH;
realUid: UniqueID;
realType: FileType;
XfmConfirm: PROC[fullFName: PATH, bytes: INT, uniqueID: UniqueID] RETURNS [IO.STREAM] ~ {
RETURN proc[full, bytes, uniqueID]};
[fullFName: full, underName: realName, uniqueID: realUid, fileType: realType] ¬ Find[h, file, wantedUniqueID, [highest], TRUE, FALSE];
IF checkFileType AND fileType#realType THEN PFSBackdoor.ProduceError[fileTypeMismatch, IO.PutFR["file %g has type %g instead, not %g", [rope[PFS.RopeFromPath[realName]]], [cardinal[realType]], [cardinal[fileType]] ]];
PFS.Retrieve[realName, realUid, XfmConfirm];
RETURN};
Attach: PROC [h: FSHandle, file: PATH, to: PATH, keep: CARDINAL, wantedUniqueID: UniqueID, remoteCheck: BOOL ¬ TRUE] RETURNS [toFName: PATH] ~ {
PFSBackdoor.ProduceError[accessDenied, Rope.Concat["Can't mutate ", FmtName[h, file]]]};
GetInfo: PROC [h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType] ~ {
sub: PFS.OpenFile ~ PFSNarrow.ToOpenFile[file.data];
fullFName ¬ file.fullFName;
attachedTo ¬ NIL;
[uniqueID: uniqueID, bytes: bytes, mutability: mutability, fileType: fileType] ¬ PFS.GetInfo[sub];
RETURN};
PFSNameToUnixName: PROC [h: FSHandle, file: PATH] RETURNS [ROPE] ~ {
under: PATH ~ Find[h, file, PFS.nullUniqueID, [highest], TRUE, FALSE].underName;
RETURN PFS.PFSNameToUnixName[under]};
CaseSensitive: PROC [h: FSHandle, file: PATH] RETURNS [BOOL] ~ {
def: Def ~ FindDef[h, file, TRUE, FALSE].def;
RETURN [def.case]};
Sweep: PROC [h: FSHandle, seconds: CARD] ~ {RETURN};
Validate: PROC [h: FSHandle] RETURNS [obsolete: BOOL, downMsg: ROPE]
~ {RETURN [Atom.GetProp[versionKey, versionKey]#myVal, NIL]};
ThisMapForWrite: PROC [h: FSHandle, file: PATH] RETURNS [destPath: PATH] ~ {
def: Def ~ FindDef[h, file, FALSE, FALSE].def;
destPath ¬ PFSNames.Replace[file, 0, 2, def.search.first];
RETURN};
FindDef: PROC [h: FSHandle, file: PATH, dirOk, dirPatOk: BOOL] RETURNS [server: ROPE, def: Def, serverIsPat: BOOL, spatpre: ROPE] ~ {
c1: PFSNames.Component;
dn: DefNode;
starpos: INT;
minlen: INT ~ IF dirOk THEN 2 ELSE 3;
IF file.ComponentCount[] < minlen THEN PFSBackdoor.ProduceError[illegalName, IO.PutFR["the file name or pattern %g is invalid because it doesn't have at least %g components", [rope[FmtName[h, file]]], [integer[minlen]] ], file];
c1 ¬ file.Fetch[1];
server ¬ c1.ComponentRope[];
starpos ¬ server.Find["*"];
IF starpos >=0 THEN {
IF NOT dirPatOk THEN PFSBackdoor.ProduceError[patternNotAllowed, IO.PutFR1["pattern not allowed (in search path name part of %g)", [rope[FmtName[h, file]]] ]];
RETURN [server, NIL, TRUE, server.Substr[len: starpos]]};
dn ¬ NARROW[defs.Lookup[server]];
SELECT TRUE FROM
dn#NIL => RETURN [dn.server, dn.def, FALSE, dn.server];
dirPatOk => RETURN [server, NIL, TRUE, server];
ENDCASE => PFS.Error[[environment, $SearchPathNotFound, IO.PutFR1["search path %g not found", [rope[server]] ], server]]};
ReplacePrefix: PROC [name: PATH, len: INT, with: PATH] RETURNS [PATH] ~ {
RETURN with.Cat[name.SubName[start: len, directory: name.IsADirectory[]]]};
FmtName: PROC [h: FSHandle, file: PATH] RETURNS [r: ROPE] ~ {
ENABLE PFS.Error => {r ¬ "!err in formatting name!"; CONTINUE};
file ¬ file.ReplaceComponent[0, c0];
r ¬ PFS.RopeFromPath[file];
RETURN};
FmtTime: PROC [gmt: BasicTime.GMT] RETURNS [r: ROPE] ~ {
IF gmt = BasicTime.nullGMT THEN RETURN ["<null GMT>"];
r ¬ Convert.RopeFromTimeRFC822[gmt !Convert.Error => GOTO Cant];
RETURN;
EXITS Cant => r ¬ IO.PutFR1["<unprintable time %xH>", [cardinal[LOOPHOLE[gmt]]]]};
Atom.PutProp[versionKey, versionKey, myVal];
PFSClass.Register[flavor, GetHandle];
[] ¬ PFSPrefixMap.Insert[
prefix: PFS.PathFromRope["/path"],
translation: PFSNames.ConstructName[LIST[c0], TRUE, FALSE] ];
}.