<> <> <> <> <> DIRECTORY Commander USING [CommandProc, Handle, Register], CommandTool USING [NextArgument], FS USING [StreamOpen], IO, RuntimeError USING [ UNCAUGHT ], Rope USING [Concat, Equal, Fetch, Length, ROPE, Substr], ViewerTools USING [ TiogaContents ], WalnutKernelDefs USING [ LogEntry, LogEntryObject ], WalnutOps USING [EnumerateMsgSets, EnumeratorForMsgs, EnumerateMsgs, GetHandleForRootfile, GetMsg, GetHasBeenRead, MsgSet, NextMsg, WalnutOpsHandle ], WalnutStream USING [ WriteEntry, WriteMsgBody ] ; WalnutCompactImpl: CEDAR PROGRAM IMPORTS Commander, FS, IO, Rope, RuntimeError, WalnutOps, CommandTool, WalnutStream ~ { OPEN IO; ROPE: TYPE = Rope.ROPE; Handle: TYPE ~ WalnutOps.WalnutOpsHandle; LogEntry: TYPE ~ WalnutKernelDefs.LogEntry; LogEntryObject: TYPE ~ WalnutKernelDefs.LogEntryObject; rootFileName: ROPE; newLogFileName: ROPE; doReportID: BOOL _ TRUE; WalnutCompactCommand: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> ENABLE RuntimeError.UNCAUGHT => GOTO Failed; rootFileName _ CommandTool.NextArgument[cmd]; newLogFileName _ CommandTool.NextArgument[cmd]; [result, msg] _ DoCompact[cmd]; EXITS Failed => RETURN[$Failed, "*** Uncaught signal ***"] }; DoCompact: PROC[cmd: Commander.Handle] RETURNS[result: REF ANY _ NIL, msg: ROPE _ NIL]~ { logStream: IO.STREAM _ FS.StreamOpen[newLogFileName, $create]; handle: WalnutOps.WalnutOpsHandle _ WalnutOps.GetHandleForRootfile[rootFileName]; numRead, numWritten: INT_0; IF handle=NIL THEN RETURN[$Failed, "No such Walnut database"]; []_WalnutStream.WriteEntry[logStream, NEW[LogFileInfo LogEntryObject _ [LogFileInfo[key: "Swinehart's mail database", internalFileID: 100001, logSeqNo: 1]]]]; { msgSetEntry: REF CreateMsgSet LogEntryObject _ NEW[CreateMsgSet LogEntryObject _ [CreateMsgSet[NIL]]]; WriteMsgSets: PROC[msgSet: WalnutOps.MsgSet] RETURNS [continue: BOOL_TRUE] ~ { msgSetEntry.msgSet _ msgSet.name; []_WalnutStream.WriteEntry[logStream, msgSetEntry]; }; [] _ WalnutOps.EnumerateMsgSets[handle, TRUE, WriteMsgSets]; }; { createEntry: REF CreateMsg LogEntryObject _ NEW[CreateMsg LogEntryObject _ [CreateMsg[show: FALSE]]]; addEntry: REF AddMsg LogEntryObject _ NEW[AddMsg LogEntryObject _ [AddMsg[]]]; moveEntry: REF MoveMsg LogEntryObject _ NEW[MoveMsg LogEntryObject _ [MoveMsg[]]]; beenReadEntry: REF HasBeenRead LogEntryObject _ NEW[HasBeenRead LogEntryObject _ [HasBeenRead[]]]; enum: WalnutOps.EnumeratorForMsgs _ WalnutOps.EnumerateMsgs[handle]; DO msgID: ROPE; msList: LIST OF ROPE; headers: REF TEXT; contents: ViewerTools.TiogaContents; last: INT; char0: CHAR; isActive: BOOL _ FALSE; -- Will be TRUE if message is in Active message set [msgID, msList, headers] _ WalnutOps.NextMsg[enum]; IF msgID=NIL THEN EXIT; numRead _ numRead+1; IF msList#NIL AND msList.rest=NIL AND msList.first.Equal["deleted", FALSE] THEN LOOP; contents _ WalnutOps.GetMsg[handle, msgID].contents; createEntry.msg _ msgID; last _ contents.contents.Length[] - 1; char0 _ IF last>=0 THEN contents.contents.Fetch[0] ELSE '~; IF ( contents.formatting.Length[] # 0 ) AND ( char0 = '\r OR char0 = '\l ) THEN { IF contents.contents.Fetch[last] = '\000 THEN -- NUL for padding { contents.contents _ Rope.Substr[contents.contents, 1, last-1]; contents.formatting _ Rope.Concat["\000", contents.formatting] } ELSE IF char0 = '\r OR char0 = '\l THEN contents.contents _ Rope.Substr[contents.contents, 1]; }; createEntry.textLen _ contents.contents.Length[]; createEntry.formatLen _ contents.formatting.Length[]; []_WalnutStream.WriteEntry[logStream, createEntry]; []_WalnutStream.WriteMsgBody[logStream, contents]; IF WalnutOps.GetHasBeenRead[handle, msgID] THEN { beenReadEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, beenReadEntry]; }; FOR mL: LIST OF ROPE _ msList, mL.rest WHILE mL#NIL DO SELECT TRUE FROM mL.first.Equal["active", FALSE] => { isActive _ TRUE; LOOP; }; mL.first.Equal["deleted", FALSE] => LOOP; -- should not happen, but harmless mL.rest=NIL AND ~isActive => { moveEntry.from _ "Active"; moveEntry.to _ mL.first; moveEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, moveEntry]; }; ENDCASE => { addEntry.to _ mL.first; addEntry.msg _ msgID; []_WalnutStream.WriteEntry[logStream, addEntry]; }; ENDLOOP; numWritten _ numWritten+1; IF doReportID THEN cmd.out.PutF["%g\n", rope[msgID]] ELSE IF numRead MOD 10=0 THEN cmd.out.PutRope["! "] ELSE cmd.out.PutRope["."]; ENDLOOP; }; <<Flush the stream oncet in a while.>> logStream.Close[]; cmd.err.PutF["%g messages read, %g written.\n", int[numRead], int[numWritten]]; RETURN[,"Compact log created on /walnut/Walnut.LogC"]; }; Commander.Register["WalnutCompact", WalnutCompactCommand, "Create most compact Walnut log possible from current, consistent database."]; }.