DIRECTORY BasicTime USING [GMT, nullGMT, Period], CommandTool USING [ArgumentVector, Failed, Parse], Commander USING [CommandProc, Handle, Register], FS USING [Delete, Error, ErrorDesc, FileInfo], FSBackdoor USING [EntryPtr, Enumerate, Flush, highestVersion, MakeFName, noVersion, TextFromTextRep, Version], FSName USING [BangVersionFile, ServerAndFileRopes, VersionFromRope], FSRemoteFile USING [Info], IO USING [PutF, STREAM], Process USING [CheckForAbort], Rope USING [Fetch, Find, Flatten, Length, ROPE, Substr]; Ferret: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommandTool, FS, FSBackdoor, FSName, FSRemoteFile, IO, Process, Rope = { ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Ferret: Commander.CommandProc = { out: STREAM _ cmd.out; attachedSwitch: BOOL _ TRUE; cachedSwitch: BOOL _ TRUE; deleteSwitch: BOOL _ FALSE; flushSwitch: BOOL _ FALSE; rootName: ROPE; localName: ROPE; -- NIL => Checking cached file remoteName: ROPE; thisVersion: FSBackdoor.Version; createWanted: BasicTime.GMT; CheckFile: PROC RETURNS [stop: BOOL] = { versionFound: FSBackdoor.Version; bytesFound: INT; createFound: BasicTime.GMT _ BasicTime.nullGMT; server, fileName: ROPE; fsError: FS.ErrorDesc _ [ok, NIL, NIL]; stop _ AbortRequested[cmd]; [server: server, file: fileName] _ FSName.ServerAndFileRopes[remoteName]; [versionFound, bytesFound, createFound] _ FSRemoteFile.Info[ server, fileName, createWanted ! FS.Error => { fsError _ error; CONTINUE; } ]; IF fsError.group # ok THEN { IO.PutF[out, "\n"]; IF localName # NIL THEN { -- Attached file SELECT fsError.code FROM $illegalName, $unknownFile, -- Remote file doesn't exist at all (no versions) $unknownCreatedTime => { -- Remote file exists, but not the desired versionStamp fullFName: ROPE _ NIL; created: BasicTime.GMT; IO.PutF[out, "Attached file missing from server.\n %G => %G\n", [rope[localName]], [rope[remoteName]]]; [fullFName: fullFName, created: created] _ FS.FileInfo[ name: rootName, remoteCheck: TRUE, wDir: "///" ! FS.Error => CONTINUE]; IF fullFName # NIL THEN { SELECT BasicTime.Period[to: created, from: createWanted] FROM > 0 => { IO.PutF[out, " Newer local version is %G\n", [rope[fullFName]]]; deleted _ deleted.SUCC; IF deleteSwitch THEN { IO.PutF[out, " Deleting %G ...", [rope[localName]]]; FS.Delete[localName]; IO.PutF[out, " ok.\n"]; } ELSE IO.PutF[out, " Would have deleted %G.\n", [rope[localName]]]; RETURN; }; = 0 => { targetVersion: FSBackdoor.Version _ ExtractVersion[remoteName]; remoteVersion: FSBackdoor.Version _ FSBackdoor.highestVersion; remoteCreate: BasicTime.GMT; [server: server, file: fileName] _ FSName.ServerAndFileRopes[remoteName]; fileName _ FSName.BangVersionFile[fileName, FSBackdoor.highestVersion]; [remoteVersion, , remoteCreate] _ FSRemoteFile.Info[server, fileName, BasicTime.nullGMT ! FS.Error => CONTINUE]; IF remoteVersion = FSBackdoor.highestVersion THEN { IO.PutF[out, " No remote version at all.\n"]; RETURN; }; fileName _ FSName.BangVersionFile[remoteName, remoteVersion]; SELECT BasicTime.Period[to: remoteCreate, from: createWanted] FROM > 0 => { IO.PutF[out, " Newer remote version is %G\n", [rope[fileName]]]; deleted _ deleted.SUCC; IF deleteSwitch THEN { IO.PutF[out, "Deleting %G ...", [rope[localName]]]; FS.Delete[localName]; IO.PutF[out, " ok.\n"]; } ELSE IO.PutF[out, " Would have deleted %G.\n", [rope[localName]]]; RETURN; }; = 0 => ERROR; < 0 => { rescueMe _ rescueMe.SUCC; IO.PutF[out, " *** Beware: Missing remote version was NEWER.\n %G >> %G\n", [rope[remoteName]], [rope[fileName]]]; IO.PutF[out, " *** There is hope. The file is in your cache!\n"]; }; ENDCASE => ERROR; }; < 0 => IO.PutF[out, " *** Beware: Missing remote version was attached to a NEWER local version %G!h\n", [rope[rootName]]]; ENDCASE => ERROR; }; }; ENDCASE => { rescueMe _ rescueMe.SUCC; IO.PutF[out, "*** %G\n", [rope[fsError.explanation]]]; }; RETURN; }; IF localName = NIL THEN { -- Cached file SELECT fsError.code FROM $illegalName, $unknownFile => { -- Remote file doesn't exist targetVersion: FSBackdoor.Version _ ExtractVersion[remoteName]; remoteVersion: FSBackdoor.Version _ FSBackdoor.highestVersion; remoteCreate: BasicTime.GMT; IO.PutF[out, "Cached file missing from server.\n %G\n", [rope[remoteName]]]; [server: server, file: fileName] _ FSName.ServerAndFileRopes[rootName]; fileName _ FSName.BangVersionFile[rootName, FSBackdoor.highestVersion]; [remoteVersion, , remoteCreate] _ FSRemoteFile.Info[server, fileName, createWanted ! FS.Error => CONTINUE ]; IF remoteVersion = FSBackdoor.highestVersion THEN { -- No remote versions IO.PutF[out, " No remote versions exist.\n"]; flushed _ flushed.SUCC; IF flushSwitch THEN { IO.PutF[out, " Flushing %G ...", [rope[remoteName]]]; FSBackdoor.Flush[remoteName]; IO.PutF[out, " ok.\n"]; } ELSE IO.PutF[out, " Would have Flushed %G.\n", [rope[remoteName]]]; RETURN; } ELSE { -- Other versions exist fileName _ FSName.BangVersionFile[rootName, remoteVersion]; SELECT remoteVersion FROM > targetVersion => { IO.PutF[out, " Newer remote version is %G\n", [rope[fileName]]]; flushed _ flushed.SUCC; IF flushSwitch THEN { IO.PutF[out, " Flushing %G ...", [rope[remoteName]]]; FSBackdoor.Flush[remoteName]; IO.PutF[out, " ok.\n"]; } ELSE IO.PutF[out, " Would have Flushed %G.\n", [rope[remoteName]]]; RETURN; }; = targetVersion => IO.PutF[out, " *** Beware: Remote version exists, but it has a DIFFERENT timestamp.\n\n"]; < targetVersion => { rescueMe _ rescueMe.SUCC; IO.PutF[out, " *** Beware: Missing remote version was NEWER.\n %G >> %G\n", [rope[remoteName]], [rope[fileName]]]; IO.PutF[out, " *** There is hope. The file is in your cache!\n"]; }; ENDCASE => ERROR; }; }; ENDCASE => { rescueMe _ rescueMe.SUCC; IO.PutF[out, "*** %G\n", [rope[fsError.explanation]]]; }; RETURN; }; }; IF createWanted = BasicTime.nullGMT THEN RETURN; IF createWanted = createFound THEN RETURN; IO.PutF[out, "%G => %G\n", [rope[localName]], [rope[remoteName]]]; RETURN; }; GrabInfo: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL _ FALSE] = UNCHECKED { WITH e: entry^ SELECT FROM local => accept _ FALSE; attached => { attached _ attached.SUCC; IF attachedSwitch THEN { rootName _ FSBackdoor.TextFromTextRep[@entry[e.nameBody]]; localName _ FSBackdoor.MakeFName[rootName, e.version]; thisVersion _ e.version; remoteName _ FSBackdoor.TextFromTextRep[@entry[e.attachedTo]]; createWanted _ e.created; accept _ TRUE; }; }; cached => { cached _ cached.SUCC; IF cachedSwitch THEN { rootName _ FSBackdoor.TextFromTextRep[@entry[e.nameBody]]; localName _ NIL; remoteName _ FSBackdoor.MakeFName[rootName, e.version]; thisVersion _ FSBackdoor.noVersion; createWanted _ BasicTime.nullGMT; accept _ TRUE; }; }; ENDCASE => ERROR; }; passes: INT _ 0; attached, cached, deleted, flushed, rescueMe: INT _ 0; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Failed}]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE = argv[i]; IF Rope.Length[arg] = 0 THEN LOOP; IF Rope.Fetch[arg, 0] = '- THEN { sense: BOOL _ TRUE; FOR i: INT IN [1..Rope.Length[arg]) DO SELECT Rope.Fetch[arg, i] FROM '~ => {sense _ NOT sense; LOOP}; 'd, 'D => deleteSwitch _ sense; 'f, 'F => flushSwitch _ sense; ENDCASE; sense _ TRUE; ENDLOOP; LOOP; }; FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: Rope.Flatten[arg], localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: GrabInfo, acceptProc: CheckFile]; passes _ passes.SUCC; ENDLOOP; IF passes = 0 THEN FSBackdoor.Enumerate[ volName: NIL, nameBodyPattern: NIL, localOnly: FALSE, allVersions: TRUE, version: [0], matchProc: GrabInfo, acceptProc: CheckFile]; IO.PutF[out, "\nProcessed %G attachments and %G cached files.\n", [integer[attached]], [integer[cached]]]; IF deleted # 0 THEN { IF deleteSwitch THEN IO.PutF[out, "%G files were deleted.\n", [integer[deleted]]] ELSE IO.PutF[out, "%G files would have been deleted.\n", [integer[deleted]]]; }; IF flushed # 0 THEN { IF flushSwitch THEN IO.PutF[out, "%G files were flushed.\n", [integer[flushed]]] ELSE IO.PutF[out, "%G files would have been flushed.\n", [integer[flushed]]]; }; IF rescueMe # 0 THEN IO.PutF[out, "***** %G files need rescuing by hand. *****\n", [integer[rescueMe]]]; EXITS Failed => result _ $Failure; }; ExtractVersion: PROC [fileName: ROPE] RETURNS [version: FSBackdoor.Version] = { bang: INT _ Rope.Find[fileName, "!", 0]; IF bang < 0 THEN RETURN[FSBackdoor.noVersion]; version _ FSName.VersionFromRope[Rope.Substr[fileName, bang+1]]; }; AbortRequested: PROC [cmd: Commander.Handle] RETURNS [BOOL] = { Process.CheckForAbort[]; RETURN [FALSE]; }; Init: PROCEDURE = { Commander.Register[ "///Commands/Ferret", Ferret, "Ferret -- Find files in cache that are NOT on the server. (Finds lots of junk too.)"]; }; Init[]; }. &Ferret.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Hal Murray June 23, 1985 5:32:21 pm PDT [cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [ in, out, err: STREAM, commandLine, command: ROPE, propertyList: List.AList, procData: CommandProcHandle] $illegalName is probably no directory. Groan. The highest local version of this file has the desired stamp and it is in our cache. The server has some version of this file, but not the one we want. This is dull if a better file is on the server, but it's VERY interesting if our copy is better than what's on the server. No remote version, but directory and such are reasonable Another opportunity for more housecleaning, but again, this path doesn't happen very often. Initialization Κ έ˜codešœ ™ Kšœ Οmœ7™BK™'—K™šΟk ˜ Kšœ žœ˜'Jšœ žœ!˜2Kšœ žœ!˜0Kšžœžœ&˜.Kšœ žœ^˜nKšœžœ8˜DKšœ žœ˜Kšžœžœžœ˜Kšœžœ˜Kšœžœ žœ˜8—K˜šΟbœžœž˜Kšžœ$žœ$žœ˜bKšœž˜K˜Kšžœžœžœ˜Kšžœžœžœžœ˜—K˜šŸœ˜!Kš œžœžœ žœžœžœžœ™Ešœ™Kšœžœžœ™1Kšœ6™6—Kšœžœ ˜Kšœžœžœ˜Kšœžœžœ˜Kšœžœžœ˜Kšœ žœžœ˜Kšœ žœ˜Kšœ žœΟc˜/Kšœ žœ˜Kšœ ˜ Kšœžœ˜šΟn œžœžœžœ˜(Kšœ!˜!Kšœ žœ˜Kšœžœ˜/Kšœžœ˜Kšœ žœžœžœ˜'Kšœ˜KšœI˜Išœ]˜]Kšžœžœ˜-—šžœžœžœ˜Kšžœ˜šžœ žœžœ ˜*šžœž˜šœ 1˜MKšœ&™&—šœ 7˜PKšœ žœžœ˜Kšœžœ˜Kšžœh˜jKš œ+žœ(žœžœ žœ˜šžœ žœžœ˜šžœ3ž˜=šœ ˜ KšžœA˜CKšœžœ˜šžœžœž˜Kšžœ5˜7Kšžœ˜Kšžœ˜—Kšžœžœ>˜EKšžœ˜ —šœ˜KšœΨžœ=™™Kšœ?˜?Kšœ>˜>Kšœžœ˜KšœI˜IKšœG˜GKšœZžœ žœ˜pšžœ+žœ˜3Kšœ8™8Kšœ[™[Kšžœ.˜0Kšžœ˜ —Kšœ=˜=šžœ8ž˜Bšœ ˜ KšžœA˜CKšœžœ˜šžœžœž˜Kšžœ1˜3Kšžœ˜Kšžœ˜—Kšžœžœ>˜EKšžœ˜ —Kšœžœ˜ šœ˜Kšœžœ˜Kšžœt˜vKšžœC˜E—Kšžœžœ˜——Kšœžœr˜{Kšžœžœ˜———šžœ˜ Kšœžœ˜Kšžœ8˜:—Kšžœ˜ ——šžœ žœžœ ˜(šžœž˜šœ  ˜˜>Kšœžœ˜KšžœM˜OKšœG˜GKšœG˜GKšœUžœ žœ˜lšžœ+žœ ˜IKšžœ.˜0Kšœžœ˜šžœ žœ˜Kšžœ6˜8Kšœ˜Kšžœ˜—Kšžœžœ?˜FKšžœ˜ —šžœ ˜Kšœ;˜;šžœž˜šœ˜KšžœA˜CKšœžœ˜šžœ žœž˜Kšžœ6˜8Kšœ˜Kšžœ˜—Kšžœžœ?˜FKšžœ˜ —KšœžœY˜nšœ˜Kšœžœ˜Kšžœt˜vKšžœC˜E—Kšžœžœ˜———šžœ˜ Kšœžœ˜Kšžœ8˜:—Kšžœ˜ ———Kšžœ"ž œ˜0Kšžœž œ˜*Kšžœ@˜BKšžœ˜ —š‘œžœž˜Kš œžœžœžœž œ˜Ošžœ žœž˜Kšœžœ˜šœ ˜ Kšœžœ˜šžœžœ˜Kšœ:˜:Kšœ6˜6Kšœ˜Kšœ>˜>Kšœ˜Kšœ žœ˜——šœ ˜ Kšœžœ˜šžœžœ˜Kšœ:˜:Kšœ žœ˜Kšœ7˜7Kšœ#˜#Kšœ!˜!Kšœ žœ˜——Kšžœžœ˜——Jšœžœ˜Jšœ.žœ˜6šœ=˜=Jšœ)žœžœ ˜8—šžœžœžœž˜Jšœžœ ˜Jšžœžœžœ˜"šžœžœ˜!Jšœžœžœ˜šžœžœžœž˜&šžœž˜Jšœžœžœ˜ Jšœ˜Jšœ˜Jšžœ˜—Jšœžœ˜ Jšžœ˜—Jšžœ˜—šœ˜Kšœ žœ1žœžœ<˜‘—Kšœžœ˜Jšžœ˜—šžœ žœ˜(Kš œ žœžœ žœžœ<˜ƒ—Kšžœh˜jšžœ žœ˜Kšžœžœžœ:˜QKšžœžœI˜P—šžœ žœ˜Kšžœ žœžœ:˜PKšžœžœI˜P—KšžœžœžœS˜kKšžœ ˜%—K˜K˜š‘œžœ žœžœ"˜OKšœžœ˜(Kšžœ žœžœ˜.KšœC˜C—K˜š‘œžœžœžœ˜?Kšœ˜Kšžœžœ˜—K˜š‘œž œ˜šœ˜Kšœ˜KšœZ˜Z——K™K™K˜˜K˜—Kšžœ˜—…—"d/g