<> <> <> DIRECTORY ArchivistBTreePublic, Basics, BasicTime, Convert, DFUtilities, FileSets, FileSetsPrivate, FS, IO, MoreFileSets, Process, RedBlackTree, Rope, Tempus, TextFind, TextReplace, TiogaOps, ViewerClasses, ViewerTools, WalnutWindow; MoreFileSetsImpl: CEDAR PROGRAM IMPORTS ArchivistBTreePublic, BasicTime, Convert, FileSets, FileSetsPrivate, FS, IO, Process, Rope, Tempus, TiogaOps, WalnutWindow EXPORTS MoreFileSets ={OPEN FileSets, FileSetsPrivate, MoreFileSets; <> BTree: TYPE = REF BTreeRec; BTreeRec: TYPE = RECORD [h: ArchivistBTreePublic.Handle]; IsArchived: PUBLIC PROC [--caches current BTree--] RETURNS [f: Filter] = { f _ NEW [FilterRep _ [TestArchive, NEW [BTreeRec _ [ArchivistBTreePublic.OpenBTree[] ]] ]]; }; TestArchive: PROC [fn: FileNote, data: REF ANY] RETURNS [BOOL] = { bt: BTree _ NARROW[data]; created: GMT _ GetCreated[fn]; cp: FS.ComponentPositions _ FS.ExpandName[fn.fsName].cp; SELECT cp.ver.length FROM =0 => { fil: ArchivistBTreePublic.FileInfoList _ ArchivistBTreePublic.EnumerateRecord[bt.h, fn.fsName, created]; RETURN [fil # NIL]; }; #0 => RETURN [ArchivistBTreePublic.ReadFileInfo[bt.h, fn.fsName, created] # NIL]; ENDCASE => ERROR; }; <> FindByDate: PROC [date: GMT] RETURNS [v: Viewer] = { list: LIST OF Viewer _ WalnutWindow.EnumWalnutViewers[keepSeparate: TRUE].msgList; FOR list _ list, list.rest WHILE list # NIL DO { root: TiogaOps.Ref _ TiogaOps.ViewerDoc[list.first]; curNode: TiogaOps.Ref _ root; FOR curNode: TiogaOps.Ref _ TiogaOps.StepForward[root], TiogaOps.StepForward[curNode] WHILE curNode # NIL DO from: STREAM _ IO.RIS[TiogaOps.GetRope[curNode].Cat["\n"]]; FOR i: INT _ from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO keyword: ROPE _ from.GetTokenRope[MyBreak].token; Process.CheckForAbort[]; IF keyword.Equal["Date:", FALSE] THEN { restOfLine: ROPE _ from.GetLineRope[]; thisDate: GMT _ Tempus.Parse[restOfLine].time; IF thisDate = date THEN RETURN [list.first] ELSE GOTO NotThisOne; }; IF keyword.Fetch[keyword.Length[]-1] # ': THEN GOTO NotThisOne; IF keyword.Equal["Archived:"] OR keyword.Equal["Archive:"] OR keyword.Equal["Retrieved:"] OR keyword.Equal["Retrieve:"] THEN GOTO NotThisOne; [] _ from.GetLineRope[]; ENDLOOP; ENDLOOP; SIGNAL Warning[IO.PutFR["No date in Walnut message viewer named %g", IO.refAny[list.first.name]]]; EXITS NotThisOne => NULL; } ENDLOOP; SIGNAL Warning[IO.PutFR["no message viewer on an archive message dated %g", IO.time[date]]]; v _ NIL; }; FromArchiveMsg: PUBLIC PROC [date: GMT, which: ArchiveWhich, ids: IdentificationScheme _ []] RETURNS [fs: FileSet] = { Add: PROC [fn: FileNote] --FileConsumer-- = {[] _ Insert[fs, fn]}; fs _ NewFileSet[ids]; EnumArchiveMsg[date, Add, which, ids]; fs.summary _ Convert.RopeFromInt[fs.Size[]]; }; ParseError: ERROR [message: ROPE] = CODE; EnumArchiveMsg: PUBLIC PROC [date: GMT, to: FileConsumer, which: ArchiveWhich, ids: IdentificationScheme _ []] = { heading: BOOL _ TRUE; v: Viewer _ FindByDate[date]; root, curNode: TiogaOps.Ref; IF v = NIL THEN RETURN; root _ TiogaOps.ViewerDoc[v]; curNode _ root; FOR curNode: TiogaOps.Ref _ TiogaOps.StepForward[root], TiogaOps.StepForward[curNode] WHILE curNode # NIL DO from: STREAM _ IO.RIS[TiogaOps.GetRope[curNode].Cat["\n"]]; Skip: PROC [toke: ROPE] = { got: ROPE _ from.GetTokenRope[MyBreak].token; IF NOT got.Equal[toke] THEN ERROR ParseError[IO.PutFR["Got %g instead of %g in archive message of %g", IO.refAny[got], IO.refAny[toke], IO.time[date]]]; }; InsistEOL: PROC = { c: CHAR _ from.GetChar[]; IF c # '\n THEN ERROR ParseError[IO.PutFR["Got %g instead of newline in archive message of %g", IO.char[c], IO.time[date]]]; }; FOR i: INT _ from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO ENABLE ParseError => {SIGNAL Warning[message]; EXIT}; keyword: ROPE _ from.GetTokenRope[MyBreak].token; Process.CheckForAbort[]; IF keyword.Equal["Archived:"] THEN { fileName, pv, bv: ROPE; gmt: GMT; fn: FileNote; heading _ FALSE; fileName _ from.GetTokenRope[MyBreak].token; Skip["of"]; gmt _ from.GetTime[]; Skip["on"]; pv _ from.GetTokenRope[MyBreak].token; Skip["or"]; bv _ from.GetTokenRope[MyBreak].token; InsistEOL[]; IF (fn _ CreateNote[fileName: fileName, created: gmt, ids: ids]) # NIL THEN { fn.primaryVolume _ pv; fn.backupVolume _ bv; to[fn]; }; } ELSE IF keyword.Equal["Retrieved:"] THEN { fromName, toName: ROPE; gmt: GMT; fn: FileNote; heading _ FALSE; fromName _ from.GetTokenRope[MyBreak].token; Skip["of"]; gmt _ from.GetTime[]; Skip["as"]; toName _ from.GetTokenRope[MyBreak].token; InsistEOL[]; IF (fn _ CreateNote[fileName: SELECT which FROM archive => fromName, online => toName, ENDCASE => ERROR, created: gmt, ids: ids]) # NIL THEN to[fn]; } ELSE IF keyword.Equal["Archive:"] THEN { fileName: ROPE; fn: FileNote; heading _ FALSE; fileName _ from.GetTokenRope[MyBreak].token; InsistEOL[]; IF HasWildcards[fileName] THEN { Consume: PROC [fullFName: ROPE] RETURNS [continue: BOOL] --FS.NameProc-- = { IF (fn _ CreateNote[fullFName, noGMT, ids]) # NIL THEN to[fn]; continue _ TRUE}; FS.EnumerateForNames[fileName, Consume]; } ELSE { IF (fn _ CreateNote[fileName, noGMT, ids]) # NIL THEN to[fn]; }; } ELSE IF keyword.Equal["Retrieve:"] THEN { fileName, pv, bv: ROPE; gmt: GMT; fn: FileNote; heading _ FALSE; fileName _ from.GetTokenRope[MyBreak].token; Skip["of"]; gmt _ from.GetTime[]; Skip["from"]; pv _ from.GetTokenRope[MyBreak].token; Skip["or"]; bv _ from.GetTokenRope[MyBreak].token; InsistEOL[]; IF (fn _ CreateNote[fileName, gmt, ids]) # NIL THEN { fn.primaryVolume _ pv; fn.backupVolume _ bv; to[fn]; }; } ELSE { IF NOT heading THEN ERROR ParseError[IO.PutFR["Got non-archiving keyword %g among archiving keywords in archive message of %g", IO.rope[keyword], IO.time[date]]]; IF keyword.Length[] = 0 THEN ERROR; IF keyword.Fetch[keyword.Length[]-1] # ': THEN ERROR ParseError[IO.PutFR["Found non-keyword %g in archive message of %g", IO.refAny[keyword], IO.time[date]]]; [] _ from.GetLineRope[]; }; ENDLOOP; from.Close[]; ENDLOOP; }; HasWildcards: PROC [fileName: ROPE] RETURNS [hasem: BOOL] = {hasem _ fileName.Find["*"] >= 0}; MyBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ IF char IN ['\000 .. ' ] THEN sepr ELSE other; }; RefineTime: PROC [fileName: ROPE, date: DFUtilities.Date] RETURNS [gmt: GMT] = { SELECT date.format FROM explicit => RETURN [date.gmt]; omitted, greaterThan, notEqual => RETURN [GetGMT[fileName]]; ENDCASE => ERROR; }; GetGMT: PROC [fileName: ROPE] RETURNS [created: GMT] = { ok: BOOL _ TRUE; [created: created] _ FS.FileInfo[name: fileName, remoteCheck: FALSE !FS.Error => {ok _ FALSE; CONTINUE}]; IF NOT ok THEN created _ noGMT; }; FromArchivist: PUBLIC PROC [pattern: ROPE, created: GMT _ noGMT, ids: IdentificationScheme _ []] RETURNS [fs: FileSet] = { Add: PROC [fn: FileNote] = {[] _ Insert[fs, fn]}; fs _ NewFileSet[ids]; EnumArchivist[pattern, Add, created, ids]; fs.summary _ Convert.RopeFromInt[fs.Size[]]; }; EnumArchivist: PUBLIC PROC [pattern: ROPE, to: FileConsumer, created: GMT _ noGMT, ids: IdentificationScheme _ []] = { Enumerate: PROC [pattern: ROPE, Consume: Consumer] = { h: ArchivistBTreePublic.Handle _ ArchivistBTreePublic.OpenBTree[]; fil: ArchivistBTreePublic.FileInfoList; fil _ h.EnumerateRecord[pattern, created]; h.CloseBTree[]; FOR fil _ fil, fil.next WHILE fil # NIL DO primaryVolume, backupVolume: ROPE; vs: IO.STREAM _ IO.RIS[fil.volumes]; {ENABLE IO.EndOfStream => IF stream = vs THEN { SIGNAL Warning[IO.PutFR["Archivist thinks %g of %g has less than two volumes; tell Tim Diebert", IO.rope[fil.fileName], IO.time[fil.created]]]; CONTINUE}; primaryVolume _ vs.GetTokenRope[IO.IDProc].token; backupVolume _ vs.GetTokenRope[IO.IDProc].token; }; vs.Close[]; SELECT fil.state FROM none, pending, backup => NULL; complete => Consume[fil.fileName, fil.created, primaryVolume, backupVolume]; ENDCASE => ERROR; ENDLOOP; h _ h; }; Consume: PROC [fullFName: ROPE, created: GMT, primaryVolume, backupVolume: ROPE] = { fn: FileNote _ CreateNote[fullFName, created, ids]; IF fn # NIL THEN { fn.primaryVolume _ primaryVolume; fn.backupVolume _ backupVolume; to[fn]; }; }; HashEnumerate[pattern, Enumerate, Consume]; }; }.