<> <> <> DIRECTORY Commander USING [CommandProc, Register], FS USING [Error, PagesForBytes], FSRope USING [StreamFromRope], IO, Process USING [Detach], RefTab USING [EachPairAction, Ref, Val, Create, Fetch, Store, Pairs], Rope USING [ROPE], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams], ViewerOps USING [FindViewer, OpenIcon], WalnutDefs USING [Error], WalnutLog USING [LogLength, OpenLogStreams, ReturnCurrentLogStreams, ShutdownLog], WalnutLogExpunge USING [CopyEntry, EndExpunge, GetExpungeProgress, PeekEntry, SetPosition, Shutdown, SkipEntry, StartExpunge], WalnutRoot USING [CommitAndContinue, Open, RegisterStatsProc, Shutdown, StartTransaction, SwapLogs, UnregisterStatsProc], WalnutStream USING [FindNextEntry, Open, PeekEntry]; LogInfo: CEDAR PROGRAM IMPORTS Commander, FS, FSRope, IO, Process, RefTab, ViewerIO, ViewerOps, WalnutDefs, WalnutLog, WalnutLogExpunge, WalnutRoot, WalnutStream = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; out: STREAM _ NIL; debugging: BOOL _ FALSE; Xyz: TYPE = REF ValueObject; ValueObject: TYPE = RECORD[num, bytes: INT _ 0]; TSStream: PROC[name: ROPE] RETURNS [in, out: STREAM] = { v: ViewerClasses.Viewer _ ViewerOps.FindViewer[name]; [in, out] _ ViewerIO.CreateViewerStreams[name, v]; IF v#NIL THEN IF v.iconic THEN ViewerOps.OpenIcon[v]; }; SetupScan: PROC[commandLine: ROPE] = { clStream: STREAM _ FSRope.StreamFromRope[commandLine]; logFile: ROPE; logFile _ clStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token; Scan[logFile]; }; Scan: PROC[logFile: ROPE] = { strm: STREAM; logLength: INT; out _ TSStream["Walnut Rescue"].out; out.PutRope["\n\n************************************************************\n"]; out.PutF["\t\t Scan Log for Entries, started @ %g\n", IO.time[] ]; IF logFile = NIL THEN { out.PutRope["No input file specified - quitting\n"]; RETURN; }; strm _ WalnutStream.Open[name: logFile, readOnly: TRUE ! FS.Error => CONTINUE]; IF strm = NIL THEN { out.PutF["Could not open %g\n", IO.rope[logFile] ]; RETURN }; IF (logLength _ strm.GetLength[]) = 0 THEN { out.PutF["The log file %g is empty\n", IO.rope[logFile] ]; strm.Close[]; RETURN }; out.PutF["Scanning the logfile: %g\n\n(0)", IO.rope[logFile] ]; strm.SetIndex[0]; DoScan[strm, logLength]; strm.Close[]; }; SetupFix: PROC[commandLine: ROPE] = { clStream: STREAM _ FSRope.StreamFromRope[commandLine]; rootFile: ROPE; entriesToIgnore, lastToIgnore: LIST OF ATOM; this: ATOM; rootFile _ clStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token; DO this _ clStream.GetAtom[ ! IO.EndOfStream => { this _ NIL; CONTINUE} ]; IF this = NIL THEN EXIT; IF entriesToIgnore = NIL THEN entriesToIgnore _ lastToIgnore _ LIST[this] ELSE { lastToIgnore.rest _ LIST[this]; lastToIgnore _ lastToIgnore.rest; }; ENDLOOP; DoFix[rootFile, entriesToIgnore]; }; Fix: PROC[rootFile: ROPE, entriesToIgnore: LIST OF REF ANY] = { toBeIgnored: LIST OF ATOM; FOR ei: LIST OF REF ANY _ entriesToIgnore, ei.rest UNTIL ei = NIL DO ax: ATOM _ NARROW[ei.first]; toBeIgnored _ CONS[ax, toBeIgnored]; ENDLOOP; DoFix[rootFile, toBeIgnored]; }; DoFix: PROC[rootFile: ROPE, toBeIgnored: LIST OF ATOM] = { previousAt, at: INT _ -1; ident: ATOM; logLength, newLen: INT; expungeID: INT; num, numSinceFlush: INT _ 0; bytesBetweenFlushes: INT = 200000; pagesNeeded: INT; out _ TSStream["Walnut Rescue"].out; out.PutRope["\n\n************************************************************\n"]; out.PutF["\t\tFix Log, started at %g\n", IO.time[] ]; IF rootFile = NIL THEN { out.PutRope["\nNo input file specified\n"]; RETURN; }; out.PutF["\tFixing the rootfile: %g\n", IO.rope[rootFile] ]; WalnutRoot.RegisterStatsProc[Report]; [] _ WalnutRoot.Open[rootFile]; [] _ WalnutRoot.StartTransaction[]; [] _ WalnutLog.OpenLogStreams[]; pagesNeeded _ FS.PagesForBytes[logLength _ WalnutLog.LogLength[]]; WalnutLog.ReturnCurrentLogStreams[]; expungeID _ WalnutLogExpunge.StartExpunge[pagesNeeded]; IF toBeIgnored = NIL THEN out.PutRope[" No entries except invalid ones will be ignored\n"] ELSE { out.PutRope["\n Ignoring the following entries: "]; FOR ignore: LIST OF ATOM _ toBeIgnored, ignore.rest UNTIL ignore = NIL DO out.PutF[" %g,", IO.atom[ignore.first]]; ENDLOOP; out.PutChar['\n]; }; [] _ WalnutLogExpunge.SetPosition[0]; BEGIN ENABLE WalnutDefs.Error => { out.PutF["WalnutDefs Error: code: %g, info: %g @ %g", IO.atom[code], IO.rope[explanation], IO.time[] ]; GOTO error; }; DO bytesThisCopy, newPos: INT; didSkip: BOOL _ FALSE; previousAt _ at; [ident, , at]_ WalnutLogExpunge.PeekEntry[]; IF ident = NIL AND at # -1 THEN EXIT; IF at = previousAt THEN { -- probably transAbort [] _ WalnutLogExpunge.SkipEntry[]; out.PutF["\n At pos %g a second time\n", IO.int[at]]; LOOP }; FOR ignore: LIST OF ATOM _ toBeIgnored, ignore.rest UNTIL ignore = NIL DO IF ident = ignore.first THEN { [] _ WalnutLogExpunge.SkipEntry[]; didSkip _ TRUE; EXIT; }; ENDLOOP; IF didSkip THEN LOOP; IF ident = $LogFileInfo THEN { [] _ WalnutLogExpunge.SkipEntry[]; LOOP }; IF ~ValidIdent[ident, at] THEN { [] _ WalnutLogExpunge.SkipEntry[]; LOOP }; [newPos, bytesThisCopy] _ WalnutLogExpunge.CopyEntry[]; IF debugging THEN out.PutF[" - copied to %g", IO.int[newPos]]; IF ident = $CreateMsg THEN { IF (num_ num + 1) MOD 10 = 0 THEN IF num MOD 100 = 0 THEN out.PutF["(%g)", IO.int[num]] ELSE out.PutChar['.]; }; numSinceFlush _ numSinceFlush + bytesThisCopy; IF numSinceFlush >= bytesBetweenFlushes THEN { [] _ WalnutLogExpunge.GetExpungeProgress[]; WalnutRoot.CommitAndContinue[]; numSinceFlush _ 0; }; ENDLOOP; [] _ WalnutLogExpunge.GetExpungeProgress[]; WalnutLogExpunge.EndExpunge[]; WalnutRoot.CommitAndContinue[]; [ , newLen] _ WalnutRoot.SwapLogs[expungeID]; WalnutRoot.UnregisterStatsProc[Report]; WalnutRoot.Shutdown[]; out.PutF["\n The old log was %g bytes, the new log is %g bytes", IO.int[logLength], IO.int[newLen]]; out.PutF["\n The log file contains %g messages", IO.int[num]]; EXITS error => { WalnutLogExpunge.EndExpunge[]; WalnutRoot.UnregisterStatsProc[Report]; WalnutLogExpunge.Shutdown[]; WalnutLog.ShutdownLog[]; WalnutRoot.Shutdown[]; }; END; }; ValidIdent: PROC[ident: ATOM, at: INT] RETURNS[valid: BOOL] = { <> SELECT ident FROM $LogFileInfo => RETURN[TRUE]; $CreateMsg => RETURN[TRUE]; $ExpungeMsgs => RETURN[TRUE]; $WriteExpungeLog => RETURN[TRUE]; $CreateMsgSet => RETURN[TRUE]; $DestroyMsgSet => RETURN[TRUE]; $EmptyMsgSet => RETURN[TRUE]; $HasBeenRead => RETURN[TRUE]; $AddMsg => RETURN[TRUE]; $RemoveMsg => RETURN[TRUE]; $MoveMsg => RETURN[TRUE]; $RecordNewMailInfo => RETURN[TRUE]; $StartCopyNewMail => RETURN[TRUE]; $EndCopyNewMailInfo => RETURN[TRUE]; $AcceptNewMail => RETURN[TRUE]; $StartReadArchiveFile => RETURN[TRUE]; $EndReadArchiveFile => RETURN[TRUE]; $StartCopyReadArchive => RETURN[TRUE]; $EndCopyReadArchiveInfo => RETURN[TRUE]; ENDCASE => NULL; out.PutF["\n~~~~ Invalid Entry with identifier $%g at log pos %g\n", IO.atom[ident], IO.int[at] ]; RETURN[FALSE] }; DoScan: PROC[strm: STREAM, logLength: INT] = { ident: ATOM; length: INT; table: RefTab.Ref _ RefTab.Create[]; found: BOOL; val: RefTab.Val; count: INT _ 0; at : INT _ strm.GetIndex[]; ForPrinting: RefTab.EachPairAction = { ident: ATOM _ NARROW[key]; this: Xyz _ NARROW[val]; out.PutF["Ident: %g, num: %g, bytes: %g\n", IO.atom[ident], IO.int[this.num], IO.int[this.bytes] ]; RETURN[FALSE]; -- don't quit }; DO this: Xyz; [ident, , length] _ WalnutStream.PeekEntry[strm]; IF ident = NIL THEN { curPos: INT _ strm.GetIndex[]; IF curPos = logLength THEN EXIT; out.PutF["\n No entry found at %g\n", IO.int[curPos]]; at _ WalnutStream.FindNextEntry[strm]; IF at = -1 THEN { out.PutRope["No more entries found"]; EXIT }; out.PutF["Next entry found at %g - continuing\n", IO.int[at] ]; LOOP; }; strm.SetIndex[at _ at + length]; [found, val] _ RefTab.Fetch[table, ident]; IF found THEN { this _ NARROW[val]; this.num _ this.num + 1; this.bytes _ this.bytes + length; } ELSE this _ NEW[ValueObject _ [1, length]]; [] _ RefTab.Store[table, ident, this]; IF (count _ count + 1) MOD 10 = 0 THEN { IF count MOD 100 = 0 THEN out.PutF["(%g)", IO.int[count]] ELSE out.PutChar['~]; }; ENDLOOP; out.PutF["\n\n\t\tThe log (%g bytes) contained the following %g entries:\n", IO.int[logLength], IO.int[count]]; [] _ RefTab.Pairs[table, ForPrinting]; }; Report: PROC[msg: ROPE] = { out.PutF["\n %g @ %g\n", IO.rope[msg], IO.time[]] }; CmdScan: Commander.CommandProc = TRUSTED { Process.Detach[FORK SetupScan[cmd.commandLine]] }; CmdFix: Commander.CommandProc = TRUSTED { Process.Detach[FORK SetupFix[cmd.commandLine]] }; Commander.Register["Scan", CmdScan, "Summarizes a Walnut log; syntax is \"Scan logFile\""]; Commander.Register["Fix", CmdFix, "Remove entries from a Walnut log; syntax is \"Fix rootFile {AtomNames of entries to remove}\""]; END.