<<>> <> <> <> <> 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 [""]; r ¬ Convert.RopeFromTimeRFC822[gmt !Convert.Error => GOTO Cant]; RETURN; EXITS Cant => r ¬ IO.PutFR1["", [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] ]; }.