DIRECTORY AlpineFS USING [StreamOpen], Commander USING [CommandProc, Register], CommandTool USING [CurrentWorkingDirectory], FS USING [ComponentPositions, Error, BytesForPages, ExpandName, PagesForBytes, StreamOpen], FSRope USING [StreamFromRope], IO, Process USING [Detach], RefTab USING [EachPairAction, Ref, Val, Create, Fetch, Store, Pairs], Rope, UserProfile USING [Token], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams], ViewerOps USING [FindViewer, OpenIcon], WalnutDefs USING [Error], WalnutLog USING [LogLength, OpenLogStreams, ReturnCurrentLogStreams, ShutdownLog], WalnutLogExpunge -- using almost everything -- , WalnutRoot USING [CommitAndContinue, Open, RegisterStatsProc, Shutdown, StartTransaction, SwapLogs, UnregisterStatsProc], WalnutStream USING [FindNextEntry, Open, PeekEntry]; LogOps: CEDAR PROGRAM IMPORTS AlpineFS, Commander, CommandTool, FS, FSRope, IO, Process, RefTab, Rope, UserProfile, ViewerIO, ViewerOps, WalnutDefs, WalnutLog, WalnutLogExpunge, WalnutRoot, WalnutStream = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; out, tsLogStrm: STREAM _ NIL; debugging: BOOL _ FALSE; Xyz: TYPE = REF ValueObject; ValueObject: TYPE = RECORD[num, bytes: INT _ 0]; starsRope: ROPE = "\n\n************************************************************\n"; noEntry: ROPE = "\n ***** No entry found at %g, backing up to prevPos: %g\n"; bad: ROPE = "\tBad entry had ident $%g, length %g; next entry was %g bytes later\n"; nextValid: ROPE = "\n\tNext valid entry found at %g - continuing\n\n"; noInputFile: ROPE = "\nNo input file specified & no Walnut.WalnutRootFile entry in profile - quitting\n"; noRootFile: ROPE = "\nNo rootFile file specified & no Walnut.WalnutRootFile entry in profile - quitting\n"; eosR: ROPE = "\n\n **** EndOfStream encountered at pos %g, when trying for pos %g\n"; endS: ROPE = "Bad entry had ident %g, length %g, end was %g bytes later\n"; TSStream: PROC[name, tsLogFile, wDir: ROPE] RETURNS [out, tsLogStrm: STREAM] = { v: ViewerClasses.Viewer _ ViewerOps.FindViewer[name]; out _ ViewerIO.CreateViewerStreams[name, v, NIL, FALSE].out; IF tsLogFile # NIL THEN tsLogStrm _ FS.StreamOpen[ fileName: tsLogFile, accessOptions: $create, keep: 10, wDir: wDir]; IF v#NIL THEN IF v.iconic THEN ViewerOps.OpenIcon[v]; }; SetupScan: PROC[commandLine, wDir: ROPE] = { clStream: STREAM _ FSRope.StreamFromRope[commandLine]; logFile: ROPE; startPos: INT; logFile _ clStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token; startPos _ clStream.GetInt[ ! IO.EndOfStream => {startPos _ 0; CONTINUE }]; Scan[logFile, wDir, startPos]; }; Scan: PROC[logFile, wDir: ROPE, startPos: INT] = { strm: STREAM; logLength: INT; fullLogName: ROPE; scanStart: ROPE = "\t\t Scan Log for Entries, started @ %g\n"; scan: ROPE = "Scanning the logfile: %g, starting at pos: %g:\n\n(0)"; [out, tsLogStrm] _ TSStream["Walnut Rescue", "WalnutRescue.Log", wDir]; out.PutRope[starsRope]; tsLogStrm.PutRope[starsRope]; out.PutF[scanStart, IO.time[] ]; tsLogStrm.PutF[scanStart, IO.time[] ]; IF logFile = NIL THEN { [logFile, strm] _ TryUsingRootFileName[]; IF logFile = NIL THEN { out.PutRope[noInputFile]; tsLogStrm.PutRope[noInputFile]; tsLogStrm.Close[]; RETURN; }; }; fullLogName _ FS.ExpandName[logFile, wDir].fullFName; IF strm = NIL THEN strm _ WalnutStream.Open[name: fullLogName, readOnly: TRUE ! FS.Error => CONTINUE]; IF strm = NIL THEN { noOpen: ROPE = "Could not open %g\n"; out.PutF[noOpen, IO.rope[fullLogName] ]; tsLogStrm.PutF[noOpen, IO.rope[fullLogName] ]; tsLogStrm.Close[]; RETURN }; IF (logLength _ strm.GetLength[]) = 0 THEN { empty: ROPE = "The log file %g is empty\n"; out.PutF[empty, IO.rope[fullLogName] ]; tsLogStrm.PutF[empty, IO.rope[fullLogName] ]; tsLogStrm.Close[]; RETURN }; out.PutF[scan, IO.rope[fullLogName], IO.int[startPos] ]; tsLogStrm.PutF[scan, IO.rope[fullLogName], IO.int[startPos] ]; DoScan[strm, startPos, logLength]; tsLogStrm.Close[]; }; TryUsingRootFileName: PROC RETURNS[logFile: ROPE, strm: STREAM] = { rootFile: ROPE _ UserProfile.Token[key: "Walnut.WalnutRootFile", default: ""]; cp: FS.ComponentPositions; IF rootFile.Length[] = 0 THEN RETURN[NIL, NIL]; cp _ FS.ExpandName[rootFile].cp; logFile _ Rope.Concat[Rope.Substr[rootFile, 0, cp.ext.start], "LogX"]; -- try logX strm _ WalnutStream.Open[name: logFile, readOnly: TRUE ! FS.Error => CONTINUE]; IF strm = NIL THEN RETURN[logFile, NIL]; IF strm.GetLength[] # 0 THEN RETURN; strm.Close[]; -- try the other log strm _ NIL; logFile _ Rope.Concat[Rope.Substr[rootFile, 0, cp.ext.start], "LogY"]; -- try logY }; SetupFix: PROC[commandLine, wDir: 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; FixWalnutLog[rootFile, wDir, FALSE, entriesToIgnore]; }; Fix: PROC[rootFile, wDir: 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; FixWalnutLog[rootFile, wDir, FALSE, toBeIgnored]; }; FixWalnutLog: PUBLIC PROC[ rootFile, wDir: ROPE, remoteOrphans: BOOL _ FALSE, toBeIgnored: LIST OF ATOM _ NIL] = { badBitsStrm: STREAM; serverAndDir: ROPE; BEGIN previousAt, at: INT _ -1; ident: ATOM; logLength, newLen: INT; expungeID: INT; num, numSinceFlush: INT _ 0; bytesBetweenFlushes: INT = FS.BytesForPages[100]; pagesNeeded: INT; msgID: REF TEXT; startFix: ROPE = "\t\tFix Log, started at %g\n"; fixWho: ROPE = "\tFixing the rootfile: %g\n"; DumpBadBits: PROC[startPos, endPos: INT] = { numBad: INT _ endPos - startPos; IF badBitsStrm = NIL THEN { orphan: ROPE _ IF remoteOrphans THEN serverAndDir.Concat["WalnutRescue.orphanBytesLog"] ELSE FS.ExpandName["WalnutRescue.orphanBytesLog", wDir].fullFName; out.PutF["\n*** Writing the orphan bytes on %g\n", IO.rope[orphan] ]; tsLogStrm.PutF["\n*** Writing the orphan bytes on %g%g\n", IO.rope[wDir], IO.rope[orphan] ]; badBitsStrm _ IF remoteOrphans THEN AlpineFS.StreamOpen[name: orphan, accessOptions: $create, keep: 2] ELSE FS.StreamOpen[fileName: orphan, accessOptions: $create, keep: 10]; badBitsStrm.SetIndex[0]; badBitsStrm.PutF["\nOrphaned bits from fixing %g, started at %g\n\n", IO.rope[rootFile], IO.time[]]; badBitsStrm.Flush[]; }; badBitsStrm.PutRope[starsRope]; badBitsStrm.PutF["\n\t%g orphan bytes, starting at pos: %g\n", IO.int[numBad], IO.int[startPos] ]; WalnutLogExpunge.SetIndex[startPos]; WalnutLogExpunge.CopyBytes[badBitsStrm, numBad]; badBitsStrm.Flush[]; }; [out, tsLogStrm] _ TSStream["Walnut Rescue", "WalnutRescue.log", wDir]; out.PutRope[starsRope]; tsLogStrm.PutRope[starsRope]; out.PutF[startFix, IO.time[] ]; tsLogStrm.PutF[startFix, IO.time[] ]; IF rootFile = NIL THEN { rootFile _ UserProfile.Token[key: "Walnut.WalnutRootFile", default: ""]; IF rootFile.Length[] = 0 THEN { out.PutRope[noRootFile]; tsLogStrm.PutRope[noRootFile]; tsLogStrm.Close[]; RETURN; }; }; out.PutF[fixWho, IO.rope[rootFile] ]; tsLogStrm.PutF[fixWho, IO.rope[rootFile] ]; IF remoteOrphans THEN serverAndDir _ ParseRootName[rootFile]; IF toBeIgnored = NIL THEN { r1: ROPE = " No entries except invalid ones will be ignored\n"; out.PutRope[r1]; tsLogStrm.PutRope[r1]; } ELSE { r2: ROPE = "\n Ignoring the following entries: "; r3: ROPE = " %g,"; out.PutRope[r2]; tsLogStrm.PutRope[r2]; FOR ignore: LIST OF ATOM _ toBeIgnored, ignore.rest UNTIL ignore = NIL DO out.PutF[r3, IO.atom[ignore.first]]; tsLogStrm.PutF[r3, IO.atom[ignore.first]]; ENDLOOP; out.PutChar['\n]; tsLogStrm.PutChar['\n]; }; WalnutRoot.RegisterStatsProc[Report]; BEGIN ENABLE { WalnutDefs.Error => { ec: ROPE = "\n *** WalnutDefs Error: code: %g, info: %g @ %g"; qt: ROPE = "\n quitting ...\n"; out.PutF[ec, IO.atom[code], IO.rope[explanation], IO.time[] ]; tsLogStrm.PutF[ec, IO.atom[code], IO.rope[explanation], IO.time[] ]; out.PutRope[qt]; tsLogStrm.PutRope[qt]; GOTO error; }; UNWIND => GOTO error; }; IF WalnutRoot.Open[rootFile].isReadOnly THEN { out.PutF["\n %g is readOnly - can't Fix\n", IO.rope[rootFile]]; tsLogStrm.PutF["\n %g is readOnly - can't Fix\n", IO.rope[rootFile]]; WalnutRoot.UnregisterStatsProc[Report]; WalnutRoot.Shutdown[]; RETURN; }; [] _ WalnutRoot.StartTransaction[]; [] _ WalnutLog.OpenLogStreams[]; pagesNeeded _ FS.PagesForBytes[logLength _ WalnutLog.LogLength[]]; WalnutLog.ReturnCurrentLogStreams[]; expungeID _ WalnutLogExpunge.StartExpunge[pagesNeeded]; [] _ WalnutLogExpunge.SetPosition[0]; [ident, msgID, at] _ WalnutLogExpunge.PeekEntry[]; DO bytesThisCopy, newPos: INT; doSkip: BOOL _ FALSE; nextIdent: ATOM; nextMsgID: REF TEXT; nextAt: INT; thisStatus: WalnutLogExpunge.EntryStatus; thisStatus _ WalnutLogExpunge.ExamineThisEntry[]; -- advances log if valid entry IF thisStatus = EndOfStream THEN { eosR: ROPE = "\n***** EndOfStream encountered at pos %g, last entry was at pos %g\n"; badE: ROPE = "\t\tBad entry had ident %g, end was %g bytes later\n"; out.PutF[eosR, IO.int[logLength], IO.int[at]]; tsLogStrm.PutF[eosR, IO.int[logLength], IO.int[at]]; out.PutF[badE, IO.atom[ident], IO.int[logLength - at] ]; tsLogStrm.PutF[badE, IO.atom[ident], IO.int[logLength - at] ]; DumpBadBits[at, logLength]; EXIT }; [nextIdent, nextMsgID, nextAt] _ WalnutLogExpunge.PeekEntry[]; IF nextIdent = NIL THEN { curPos: INT _ WalnutLogExpunge.GetIndex[]; IF curPos = logLength THEN EXIT ELSE { -- we have a problem out.PutF[noEntry, IO.int[curPos], IO.int[at] ]; tsLogStrm.PutF[noEntry, IO.int[curPos], IO.int[at] ]; nextAt _ at + WalnutLogExpunge.SetPosition[at+1] + 1; IF nextAt = at THEN { noMore: ROPE = "No more entries found\n"; out.PutRope[noMore]; tsLogStrm.PutRope[noMore]; DumpBadBits[at, logLength]; EXIT }; IF nextAt <= curPos THEN { -- previous entry is not ok out.PutF[bad, IO.atom[ident], IO.int[curPos - at], IO.int[nextAt - at] ]; tsLogStrm.PutF[bad, IO.atom[ident], IO.int[curPos - at], IO.int[nextAt - at] ]; IF msgID # NIL THEN { id: ROPE = "\t The msgID was: \"%g\"\n"; out.PutF[id, IO.text[msgID] ]; tsLogStrm.PutF[id, IO.text[msgID] ]; }; out.PutF[nextValid, IO.int[nextAt] ]; tsLogStrm.PutF[nextValid, IO.int[nextAt] ]; DumpBadBits[at, nextAt]; WalnutLogExpunge.SetIndex[nextAt]; [ident, msgID, at] _ WalnutLogExpunge.PeekEntry[]; LOOP; }; }; }; WalnutLogExpunge.SetIndex[at]; IF at = previousAt THEN { -- probably transAbort r4: ROPE = "\n At pos %g a second time\n"; [] _ WalnutLogExpunge.SkipEntry[]; out.PutF[r4, IO.int[at]]; tsLogStrm.PutF[r4, IO.int[at]]; ident _ nextIdent; msgID _ nextMsgID; at _ nextAt; LOOP; }; FOR ignore: LIST OF ATOM _ toBeIgnored, ignore.rest UNTIL ignore = NIL DO IF ident = ignore.first THEN { doSkip _ TRUE; EXIT; }; ENDLOOP; IF doSkip OR ident=$LogFileInfo OR ident=$ExpungeMsgs OR ~ValidIdent[ident, at, TRUE] THEN [] _ WalnutLogExpunge.SkipEntry[] ELSE { [newPos, bytesThisCopy] _ WalnutLogExpunge.CopyEntry[]; IF ident = $CreateMsg THEN { IF (num_ num + 1) MOD 10 = 0 THEN IF num MOD 100 = 0 THEN { out.PutF["(%g)", IO.int[num]]; tsLogStrm.PutF["(%g)", IO.int[num]] } ELSE { out.PutChar['.]; tsLogStrm.PutChar['.] }; }; numSinceFlush _ numSinceFlush + bytesThisCopy; IF numSinceFlush >= bytesBetweenFlushes THEN { [] _ WalnutLogExpunge.GetExpungeProgress[]; WalnutRoot.CommitAndContinue[]; numSinceFlush _ 0; }; }; IF nextIdent = NIL THEN { -- previous entry not followed by a valid entry; go find it curPos: INT = WalnutLogExpunge.GetIndex[]; noEntry: ROPE = "\nNo entry found at pos %g; next valid entry was %g bytes later"; next: INT = curPos + WalnutLogExpunge.SetPosition[curPos]; IF next = -1 THEN { noMore: ROPE = "No more entries found\n"; out.PutRope[noMore]; tsLogStrm.PutRope[noMore]; DumpBadBits[curPos, logLength]; EXIT }; out.PutF[noEntry, IO.int[curPos], IO.int[next - curPos]]; tsLogStrm.PutF[noEntry, IO.int[curPos], IO.int[next - curPos]]; DumpBadBits[curPos, next]; WalnutLogExpunge.SetIndex[next]; [ident, msgID, at] _ WalnutLogExpunge.PeekEntry[]; LOOP; }; ident _ nextIdent; msgID _ nextMsgID; at _ nextAt; ENDLOOP; [] _ WalnutLogExpunge.GetExpungeProgress[]; WalnutLogExpunge.EndExpunge[]; WalnutRoot.CommitAndContinue[]; [ , newLen] _ WalnutRoot.SwapLogs[expungeID]; WalnutRoot.UnregisterStatsProc[Report]; WalnutRoot.Shutdown[]; BEGIN old: ROPE = "\n The old log was %g bytes, the new log is %g bytes"; had: ROPE = "\n The log file contains %g messages\n"; out.PutF[old, IO.int[logLength], IO.int[newLen]]; tsLogStrm.PutF[old, IO.int[logLength], IO.int[newLen]]; out.PutF[had, IO.int[num]]; tsLogStrm.PutF[had, IO.int[num]]; tsLogStrm.Close[]; IF badBitsStrm # NIL THEN badBitsStrm.Close[]; END; EXITS error => { WalnutLogExpunge.EndExpunge[]; WalnutRoot.UnregisterStatsProc[Report]; WalnutLogExpunge.Shutdown[]; WalnutLog.ShutdownLog[]; WalnutRoot.Shutdown[]; IF badBitsStrm # NIL THEN badBitsStrm.Close[]; tsLogStrm.Close[]; }; END; END; }; ValidIdent: PROC[ident: ATOM, at: INT, doReport: BOOL] 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; IF doReport THEN { inv: ROPE = "\n~~~~ Invalid Entry with identifier $%g at log pos %g\n"; out.PutF[inv, IO.atom[ident], IO.int[at] ]; tsLogStrm.PutF[inv, IO.atom[ident], IO.int[at] ]; }; RETURN[FALSE] }; DoScan: PROC[strm: STREAM, startPos, logLength: INT] = { ident: ATOM; msgID: REF TEXT; length, previousAt: INT; validTable: RefTab.Ref _ RefTab.Create[]; invalidTable: RefTab.Ref _ RefTab.Create[]; table: RefTab.Ref; found: BOOL; val: RefTab.Val; validCount, invalidCount, count: INT _ 0; at: INT _ startPos; ForPrinting: RefTab.EachPairAction = { ident: ATOM _ NARROW[key]; this: Xyz _ NARROW[val]; pr: ROPE = "Ident: %g, num: %g, bytes: %g\n"; out.PutF[pr, IO.atom[ident], IO.int[this.num], IO.int[this.bytes] ]; tsLogStrm.PutF[pr, IO.atom[ident], IO.int[this.num], IO.int[this.bytes] ]; RETURN[FALSE]; -- don't quit }; strm.SetIndex[startPos]; IF startPos # 0 THEN at _ WalnutStream.FindNextEntry[strm]; previousAt _ at; DO this: Xyz; previousIdent: ATOM _ ident; previousMsgID: REF TEXT _ msgID; [ident, msgID, length] _ WalnutStream.PeekEntry[strm]; IF ident = NIL THEN { curPos: INT _ strm.GetIndex[]; IF curPos = logLength THEN EXIT; out.PutF[noEntry, IO.int[curPos], IO.int[previousAt] ]; tsLogStrm.PutF[noEntry, IO.int[curPos], IO.int[previousAt] ]; strm.SetIndex[previousAt+1]; at _ WalnutStream.FindNextEntry[strm]; IF at = -1 THEN { noMore: ROPE = "No more entries found\n"; out.PutRope[noMore]; tsLogStrm.PutRope[noMore]; EXIT }; out.PutF[bad, IO.atom[previousIdent], IO.int[curPos - previousAt], IO.int[at - previousAt] ]; tsLogStrm.PutF[bad, IO.atom[previousIdent], IO.int[curPos - previousAt], IO.int[at - previousAt] ]; IF previousMsgID # NIL THEN { id: ROPE = "\t The msgID was: \"%g\"\n"; out.PutF[id, IO.text[previousMsgID] ]; tsLogStrm.PutF[id, IO.text[previousMsgID] ]; }; out.PutF[nextValid, IO.int[at] ]; tsLogStrm.PutF[nextValid, IO.int[at] ]; LOOP; }; previousAt _ at; strm.SetIndex[at _ at + length ! IO.EndOfStream => { curPos: INT = strm.GetLength[]; lastLen: INT = curPos - previousAt; eosR: ROPE = "\n\n **** EndOfStream encountered at pos %g, when trying for pos %g\n"; endS: ROPE = "Bad entry had ident %g, length %g, end was %g bytes later\n"; out.PutF[eosR, IO.int[curPos], IO.int[at]]; tsLogStrm.PutF[eosR, IO.int[curPos], IO.int[at]]; out.PutF[endS, IO.atom[ident], IO.int[length], IO.int[lastLen] ]; tsLogStrm.PutF[endS, IO.atom[ident], IO.int[length], IO.int[lastLen] ]; IF msgID # NIL THEN { id: ROPE = "\t The msgID was: \"%g\"\n"; out.PutF[id, IO.text[msgID] ]; tsLogStrm.PutF[id, IO.text[msgID] ]; }; strm.SetIndex[curPos]; CONTINUE; } ]; IF ValidIdent[ident, 0, FALSE] THEN { table _ validTable; validCount _ validCount + 1; } ELSE { table _ invalidTable; invalidCount _ invalidCount + 1; }; [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]]; tsLogStrm.PutF["(%g)", IO.int[count]] } ELSE { out.PutChar['~]; tsLogStrm.PutChar['~] }; }; ENDLOOP; IF count = 0 THEN { cnt: ROPE = "\n\t\tThe log (%g bytes) contained no entries\n"; out.PutF[cnt, IO.int[logLength] ]; tsLogStrm.PutF[cnt, IO.int[logLength] ]; RETURN }; BEGIN msg: ROPE = "\n\n\tThe log (%g bytes) contained the following entries:\n"; val: ROPE = "\n\tThe table of valid entries (%g) is:\n"; inVal: ROPE = "\n\tThe table of invalid entries (%g) is:\n"; out.PutF[msg, IO.int[logLength]]; tsLogStrm.PutF[msg, IO.int[logLength]]; IF validCount # 0 THEN { out.PutF[val, IO.int[validCount]]; tsLogStrm.PutF[val, IO.int[validCount]]; [] _ RefTab.Pairs[validTable, ForPrinting]; }; IF invalidCount # 0 THEN { out.PutF[inVal, IO.int[invalidCount]]; tsLogStrm.PutF[inVal, IO.int[invalidCount]]; [] _ RefTab.Pairs[invalidTable, ForPrinting]; }; END; }; Report: PROC[msg: ROPE] = { out.PutF["\n %g @ %g\n", IO.rope[msg], IO.time[]]; tsLogStrm.PutF["\n %g @ %g\n", IO.rope[msg], IO.time[]]; }; CmdScan: Commander.CommandProc = { wDir: ROPE = CommandTool.CurrentWorkingDirectory[]; TRUSTED { Process.Detach[FORK SetupScan[cmd.commandLine, wDir] ] }; }; CmdFix: Commander.CommandProc = { wDir: ROPE = CommandTool.CurrentWorkingDirectory[]; TRUSTED { Process.Detach[FORK SetupFix[cmd.commandLine, wDir] ] }; }; ParseRootName: PROC[rootFile: ROPE] RETURNS[serverAndDir: ROPE] = { cp: FS.ComponentPositions; full: ROPE; [full, cp, ] _ FS.ExpandName[rootFile]; RETURN[full.Substr[0, cp.base.start]]; }; Commander.Register["ScanWalnutLog", CmdScan, "Summarizes a Walnut log; syntax is \"ScanWalnutLog logFile\""]; Commander.Register["FixWalnutLog", CmdFix, "Remove entries from a Walnut log; syntax is \"FixWalnutLog rootFile {AtomNames of entries to remove}\""]; END. (LogOps.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Willie-Sue, October 30, 1985 2:17:13 pm PST Woosh, August 14, 1985 4:01:15 pm PDT see if this is a well-formed entry back up to the good entry this changes whenever WalnutKernelDefs.LogEntry changes Κ™˜šœ ™ Icodešœ Οmœ1™Jšœžœ;˜EJ˜JšœG˜GJšœ5˜5Jšœžœ&žœ ˜Hšžœ žœžœ˜Jšœ)˜)šžœ žœžœ˜Jšœ:˜:Jšœ˜Jšžœ˜J˜—J˜—Jšœžœ%˜5J˜šžœžœž˜Jšœ6žœžœ žœ˜S—šžœžœžœ˜Jšœžœ˜%Jšœžœ.žœ˜XJšœ˜Jšž˜J˜—šžœ$žœ˜,Jšœžœ ˜+Jšœžœ-žœ˜VJšœ˜Jšž˜J˜—Jšœžœžœ˜8Jšœžœžœ˜>Jšœ"˜"Jšœ˜J˜—J˜š  œžœžœ žœžœ˜CJšœ žœ@˜NJšœžœ˜Jš žœžœžœžœžœ˜/Jšœžœ˜ JšœHŸ ˜SJšœ2žœžœ žœ˜OJš žœžœžœžœ žœ˜(Jšžœžœžœ˜$JšœŸ˜#Jšœžœ˜ JšœHŸ ˜SJ˜—J˜š œžœžœ˜+Jšœ žœ&˜6Jšœ žœ˜Jšœžœžœžœ˜,Jšœžœ˜ Jšœ!žœ žœžœ˜Ošž˜Jšœžœžœžœ˜GJšžœžœžœžœ˜šžœžœžœ"žœ˜IJšžœžœ,˜K—Jšžœ˜—Jšœžœ˜5J˜—J˜š œžœžœžœžœžœžœ˜EJšœ žœžœžœ˜šžœžœžœžœžœžœžœž˜DJšœžœžœ ˜Jšœžœ˜$Jšžœ˜—Jšœžœ˜1Jšœ˜—J˜š  œž œ˜Jšœžœžœžœžœžœžœžœ˜XJšœ žœ˜Jšœžœ˜Jšž˜Jšœžœ˜Jšœžœ˜ Jšœžœ˜Jšœ žœ˜Jšœžœ˜Jšœžœžœ˜1Jšœ žœ˜Jšœžœžœ˜Jšœ žœ"˜0Jšœžœ!˜-š  œžœžœ˜,Jšœžœ˜ šžœžœžœ˜šœž œž˜$Jšœ2˜2Jšžœžœ;˜B—J˜Jšœ3žœ˜Ešœ:˜:Jšœžœ žœ˜#—šœ ˜ šžœž˜JšœB˜BJšžœžœ@˜G——Jšœ˜šœE˜EJšžœžœ ˜—Jšœ˜J˜—Jšœ˜šœ>˜>Jšœžœ!˜%—Jšœ$˜$Jšœ0˜0Jšœ˜J˜—J˜JšœG˜GJšœ5˜5Jšœžœ$žœ ˜EJ˜šžœ žœžœ˜JšœH˜Hšžœžœ˜Jšœ7˜7Jšœ˜Jšžœ˜J˜—J˜—Jšœžœ*žœ˜QJšžœžœ(˜=J˜šžœžœžœ˜Jšœžœ7˜?Jšœ(˜(J˜šžœ˜Jšœžœ)˜1Jšœžœ ˜Jšœ'˜'š žœ žœžœžœžœ žœž˜IJšœ žœ*žœ˜PJšžœ˜—Jšœ)˜)J˜——J˜Jšœ%˜%šžœžœ˜šœ˜Jšœžœ7˜?Jšœžœ˜Jšœ žœ žœžœ ˜>Jšœžœ žœžœ ˜DJšœ(˜(Jšžœ˜ J˜—Jšžœžœ˜J˜—J˜šžœ&žœ˜.Jšœ,žœ˜?Jšœ2žœ˜EJšœ'˜'Jšœ˜Jšžœ˜J˜—J˜J˜#J˜ Jšœžœ2˜BJ˜$Jšœ7˜7J˜J˜%J˜Jšœ2˜2J˜šž˜Jšœžœ˜Jšœžœžœ˜Jšœ žœ˜Jšœ žœžœ˜Jšœžœ˜ Jšœ)˜)J˜Jšœ"™"Jšœ3Ÿ˜Qšžœžœ˜"JšœžœK˜UJšœžœ:˜DJšœžœžœ ˜.Jšœžœžœ ˜4Jšœžœžœ˜8Jšœžœžœ˜>Jšœ˜Jšž˜J˜—J˜Jšœ?˜?šžœ žœžœ˜Jšœžœ˜*šžœžœž˜šžœŸ˜Jšœžœžœ ˜/Jšœžœžœ ˜5Jšœ5˜5šžœ žœ˜Jšœžœ˜)Jšœ/˜/Jšœ˜Jšž˜Jšœ˜—šžœžœŸ˜7˜ Jšžœžœžœ˜;—šœ˜Jšžœžœžœ˜;—šžœ žœžœ˜Jšœžœ ˜(Jšœ žœ$žœ˜DJ˜—Jšœžœ*žœ˜QJšœ˜Jšœ"˜"Jšœ2˜2Jšžœ˜J˜—J˜——J˜—J™Jšœ™Jšœ˜šžœžœŸ˜1Jšœžœ"˜*Jšœ"˜"Jšœ žœžœ ˜9Jšœ˜Jšœ˜J˜ Jšžœ˜J˜—J˜š žœ žœžœžœžœ žœž˜Išžœž˜Jšœ žœžœ˜—Jšžœ˜—J˜š žœžœžœžœžœž˜ZJšœ!˜!šžœ˜Jšœ7˜7J˜šžœžœ˜šžœžœž˜!šžœžœ ž˜Jšœžœ#žœ ˜FJšžœ,˜0—J˜——J˜Jšœ.˜.šžœ&žœ˜.J˜+J˜Jšœ˜J˜—J˜——šžœ žœžœŸ;˜UJšœžœ˜*Jšœ žœE˜RJšœžœ1˜:šžœ žœ˜Jšœžœ˜)Jšœ/˜/Jšœ˜Jšž˜Jšœ˜—Jšœžœžœ˜9Jšœžœžœ˜?Jšœ˜J˜ J˜2Jšžœ˜J˜J˜—J˜J˜J˜ J˜Jšžœ˜—J˜Jšœ+˜+J˜J˜J˜Jšœ-˜-Jšœ'˜'J˜J˜šž˜Jšœžœ:˜CJšœžœ,˜5Jšœžœžœ˜1Jšœžœžœ˜7Jšœžœ!žœ ˜>Jšœ˜Jšžœžœžœ˜.Jšžœ˜J˜šž˜˜ J˜Jšœ'˜'Jšœ˜Jšœ˜J˜Jšžœžœžœ˜.Jšœ˜J˜——Jšžœ˜Jšžœ˜—J˜—J˜š  œžœžœžœ žœžœžœ˜OJšœ7™7šžœž˜Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜!Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœžœžœ˜#Jšœžœžœ˜"Jšœžœžœ˜$Jšœžœžœ˜Jšœžœžœ˜&Jšœžœžœ˜$Jšœžœžœ˜&Jšœžœžœ˜(Jšžœžœ˜—šžœ žœ˜Jšœžœ>˜GJšœ žœžœ ˜+Jšœžœžœ ˜1J˜—Jšžœžœ˜ J˜—J˜š œžœžœžœ˜8Jšœžœ˜ Jšœžœžœ˜Jšœžœ˜J˜)J˜+J˜Jšœžœ˜ J˜Jšœ!žœ˜)Jšœžœ ˜J˜š  œ˜&Jšœžœžœ˜Jšœ žœ˜Jšœžœ%˜-Jšœ žœžœžœ˜DJšœžœžœžœ˜JJšžœžœŸ ˜J˜—Jšœ˜Jšžœžœ'˜;Jšœ˜J˜šž˜J˜ Jšœžœ ˜Jšœžœžœ ˜ Jšœ6˜6šžœ žœžœ˜Jšœžœ˜Jšžœžœžœ˜ Jšœžœžœ˜7Jšœžœžœ˜=J˜Jšœ&˜&šžœ žœ˜Jšœžœ˜)Jšœ/˜/Jšž˜Jšœ˜—˜ Jšžœžœžœ˜O—šœ˜Jšžœžœžœ˜O—šžœžœžœ˜Jšœžœ ˜(Jšœ žœ,žœ˜TJ˜—Jšœžœ&žœ ˜IJšžœ˜J˜—J˜šœ!žœ˜4Jšœžœ˜Jšœ žœ˜#šœžœ˜ JšœH˜H—JšœžœA˜KJšœžœžœ ˜+Jšœžœžœ ˜1Jšœžœžœžœ˜AJšœžœžœžœ˜Gšžœ žœžœ˜Jšœžœ ˜(Jšœ žœ$žœ˜DJ˜—Jšœ˜Jšžœ˜ J˜—šžœžœžœ˜%Jšœ˜Jšœ˜Jšœ˜šžœ˜Jšœ˜Jšœ ˜ J˜——Jšœ*˜*šžœžœ˜Jšœžœ˜J˜Jšœ!˜!J˜—Jšžœžœ˜+J˜Jšœ&˜&šžœžœžœ˜(šžœžœ žœ˜Jšœžœ&žœ˜KJšžœ-˜1—J˜—Jšžœ˜—J˜šžœ žœ˜Jšœžœ5˜>Jšœžœ(žœ˜LJšž˜J˜—šž˜JšœžœA˜JJšœžœ/˜8Jšœžœ1˜