WalnutCompactImpl.mesa
Copyright Ó 1990 by Xerox Corporation. All rights reserved.
Swinehart, November 21, 1990 5:07 pm PST
Notes:
Writes a new Walnut log file as Walnut.LogC, from an open, allegedly-consistent WallTapestry database. The new log contains only message set creation entries for current message sets, and bodies of messages that are still in the database and not deleted. The user is expected to follow a procedure that has yet to be designed to reassign logs and scavenge a new index set into existence.
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: BOOLTRUE;
WalnutCompactCommand: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANYNIL, msg: ROPENIL]
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 ANYNIL, msg: ROPENIL]~ {
logStream: IO.STREAMFS.StreamOpen[newLogFileName, $create];
handle: WalnutOps.WalnutOpsHandle ← WalnutOps.GetHandleForRootfile[rootFileName];
numRead, numWritten: INT𡤀
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: BOOLTRUE] ~ {
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: BOOLFALSE; -- 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."];
}.