<> <> <> <> <> <> <> <> <> <> <> 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; <> 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]; }; }; <> 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 { <> 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]; }; }; <> 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[]; <> 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}; <> 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]; <> { -- 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]; <> 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. <> <> <> <> <<>> <<>>