<> <> <> <<>> DIRECTORY BasicTime, Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, Failed, Parse], Convert, File, FS, FSBackdoor, FSRemoteFile, IO, Process USING [CheckForAbort], RedBlackTree, Rope; RecoverFromCache: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommandTool, Convert, File, FS, FSBackdoor, FSRemoteFile, IO, Process, RedBlackTree, Rope = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; GNameToDate: TYPE = REF GNameToDateObj; GNameToDateObj: TYPE = RECORD [ key: ROPE, createTime: BasicTime.GMT ]; RecoverFromCacheProc: Commander.CommandProc = { <<[cmd: REF CommandObject] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> <> <> <> <<>> out: STREAM _ cmd.out; count: INT _ 10; volume: File.Volume; lastEntry: FSBackdoor.EntryPtr; localCount, cachedCount, attachedCount: INT _ 0; stopAttachmentsList: BOOL _ FALSE; matchAttachmentsProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] ={ Process.CheckForAbort[]; lastEntry _ entry; RETURN[TRUE, stopAttachmentsList]; }; acceptAttachmentsProc: SAFE PROC RETURNS [stop: BOOLEAN] = TRUSTED { thisFP: File.FP _ File.nullFP; fileName: ROPE; fileNameLessVersion: ROPE; createTime: BasicTime.GMT; attachedTo: ROPE; stop _ FALSE; fileNameLessVersion _ FSBackdoor.TextFromTextRep[@lastEntry[lastEntry.nameBody]]; fileName _ Rope.Cat[fileNameLessVersion, "!", Convert.RopeFromCard[from: lastEntry.version, showRadix: FALSE]]; WITH e: lastEntry^ SELECT FROM attached => { node: RedBlackTree.Node; createTime _ e.created; attachedTo _ FSBackdoor.TextFromTextRep[@lastEntry[e.attachedTo]]; node _ RedBlackTree.LookupNode[lNameToGName, attachedTo]; IF node = NIL THEN { myData: GNameToDate = NEW [GNameToDateObj _ [attachedTo, createTime]]; RedBlackTree.Insert[lNameToGName, myData, attachedTo]; }; }; cached => { stopAttachmentsList _ TRUE; }; local => { }; ENDCASE ; }; matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN] ={ Process.CheckForAbort[]; lastEntry _ entry; RETURN[TRUE, FALSE]; }; acceptProc: SAFE PROC RETURNS [stop: BOOLEAN] = TRUSTED { fileName: ROPE; fileNameLessVersion: ROPE; nameBodyRopeText: Rope.Text; doTransfer: BOOL _ FALSE; stop _ FALSE; nameBodyRopeText _ FSBackdoor.TextFromTextRep[@lastEntry[lastEntry.nameBody]]; fileNameLessVersion _ IF nameBodyRopeText.InlineFetch[0] # '[ THEN Rope.Concat["[]<>", nameBodyRopeText] ELSE nameBodyRopeText; fileName _ Rope.Cat[fileNameLessVersion, "!", Convert.RopeFromCard[from: lastEntry.version, showRadix: FALSE]]; WITH e: lastEntry^ SELECT FROM cached => { lookForDateInfoProc: FS.InfoProc = CHECKED { <> IF BasicTime.Period[created, localCreateDate] = 0 THEN { foundOnServer _ TRUE; createdOnServer _ created; fullFNameOnServer _ fullFName; }; RETURN[TRUE]; }; node: RedBlackTree.Node; errorFree: BOOL _ TRUE; foundOnServer: BOOL _ FALSE; createdOnServer: BasicTime.GMT; fullFNameOnServer: ROPE; missingCreateDate: BOOL _ FALSE; file: FS.OpenFile _ FS.nullOpenFile; localCreateDate: BasicTime.GMT _ BasicTime.nullGMT; node _ RedBlackTree.LookupNode[lNameToGName, fileName]; IF node = NIL THEN { explanation: ROPE; file _ FS.Open[name: fileName, remoteCheck: FALSE ! FS.Error => {explanation _ error.explanation; CONTINUE;}]; IF file # FS.nullOpenFile THEN { localCreateDate _ FS.GetInfo[file].created; } ELSE { cmd.out.PutF["\n Open error %g during open of %g. Not processed.", IO.rope[explanation], IO.rope[fileName]]; RETURN; } } ELSE { myData: GNameToDate = NARROW[node.data]; localCreateDate _ myData.createTime; }; FS.EnumerateForInfo[pattern: Rope.Cat[fileNameLessVersion, "!*"], proc: lookForDateInfoProc ! FS.Error => CONTINUE;]; IF listOnly THEN { IF ~foundOnServer THEN cmd.out.PutF["\n Copy %g.", IO.rope[fileName]]; } ELSE { IF autoConfirm THEN { IF ~foundOnServer THEN doTransfer _ TRUE ELSE doTransfer _ FALSE; } ELSE { IF ~foundOnServer THEN { DO line: ROPE _ NIL; IF foundOnServer THEN cmd.out.PutF["\n File %g is on server, but there is a local version %g. Copy it? ", IO.rope[fullFNameOnServer], IO.int[lastEntry.version]] ELSE cmd.out.PutF["\n File %g is on not on server. Copy it? ", IO.rope[fileName]]; line _ cmd.in.GetLineRope[]; cmd.out.PutRope["\n"]; IF Rope.IsEmpty[line] THEN LOOP; IF line.Fetch[0] = 'y OR line.Fetch[0] = 'Y THEN {doTransfer _ TRUE; EXIT}; IF line.Fetch[0] = 'n OR line.Fetch[0] = 'N THEN {doTransfer _ FALSE; EXIT}; ENDLOOP; }; }; }; IF doTransfer THEN { confirm: FSRemoteFile.ConfirmProc = CHECKED { RETURN[TRUE]}; fullFName: ROPE; cp: FS.ComponentPositions; IF file = FS.nullOpenFile THEN { explanation: ROPE; file _ FS.Open[name: fileName, remoteCheck: FALSE ! FS.Error => {explanation _ error.explanation; CONTINUE;}]; IF file = FS.nullOpenFile THEN { cmd.out.PutF["\n Open error %g during open of %g. Can't copy.", IO.rope[explanation], IO.rope[fileName]]; RETURN; }; }; [fullFName: fullFName, cp: cp] _ FS.ExpandName[name: fileName] ; FSRemoteFile.Store[server: Rope.Substr[fullFName, cp.server.start, cp.server.length], file: Rope.Substr[fullFName, cp.dir.start-1, cp.ver.start-cp.dir.start], str: FS.StreamFromOpenFile[file], created: localCreateDate, proc: confirm]; [] _ FS.Copy[from: fileName, to: fileNameLessVersion, attach: FALSE]; cmd.out.PutF["\n %g copied\n", IO.rope[fileName]]; }; IF file = FS.nullOpenFile THEN FS.Close[file ! FS.Error => CONTINUE]; }; ENDCASE ; }; lNameToGName: RedBlackTree.Table; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GO TO Usage}]; counter: INT _ 1; gotVolume: BOOL _ FALSE; server: ROPE _ NIL; autoConfirm: BOOL _ FALSE; listOnly: BOOL _ FALSE; expectType: TYPE = {num, volName, switch}; expecting: expectType _ num; volumeName: ROPE _ NIL; pattern: Rope.Text; IF argv.argc < 2 THEN GOTO Usage; WHILE counter < argv.argc DO arg: ROPE _ argv[counter]; IF arg.InlineFetch[0] = '- THEN { c: CHAR; IF arg.Length[] # 2 THEN GOTO Usage; c _ arg.InlineFetch[1]; SELECT c FROM 'c => autoConfirm _ TRUE; 'l => listOnly _ TRUE; ENDCASE; } ELSE { -- NOT '- IF gotVolume THEN server _ arg ELSE {gotVolume _ TRUE; volumeName _ arg}; }; counter _ counter + 1; ENDLOOP; volume _ File.FindVolumeFromName[volumeName]; IF volume = NIL THEN GOTO Usage; lNameToGName _ RedBlackTree.Create[getKey: GetTheKey, compare: CompareStuff]; cmd.out.PutRope["Starting enumerate of FS BTree for attachments.\n"]; FSBackdoor.Enumerate[volName: volumeName, nameBodyPattern: NIL, localOnly: TRUE, allVersions: TRUE, version: FSBackdoor.highestVersion, matchProc: matchAttachmentsProc, acceptProc: acceptAttachmentsProc]; cmd.out.PutRope["Starting enumerate of FS BTree for cached files.\n"]; pattern _ Rope.Flatten[Rope.Cat["[", server,"*"]]; FSBackdoor.Enumerate[volName: volumeName, nameBodyPattern: pattern, localOnly: FALSE, allVersions: FALSE, version: FSBackdoor.highestVersion, matchProc: matchProc, acceptProc: acceptProc]; RedBlackTree.DestroyTable[lNameToGName]; EXITS Usage => RETURN[$Failure, "Usage: RecoverFromCache {-c} {Server} "]; }; GetTheKey: RedBlackTree.GetKey = { myObj: GNameToDate = NARROW[data]; RETURN[myObj.key]; }; CompareStuff: RedBlackTree.Compare = { myObj: GNameToDate = NARROW[data]; key: ROPE = NARROW[k]; RETURN[Rope.Compare[key, myObj.key]]; }; <<>> Init: PROCEDURE = { Commander.Register[ "///Commands/RecoverFromCache", RecoverFromCacheProc, "RecoverFromCache Volume {-c} {-l} {Server} -- Scan the FS file cache on the specified volume (e.g., 'Cedar') for files from Server (all files if Server omitted); for each file, check to see that there is a file by that name on the server that has a higher version number. If not, print file name, versions and date from the server and wait for confirmation. -c does autoconfirm. If confirmed, store the file from the cache. -l only does listing"]; }; <<>> <> Init[]; END. <<>> <> <> <<>> <<>>