<> <> <> <> <> <> DIRECTORY Ascii USING [Upper], BasicTime USING [GMT, nullGMT], File USING [FP, Handle], FS USING [Error, InfoProc, NameProc], FSBackdoor USING [EntryPtr, EntryType, 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 [IsLocal, ParseCacheName, ParseClientName, ParsedFName, ServerAndFileRopes, VersionInfo], FSRemoteFile USING [EnumerateForInfo, EnumerateForNames, Info], FSReport USING [LockConflict, ReportRemote, UnknownFile, UnknownVolume, UnknownVolumeLName, VersionSpecified], Rope USING [Cat, Flatten, Index, Length, ROPE, Substr, Text]; FSMainImpl2: CEDAR PROGRAM IMPORTS Ascii, FS, FSBackdoor, FSDir, FSFileOps, FSLock, FSName, FSRemoteFile, FSReport, Rope EXPORTS FS, FSBackdoor = BEGIN <> EnumerateForInfo: PUBLIC PROC [pattern: Rope.ROPE, proc: FS.InfoProc, wDir: Rope.ROPE] = BEGIN pp: FSName.ParsedFName; vI: FSName.VersionInfo; [pp, vI] _ FSName.ParseClientName[pattern, wDir, TRUE, TRUE]; IF FSName.IsLocal[pp.nameBody] THEN BEGIN fullFName, attachedTo: Rope.ROPE; fp: File.FP; created: BasicTime.GMT; bytes: INT; keep: CARDINAL; Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED BEGIN fullFName _ FSBackdoor.MakeFName [FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ]; WITH e: entry^ SELECT FROM local => BEGIN keep _ e.keep; fp _ e.fp; attachedTo _ NIL; END; attached => BEGIN keep _ e.keep; created _ e.created; attachedTo _ FSBackdoor.TextFromTextRep[@entry[e.attachedTo]]; END; ENDCASE => ERROR; RETURN [accept: TRUE, stop: FALSE]; END; Accept: PROC RETURNS [stop: BOOLEAN] = BEGIN IF attachedTo = NIL THEN BEGIN ENABLE FS.Error => IF error.code = $badFP THEN GOTO skip; [bytes: bytes, created: created] _ FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ]; END ELSE BEGIN [fullFName: attachedTo, bytes: bytes] _ FileInfo[attachedTo, created, TRUE, NIL ! FS.Error => {bytes _ -1; CONTINUE} ]; END; RETURN [ NOT proc[fullFName, attachedTo, created, bytes, keep] ]; EXITS skip => RETURN[FALSE]; END; IF pp.volDesc = NIL THEN UnknownVolumePattern[pp.fullName]; Enumerate[pp.volDesc.vName, pp.nameBody, TRUE, (vI = missing OR vI = bangStar), pp.version, Match, Accept]; END ELSE BEGIN server, rp: Rope.ROPE; [server, rp] _ FSName.ServerAndFileRopes[pp.fullName]; FSRemoteFile.EnumerateForInfo[server, rp, proc]; END; END; EnumerateForNames: PUBLIC PROC [pattern: Rope.ROPE, proc: FS.NameProc, wDir: Rope.ROPE] = BEGIN pp: FSName.ParsedFName; vI: FSName.VersionInfo; [pp, vI] _ FSName.ParseClientName[pattern, wDir, TRUE, TRUE]; IF FSName.IsLocal[pp.nameBody] THEN BEGIN fullFName: Rope.ROPE; Match: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED BEGIN fullFName _ FSBackdoor.MakeFName [ FSBackdoor.TextFromTextRep[ @entry[entry.nameBody] ], entry.version, pp.volDesc.prefix ]; RETURN [accept: TRUE, stop: FALSE]; END; Accept: PROC RETURNS [stop: BOOLEAN] = { RETURN [ NOT proc[fullFName] ] }; IF pp.volDesc = NIL THEN UnknownVolumePattern[pp.fullName]; Enumerate[pp.volDesc.vName, pp.nameBody, TRUE, (vI = missing OR vI = bangStar), pp.version, Match, Accept]; END ELSE BEGIN server, rp: Rope.ROPE; [server, rp] _ FSName.ServerAndFileRopes[pp.fullName]; FSRemoteFile.EnumerateForNames[server, rp, proc]; END; END; FileInfo: PUBLIC PROC [name: Rope.ROPE, wantedCreatedTime: BasicTime.GMT, remoteCheck: BOOLEAN, wDir: Rope.ROPE] RETURNS [fullFName, attachedTo: Rope.ROPE, keep: CARDINAL, bytes: INT, created: BasicTime.GMT] = BEGIN pn: FSName.ParsedFName; localName, localCheck: BOOLEAN; [pn, ] _ FSName.ParseClientName[name, wDir, TRUE]; localName _ FSName.IsLocal[pn.nameBody]; IF pn.volDesc = NIL THEN BEGIN -- is no system volume IF localName THEN FSReport.UnknownVolumeLName[pn.fullName] ELSE localCheck _ FALSE; END ELSE localCheck _ (localName OR NOT remoteCheck OR wantedCreatedTime # BasicTime.nullGMT OR pn.version IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion)); IF localCheck THEN BEGIN -- 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 => BEGIN ENABLE FS.Error => FSLock.ReleaseRecord[a]; [bytes: bytes, created: created] _ FSFileOps.GetProps[FSFileOps.OpenFile[pn.volDesc.vol, fp]]; FSLock.ReleaseRecord[a]; END; attached => BEGIN FSLock.ReleaseRecord[a]; bytes _ -1; IF remoteCheck THEN [fullFName: attachedTo, bytes: bytes] _ FileInfo[attachedTo, created, TRUE, NIL ! FS.Error => CONTINUE ]; END; ENDCASE => ERROR; END; IF NOT localCheck THEN BEGIN server, file: Rope.ROPE; [server, file] _ FSName.ServerAndFileRopes[pn.fullName]; [pn.version, bytes, created] _ FSRemoteFile.Info[server, file, wantedCreatedTime]; keep _ 0; END; fullFName _ FSBackdoor.MakeFName[pn.nameBody, pn.version, pn.volDesc.prefix]; END; SetKeep: PUBLIC PROC [name: Rope.ROPE, keep: CARDINAL, wDir: Rope.ROPE] = BEGIN type: FSBackdoor.EntryType; entryKeep: CARDINAL; fp: File.FP; time: BasicTime.GMT; attachedTo: Rope.Text; a: FSLock.ActiveFile; pn: FSName.ParsedFName; vI: FSName.VersionInfo; isLocal: BOOLEAN; [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, "Can't set the keep on 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 BEGIN -- change the keep IF type = local THEN BEGIN 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]; END ELSE FSDir.UpdateAttachedEntry[pn.volDesc, pn.nameBody, pn.version, keep, time, attachedTo, replace]; END; FSLock.ReleaseRecord[a]; FSDir.DoKeeps[pn.volDesc, pn.nameBody]; END; <> Enumerate: PUBLIC PROC [ volName: Rope.ROPE, nameBodyPattern: Rope.Text, localOnly, allVersions: BOOLEAN, version: FSBackdoor.Version, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN], acceptProc: PROC RETURNS [stop: BOOLEAN] ] = BEGIN Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED BEGIN name: LONG POINTER TO FSBackdoor.TextRep = @entry[entry.nameBody]; 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; END; numberMatch: BOOLEAN = 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; vDesc: FSFileOps.VolumeDesc = FSFileOps.GetVolumeDesc[volName]; IF vDesc = NIL THEN UnknownVolumePattern[nameBodyPattern]; IF Rope.Length[nameBodyPattern] = 0 THEN nameBodyPattern _ "*"; start _ Rope.Flatten[Rope.Substr[nameBodyPattern, 0, Rope.Index[nameBodyPattern, 0, "*"]]]; FSDir.EnumerateEntries[vDesc, start, matching, Matcher, acceptProc]; END; EnumerateCacheForInfo: PUBLIC PROC [proc: FSBackdoor.InfoProc, volName, pattern: Rope.ROPE] = BEGIN Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED BEGIN 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]; END; Accept: PROC RETURNS [stop: BOOLEAN] = BEGIN [bytes, keep, created] _ FSFileOps.GetProps[ FSFileOps.OpenFile[pp.volDesc.vol, fp] ]; RETURN [ NOT proc[fullGName, created, bytes, keep] ]; END; fp: File.FP; fullGName: Rope.ROPE; created: BasicTime.GMT; bytes: INT; keep: CARDINAL; pp: FSName.ParsedFName; vI: FSName.VersionInfo; [pp, vI] _ FSName.ParseCacheName[volName, pattern, TRUE]; Enumerate[volName, pp.nameBody, FALSE, (vI = bangStar OR vI = missing), pp.version, Matcher, Accept]; END; EnumerateCacheForNames: PUBLIC PROC [proc: FSBackdoor.NameProc, volName, pattern: Rope.ROPE] = BEGIN Matcher: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] = UNCHECKED BEGIN fullGName _ FSBackdoor.MakeFName[FSBackdoor.TextFromTextRep[@entry[entry.nameBody]], entry.version]; RETURN [accept: TRUE, stop: FALSE]; END; Accept: PROC RETURNS [stop: BOOLEAN] = { RETURN [ NOT proc[fullGName] ] }; fullGName: Rope.ROPE; pp: FSName.ParsedFName; vI: FSName.VersionInfo; [pp, vI] _ FSName.ParseCacheName[volName, pattern, TRUE]; Enumerate[volName, pp.nameBody, FALSE, (vI = bangStar OR vI = missing), pp.version, Matcher, Accept]; END; Flush: PUBLIC PROC [fullGName, volName: Rope.ROPE] = BEGIN 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 BEGIN IF pn.volDesc.prefix = NIL AND a.fileLock # none THEN BEGIN -- lock conflict FSLock.ReleaseRecord[a]; FSReport.LockConflict[NIL, pn.nameBody, pn.version]; END; 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]; END; END; <> UnknownVolumePattern: PROC [pattern: Rope.ROPE] = { FSBackdoor.ProduceError[unknownVolume, Rope.Cat ["No system volume so can't match \"", pattern, "\"."] ] }; MatchResult: TYPE = {fit, compatible, clash}; <> Match: UNSAFE PROC [name: LONG POINTER TO FSBackdoor.TextRep, pattern: Rope.Text] RETURNS [MatchResult] = UNCHECKED BEGIN SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = TRUSTED BEGIN <<"1" is the pattern, "2" is the name>> WHILE len1 > 0 DO c1: CHAR = pText[i1]; IF c1 = '* THEN BEGIN -- quick kill for * at end of pattern IF len1 = 1 THEN RETURN [fit]; <> BEGIN -- 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; END; RETURN [compatible]; END; 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]; END; pText: REF TEXT = LOOPHOLE[pattern]; RETURN [SubMatch [0, pText.length, 0, name.length]]; END; END.